Add fake external adapter packs

This commit is contained in:
2026-05-18 22:40:53 +02:00
parent e92c8f8c89
commit d1b46ad2f3
8 changed files with 472 additions and 13 deletions

View File

@@ -83,6 +83,7 @@ and a package compilation request for the `ContextPackageCompiler` boundary.
- `phase_memory.activation`: Markitect-compatible activation selection planning.
- `phase_memory.ports`: runtime port protocols.
- `phase_memory.adapters`: deterministic in-memory test adapters.
- `phase_memory.external_adapters`: deterministic fake external adapter packs.
- `phase_memory.runtime`: local runtime facade and stable operation envelopes.
- `phase_memory.cli`: dependency-light command-line interface.
@@ -93,5 +94,6 @@ policy and review gates, [docs/markitect-interop.md](docs/markitect-interop.md)
for package bridge boundaries, [docs/activation-quality.md](docs/activation-quality.md)
for retrieval and evaluation behavior, [docs/service-readiness.md](docs/service-readiness.md)
for service and adapter contracts, [docs/lifecycle-rules.md](docs/lifecycle-rules.md)
for profile-driven lifecycle rules, and [SCOPE.md](SCOPE.md) for repository
for profile-driven lifecycle rules, [docs/external-adapter-packs.md](docs/external-adapter-packs.md)
for fake external integration packs, and [SCOPE.md](SCOPE.md) for repository
boundaries.

View File

@@ -0,0 +1,57 @@
# External Adapter Packs
`phase-memory` can resolve runtime configuration values that declare adapter
mode `external`. Missing external adapters still fail resolution, but supplied
adapter packs can satisfy the public runtime ports.
## Fake External Pack
`phase_memory.external_adapters.fake_external_adapter_pack()` returns a local
fake pack for conformance and integration tests. It provides:
- `FakeExternalGraphStore`
- `FakeExternalEventLog`
- `FakeExternalPolicyGateway`
- `FakeTelemetryAuditSink`
- `FakeMarkitectPackageCompiler`
- `FakeExternalSemanticIndex`
- `FakeKontextualRuntimeRegistry`
The pack is not a production adapter. It is intentionally deterministic and
dependency-light so that service wiring, conformance helpers, and runtime
envelopes can be tested before live Markitect, Kontextual, or telemetry
services exist.
## Runtime Wiring
```python
from phase_memory.external_adapters import (
fake_external_adapter_pack,
fake_external_runtime_config,
)
from phase_memory.service import runtime_from_config
config = fake_external_runtime_config()
pack = fake_external_adapter_pack()
runtime = runtime_from_config(config, external_adapters=pack.adapters)
```
`resolve_runtime_adapters(config, external_adapters=pack.adapters)` returns a
`RuntimeAdapterBundle` with diagnostics. External declarations produce
`external_adapter_declared` warnings. Missing adapters produce
`missing_external_adapter` errors and block runtime construction.
## Conformance
The fake pack passes the same public helpers expected of live adapters:
- `assert_graph_store_conformance`
- `assert_event_log_conformance`
- `assert_context_compiler_conformance`
- `assert_policy_gateway_conformance`
- `assert_audit_sink_conformance`
- `assert_semantic_index_conformance`
- `assert_runtime_registry_conformance`
Live adapter packs should pass those helpers before being wired into a runtime
or service runner.

View File

@@ -59,6 +59,18 @@ External adapter modes are valid configuration values, but they must be
supplied explicitly by the caller. The local resolver reports
`missing_external_adapter` instead of silently replacing them.
## External Adapter Packs
`phase_memory.external_adapters.fake_external_adapter_pack()` supplies a
deterministic fake external pack for conformance and integration tests. It
includes fake Markitect package compilation, Kontextual-shaped graph/event and
runtime registry adapters, a fake semantic index, a fake policy gateway, and a
fake telemetry audit sink.
Use it with `fake_external_runtime_config()` and
`resolve_runtime_adapters(config, external_adapters=pack.adapters)` to exercise
the external wiring path without live services.
## Health
`health_report` emits:

View File

@@ -10,6 +10,18 @@ from .bridge import (
package_response_envelope,
)
from .contracts import graph_from_markitect, profile_from_markitect
from .external_adapters import (
ExternalAdapterPack,
FakeExternalEventLog,
FakeExternalGraphStore,
FakeExternalPolicyGateway,
FakeExternalSemanticIndex,
FakeKontextualRuntimeRegistry,
FakeMarkitectPackageCompiler,
FakeTelemetryAuditSink,
fake_external_adapter_pack,
fake_external_runtime_config,
)
from .lifecycle import (
LifecycleRuleConfig,
PhaseTransitionRule,
@@ -58,6 +70,14 @@ from .runtime import PhaseMemoryRuntime
__all__ = [
"ActivationPlan",
"Diagnostic",
"ExternalAdapterPack",
"FakeExternalEventLog",
"FakeExternalGraphStore",
"FakeExternalPolicyGateway",
"FakeExternalSemanticIndex",
"FakeKontextualRuntimeRegistry",
"FakeMarkitectPackageCompiler",
"FakeTelemetryAuditSink",
"LifecycleAction",
"LifecycleActionKind",
"LifecycleState",
@@ -101,6 +121,8 @@ __all__ = [
"plan_retention",
"plan_retention_from_rules",
"profile_from_markitect",
"fake_external_adapter_pack",
"fake_external_runtime_config",
"path_event",
"package_request_from_selection",
"package_response_envelope",

View File

@@ -0,0 +1,160 @@
"""Fake external adapter pack for service-readiness integration tests."""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any
from .adapters import InMemoryMemoryEventLog, InMemoryMemoryGraphStore, InMemorySemanticIndex
from .models import PolicyDecision
from .service import RuntimeConfig
from .utils import stable_digest
@dataclass(frozen=True)
class ExternalAdapterPack:
name: str
adapters: dict[str, Any]
capabilities: tuple[str, ...] = ()
metadata: dict[str, Any] = field(default_factory=dict)
def to_dict(self) -> dict[str, Any]:
return {
"name": self.name,
"adapters": {key: value.__class__.__name__ for key, value in sorted(self.adapters.items())},
"capabilities": list(self.capabilities),
"metadata": dict(self.metadata),
}
class FakeExternalGraphStore(InMemoryMemoryGraphStore):
"""Kontextual-shaped graph store fake backed by deterministic memory."""
class FakeExternalEventLog(InMemoryMemoryEventLog):
"""Kontextual-shaped event log fake backed by deterministic memory."""
class FakeExternalSemanticIndex(InMemorySemanticIndex):
"""Permission-aware retrieval fake with the local deterministic index shape."""
class FakeMarkitectPackageCompiler:
def compile_selection(self, selection: dict[str, Any]) -> dict[str, Any]:
selection_id = str(selection.get("id") or "selection")
package_id = f"fake-markitect:{stable_digest(selection)}"
return {
"package_id": package_id,
"package_ref": package_id,
"compiler": self.__class__.__name__,
"selection_id": selection_id,
"item_count": len(selection.get("nodes", ())) + len(selection.get("events", ())),
"diagnostics": [],
"selection": dict(selection),
}
class FakeExternalPolicyGateway:
def __init__(self, *, deny_actions: tuple[str, ...] = ()) -> None:
self.deny_actions = set(deny_actions)
def authorize(self, *, action: str, resource: str, context: dict[str, Any] | None = None) -> PolicyDecision:
allowed = action not in self.deny_actions
return PolicyDecision(
allowed,
reason="fake external policy allowed" if allowed else "fake external policy denied",
metadata={
"adapter": self.__class__.__name__,
"action": action,
"resource": resource,
"context": context or {},
},
)
class FakeTelemetryAuditSink:
def __init__(self, *, retention_days: int = 30) -> None:
self.retention_days = retention_days
self.events: list[dict[str, Any]] = []
def record(self, event: dict[str, Any]) -> dict[str, Any]:
external_ref = f"fake-audit:{stable_digest([len(self.events), event])}"
stored = {
**dict(event),
"external_ref": external_ref,
"retention_days": self.retention_days,
}
self.events.append(stored)
return {
"recorded": True,
"external_ref": external_ref,
"retention_days": self.retention_days,
"event": stored,
}
def query(self, *, operation: str | None = None) -> list[dict[str, Any]]:
if operation is None:
return list(self.events)
return [event for event in self.events if event.get("operation") == operation]
class FakeKontextualRuntimeRegistry:
def __init__(self) -> None:
self._envelopes: dict[str, dict[str, Any]] = {}
def publish_runtime_envelope(self, envelope: dict[str, Any]) -> dict[str, Any]:
reference = f"fake-kontextual:{stable_digest(envelope)}"
stored = dict(envelope)
self._envelopes[reference] = stored
return {
"published": True,
"reference": reference,
"adapter": self.__class__.__name__,
"envelope": stored,
}
def fetch_runtime_envelope(self, reference: str) -> dict[str, Any]:
return dict(self._envelopes[reference])
def fake_external_adapter_pack() -> ExternalAdapterPack:
return ExternalAdapterPack(
name="fake-external",
adapters={
"graph_store": FakeExternalGraphStore(),
"event_log": FakeExternalEventLog(),
"policy_gateway": FakeExternalPolicyGateway(),
"audit_sink": FakeTelemetryAuditSink(),
"package_compiler": FakeMarkitectPackageCompiler(),
"semantic_index": FakeExternalSemanticIndex(),
"runtime_registry": FakeKontextualRuntimeRegistry(),
},
capabilities=(
"markitect.package.compile",
"kontextual.runtime.registry",
"kontextual.graph-store.fake",
"telemetry.audit.fake",
"semantic-index.fake",
),
metadata={"intended_for": "local conformance and integration tests"},
)
def fake_external_runtime_config() -> RuntimeConfig:
return RuntimeConfig(
adapter_registry={
"graph_store": "external",
"event_log": "external",
"policy_gateway": "external",
"audit_sink": "external",
"package_compiler": "external",
"semantic_index": "external",
"runtime_registry": "external",
},
policy_mode="external",
audit_sink_mode="external",
package_compiler_mode="external",
semantic_index_mode="external",
runtime_registry_mode="external",
trust_zone_labels=("external",),
)

View File

@@ -0,0 +1,66 @@
import json
from pathlib import Path
from phase_memory.external_adapters import fake_external_adapter_pack, fake_external_runtime_config
from phase_memory.service import (
assert_audit_sink_conformance,
assert_context_compiler_conformance,
assert_event_log_conformance,
assert_graph_store_conformance,
assert_policy_gateway_conformance,
assert_runtime_registry_conformance,
assert_semantic_index_conformance,
health_report,
resolve_runtime_adapters,
runtime_from_config,
)
FIXTURES = Path(__file__).parent / "fixtures"
def _load(name: str):
return json.loads((FIXTURES / name).read_text(encoding="utf-8"))
def test_fake_external_adapter_pack_satisfies_public_conformance_helpers() -> None:
pack = fake_external_adapter_pack()
adapters = pack.adapters
assert_graph_store_conformance(adapters["graph_store"])
assert_event_log_conformance(adapters["event_log"])
assert_context_compiler_conformance(adapters["package_compiler"])
assert_policy_gateway_conformance(adapters["policy_gateway"])
assert_audit_sink_conformance(adapters["audit_sink"])
assert_semantic_index_conformance(adapters["semantic_index"])
assert_runtime_registry_conformance(adapters["runtime_registry"])
assert pack.to_dict()["adapters"]["package_compiler"] == "FakeMarkitectPackageCompiler"
def test_external_runtime_config_resolves_supplied_fake_pack() -> None:
config = fake_external_runtime_config()
pack = fake_external_adapter_pack()
bundle = resolve_runtime_adapters(config, external_adapters=pack.adapters)
runtime = runtime_from_config(config, external_adapters=pack.adapters)
assert not [diagnostic for diagnostic in bundle.diagnostics if diagnostic.severity == "error"]
assert {diagnostic.code for diagnostic in bundle.diagnostics} == {"external_adapter_declared"}
runtime.import_graph(_load("memory-graph.json"), source_ref="fake-external")
envelope = runtime.compile_package(
{
"schema_version": "markitect.memory.selection.v1",
"id": "selection.external",
"nodes": ["decision.boundary"],
"events": [],
}
)
registry_receipt = bundle.runtime_registry.publish_runtime_envelope(envelope)
fetched = bundle.runtime_registry.fetch_runtime_envelope(registry_receipt["reference"])
report = health_report(runtime, config=config)
assert envelope["valid"] is True
assert envelope["data"]["package_response"]["package_ref"].startswith("fake-markitect:")
assert fetched["operation"] == "package.compile"
assert report["ok"] is True
assert report["adapters"]["package_compiler"] == "FakeMarkitectPackageCompiler"

View File

@@ -44,33 +44,33 @@ not what adjacent repositories may already provide.
## Current Baseline - 2026-05-18
Overall maturity: **4.4 / 5**
Overall maturity: **4.5 / 5**
The repo has crossed from intent-only into a working deterministic library
foundation, a usable local runtime facade, a CLI, a file-backed local
workspace, first-slice policy/review/audit gates, a concrete Markitect package
bridge, deterministic activation quality helpers, and first-slice service
readiness contracts with profile-driven runtime adapter resolution and
profile-derived lifecycle rules.
readiness contracts with profile-driven runtime adapter resolution,
profile-derived lifecycle rules, and fake external adapter packs.
| Dimension | Current | Target | Evidence | Needed Next |
| --- | ---: | ---: | --- | --- |
| Intent and boundaries | 4.0 | 5.0 | `INTENT.md`, `SCOPE.md`, `README.md`, architecture doc, PMEM-WP-0001 closure | Keep boundaries current as runtime behavior expands. |
| Package foundation | 4.0 | 4.0 | Python package, exports, runtime facade, CLI entrypoint, config, service contracts, dependency-light tests | Maintain public API compatibility as adapters expand. |
| Profile contract ingress | 3.2 | 4.0 | Markitect-compatible profile loading, diagnostics, runtime envelopes, profile-derived runtime config, local adapter alias normalization | Add richer compatibility coverage. |
| Graph/event contract ingress | 3.5 | 4.0 | Graph loading, edge endpoint diagnostics, event model, JSONL event log, export, repair diagnostics, service import/export contracts | Add broader external adapter fixtures. |
| Graph/event contract ingress | 3.7 | 4.0 | Graph loading, edge endpoint diagnostics, event model, JSONL event log, export, repair diagnostics, service import/export contracts, fake external graph/event adapters | Add broader external adapter fixtures. |
| Phase domain model | 3.4 | 4.0 | Phases, memory kinds, lifecycle states, actions, explicit path records, profile-derived transition rules | Add migration semantics. |
| Profile execution planning | 3.8 | 4.0 | Adapter plan, capabilities, policy gates, fallback behavior, CLI output, snapshot fixture, service contract, config-driven local adapter resolution | Add external adapter injection coverage. |
| Profile execution planning | 4.0 | 4.0 | Adapter plan, capabilities, policy gates, fallback behavior, CLI output, snapshot fixture, service contract, config-driven local and fake external adapter resolution | Maintain compatibility as live adapters land. |
| Lifecycle planning | 3.6 | 4.0 | Transition, retention, refresh, compaction dry-run plans, profile-driven lifecycle rules, review-gated local apply | Add service apply contracts and migration semantics. |
| Activation planning | 3.8 | 5.0 | Budgeted selection, Markitect-compatible selection output, package request envelope, graph neighborhoods, event paths, ranking, metadata preservation, metrics | Add semantic-index adapters and broader evaluation corpora. |
| Local persistence | 3.0 | 4.0 | Versioned local workspace, file-backed graph store, JSONL event log, JSONL audit sink | Add migration/repair utilities and stronger durability semantics. |
| Policy and audit | 3.2 | 5.0 | Operation points, policy gateway checks, audit schema, review records, redaction, activation denials | Add external policy adapters and richer audit retention behavior. |
| Observability and diagnostics | 3.5 | 4.0 | Planner diagnostics, runtime diagnostics, event log corruption checks, repair diagnostics, policy denial diagnostics, health envelopes, adapter status | Add production telemetry adapters. |
| Markitect interop | 3.5 | 4.0 | Compatible contract ingress, optional validation boundary, enriched selection metadata, package request/response envelopes | Add live optional Markitect compiler adapter when available. |
| Kontextual/Infospace interop | 2.5 | 4.0 | Boundaries documented, small derived fixtures, activation quality report fixture, Kontextual delegation envelope | Add live fake/real delegation adapters and broader Infospace reports. |
| Testing and evaluation | 4.0 | 4.0 | 58 deterministic tests over planners, adapters, runtime envelopes, CLI, snapshots, file-store round trips, lifecycle rules, apply denial, review records, audit schema, policy redaction, Markitect bridge fixtures, retrieval, activation metrics, service contracts, config, health, conformance, and adapter resolution | Add broader evaluation corpora. |
| Service readiness | 3.9 | 4.0 | Runtime ports, service contracts, config model, profile-derived adapter resolution, health checks, local service runner lifecycle support, adapter conformance helpers | Add framework-specific bindings and production adapter packs. |
| Developer experience | 3.6 | 4.0 | README quick start, package map, runtime facade docs, CLI examples, local persistence guide, service adapter resolution docs, lifecycle rule docs | Add troubleshooting and richer examples. |
| Policy and audit | 3.5 | 5.0 | Operation points, policy gateway checks, audit schema, review records, redaction, activation denials, fake external policy/audit adapters | Add richer audit retention behavior and live policy adapters. |
| Observability and diagnostics | 3.8 | 4.0 | Planner diagnostics, runtime diagnostics, event log corruption checks, repair diagnostics, policy denial diagnostics, health envelopes, adapter status, fake telemetry audit sink | Add production telemetry adapters. |
| Markitect interop | 3.7 | 4.0 | Compatible contract ingress, optional validation boundary, enriched selection metadata, package request/response envelopes, fake Markitect package compiler | Add live optional Markitect compiler adapter when available. |
| Kontextual/Infospace interop | 3.0 | 4.0 | Boundaries documented, small derived fixtures, activation quality report fixture, Kontextual delegation envelope, fake Kontextual runtime registry | Add live delegation adapters and broader Infospace reports. |
| Testing and evaluation | 4.0 | 4.0 | 60 deterministic tests over planners, adapters, runtime envelopes, CLI, snapshots, file-store round trips, lifecycle rules, apply denial, review records, audit schema, policy redaction, Markitect bridge fixtures, retrieval, activation metrics, service contracts, config, health, conformance, adapter resolution, and fake external packs | Add broader evaluation corpora. |
| Service readiness | 4.0 | 4.0 | Runtime ports, service contracts, config model, profile-derived adapter resolution, health checks, local service runner lifecycle support, adapter conformance helpers, fake external adapter pack | Add framework-specific bindings and production adapter packs. |
| Developer experience | 3.7 | 4.0 | README quick start, package map, runtime facade docs, CLI examples, local persistence guide, service adapter resolution docs, lifecycle rule docs, external adapter pack docs | Add troubleshooting and richer examples. |
## Progress Update - PMEM-WP-0002
@@ -194,6 +194,27 @@ Remaining maturity blockers:
- Live external adapter implementations.
- Broader evaluation corpora.
## Progress Update - PMEM-WP-0010
Closed on 2026-05-18:
- Added `ExternalAdapterPack`.
- Added deterministic fake external graph and event adapters.
- Added fake Markitect package compiler.
- Added fake external policy gateway.
- Added fake telemetry audit sink with retention metadata.
- Added fake semantic index and fake Kontextual runtime registry.
- Added `fake_external_adapter_pack()` and `fake_external_runtime_config()`.
- Added conformance tests and all-external runtime wiring tests.
- Added external adapter pack documentation and README links.
Remaining maturity blockers:
- Live external adapter implementations.
- Broader evaluation corpora.
- Framework-specific service bindings.
- Production audit retention and telemetry integrations.
## Progress Update - PMEM-WP-0004
Closed on 2026-05-18:
@@ -259,6 +280,7 @@ flowchart TD
WP7["PMEM-WP-0007\nService readiness and adapters"]
WP8["PMEM-WP-0008\nProfile-driven runtime config"]
WP9["PMEM-WP-0009\nProfile-driven lifecycle rules"]
WP10["PMEM-WP-0010\nFake external adapter packs"]
WP1 --> WP2
WP2 --> WP3
@@ -277,6 +299,8 @@ flowchart TD
WP2 --> WP9
WP4 --> WP9
WP8 --> WP9
WP7 --> WP10
WP8 --> WP10
```
## Next Tracking Cadence

View File

@@ -0,0 +1,116 @@
---
id: PMEM-WP-0010
type: workplan
title: "Fake External Adapter Packs"
domain: markitect
repo: phase-memory
status: finished
owner: codex
topic_slug: phase-memory
created: "2026-05-18"
updated: "2026-05-18"
---
# PMEM-WP-0010: Fake External Adapter Packs
## Goal
Provide a deterministic fake external adapter pack that exercises the same
adapter injection path live Markitect, Kontextual, semantic index, policy, and
telemetry integrations will use.
This refines the external adapter workplan direction into a testable first
slice: no live service dependency, but no silent fallback either.
## Current Evidence
PMEM-WP-0008 made `external` adapter modes explicit and blocked unresolved
external adapters. PMEM-WP-0009 added profile-driven lifecycle planning. The
remaining service-readiness gap was a supplied external adapter pack that could
pass public conformance helpers and run through the service/runtime wiring.
## Non-Goals
- Call live Markitect, Kontextual, telemetry, or policy services.
- Add credentials, network dependencies, or deployment configuration.
- Replace the local file-backed adapters.
## Implementation Update - 2026-05-18
Implemented the fake external adapter pack:
- Added `ExternalAdapterPack`.
- Added fake external graph store and event log adapters.
- Added fake Markitect package compiler.
- Added fake external policy gateway.
- Added fake telemetry audit sink with retention metadata.
- Added fake semantic index and Kontextual runtime registry.
- Added `fake_external_adapter_pack()` and `fake_external_runtime_config()`.
- Added conformance and runtime wiring tests.
- Added docs and README links.
## T01 - Add fake external adapter pack model
```task
id: PMEM-WP-0010-T01
status: done
priority: high
```
Add a small pack record with adapter instances, capabilities, and metadata.
## T02 - Add fake Markitect and Kontextual adapters
```task
id: PMEM-WP-0010-T02
status: done
priority: high
```
Provide fake package compiler and runtime registry adapters that match the
public ports.
## T03 - Add fake policy, audit, semantic, graph, and event adapters
```task
id: PMEM-WP-0010-T03
status: done
priority: high
```
Provide deterministic fake implementations for the remaining runtime ports.
## T04 - Prove external config wiring
```task
id: PMEM-WP-0010-T04
status: done
priority: high
```
Resolve an all-external runtime config with the supplied fake pack and verify
runtime package compilation plus registry publication.
## T05 - Document adapter pack usage
```task
id: PMEM-WP-0010-T05
status: done
priority: medium
```
Document fake pack usage, conformance expectations, and the relationship to
future live adapters.
## Acceptance Criteria
- The fake pack satisfies all public conformance helpers.
- An all-external runtime config resolves when the pack is supplied.
- Missing external adapter behavior remains unchanged.
- Runtime envelopes can compile packages through the fake Markitect adapter.
- Runtime envelopes can be published through the fake Kontextual registry.
## Closure Review - 2026-05-18
Closed after adding fake external adapter implementations, adapter pack helpers,
runtime wiring coverage, and documentation.