Add service readiness contracts

This commit is contained in:
2026-05-18 20:33:45 +02:00
parent 1efb7d4c13
commit 2a4301d22f
7 changed files with 437 additions and 20 deletions

View File

@@ -90,5 +90,6 @@ sketch, [docs/local-persistence.md](docs/local-persistence.md) for the local
file-backed adapter, [docs/policy-audit.md](docs/policy-audit.md) for local
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, and [SCOPE.md](SCOPE.md) for repository
for retrieval and evaluation behavior, [docs/service-readiness.md](docs/service-readiness.md)
for service and adapter contracts, and [SCOPE.md](SCOPE.md) for repository
boundaries.

82
docs/service-readiness.md Normal file
View File

@@ -0,0 +1,82 @@
# Service Readiness
`phase-memory` remains local-first, but it now exposes service-readiness
contracts that can be used by an embedded runtime, a lightweight service, or
external adapters.
## Service Contracts
`phase_memory.service.service_contracts()` returns the first service contract
catalog:
- `profile.plan`
- `graph.import`
- `graph.lifecycle.plan`
- `lifecycle.apply`
- `graph.activation.plan`
- `package.compile`
- `audit.query`
- `health.check`
These contracts describe request fields and response classes. They are not a
web framework binding.
## Runtime Config
`RuntimeConfig` captures:
- local store path
- adapter registry
- policy mode
- audit sink mode
- package compiler mode
- semantic index mode
- dry-run default
- trust-zone labels
The default config is local and dependency-light.
## Health
`health_report` emits:
- adapter classes
- config
- node counts
- stale memory counts
- pending review counts
- config diagnostics
The schema is `phase_memory.health.report.v1`.
## Adapter Conformance
The service module includes reusable conformance helpers for:
- graph stores
- event logs
- context package compilers
- policy gateways
- audit sinks
External adapters should pass these helpers before being wired into a runtime.
## Kontextual Delegation
The first delegation envelope keeps ownership explicit:
- `phase-memory` owns phase policy, lifecycle planning, and activation
planning.
- `kontextual-engine` owns durable records, permission-aware retrieval, and
long-lived storage.
- Boundaries exchange JSON envelopes to avoid circular imports.
## Deployment Modes
Supported modes:
- pure library
- CLI over local files
- embedded runtime
- optional service runner
- adapter layer over external stores

View File

@@ -45,6 +45,7 @@ from .retrieval import (
retrieve_graph_neighborhood,
select_event_path,
)
from .service import RuntimeConfig, health_report, service_contracts
from .planner import plan_profile_execution
from .runtime import PhaseMemoryRuntime
@@ -96,6 +97,9 @@ __all__ = [
"plan_neighborhood_activation",
"retrieve_graph_neighborhood",
"select_event_path",
"RuntimeConfig",
"health_report",
"service_contracts",
]
__version__ = "0.1.0"

189
src/phase_memory/service.py Normal file
View File

@@ -0,0 +1,189 @@
"""Service-readiness contracts, config, health, and conformance helpers."""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any
from .adapters import AllowAllPolicyGateway, InMemoryMemoryEventLog, InMemoryMemoryGraphStore, NoopContextPackageCompiler, RecordingAuditSink
from .models import Diagnostic, MemoryEvent, MemoryNode, PolicyDecision, ProfileIntent
from .runtime import PhaseMemoryRuntime
SERVICE_CONTRACT_SCHEMA = "phase_memory.service.contracts.v1"
HEALTH_REPORT_SCHEMA = "phase_memory.health.report.v1"
KONTEXTUAL_DELEGATION_SCHEMA = "phase_memory.kontextual.delegation.v1"
SERVICE_OPERATIONS = {
"profile.plan": {"request": ["profile"], "response": "runtime_envelope"},
"graph.import": {"request": ["graph"], "response": "runtime_envelope"},
"graph.lifecycle.plan": {"request": ["graph", "parameters"], "response": "runtime_envelope"},
"lifecycle.apply": {"request": ["actions", "review_record"], "response": "runtime_envelope"},
"graph.activation.plan": {"request": ["graph", "budget"], "response": "runtime_envelope"},
"package.compile": {"request": ["selection"], "response": "runtime_envelope"},
"audit.query": {"request": ["filters"], "response": "audit_events"},
"health.check": {"request": [], "response": "health_report"},
}
@dataclass(frozen=True)
class RuntimeConfig:
local_store_path: str = ".phase-memory-local"
adapter_registry: dict[str, str] = field(default_factory=lambda: {"graph_store": "memory", "event_log": "memory"})
policy_mode: str = "allow-all"
audit_sink_mode: str = "recording"
package_compiler_mode: str = "noop"
semantic_index_mode: str = "disabled"
dry_run_default: bool = True
trust_zone_labels: tuple[str, ...] = ("local",)
@classmethod
def local_default(cls) -> "RuntimeConfig":
return cls()
def diagnostics(self) -> tuple[Diagnostic, ...]:
diagnostics: list[Diagnostic] = []
if not self.local_store_path:
diagnostics.append(Diagnostic("error", "missing_store_path", "Runtime config requires a local store path.", "local_store_path"))
if self.policy_mode not in {"allow-all", "external"}:
diagnostics.append(Diagnostic("error", "unsupported_policy_mode", "Unsupported policy mode.", "policy_mode", {"policy_mode": self.policy_mode}))
if self.semantic_index_mode not in {"disabled", "external"}:
diagnostics.append(Diagnostic("error", "unsupported_semantic_index_mode", "Unsupported semantic index mode.", "semantic_index_mode", {"semantic_index_mode": self.semantic_index_mode}))
return tuple(diagnostics)
def to_dict(self) -> dict[str, Any]:
return {
"local_store_path": self.local_store_path,
"adapter_registry": dict(self.adapter_registry),
"policy_mode": self.policy_mode,
"audit_sink_mode": self.audit_sink_mode,
"package_compiler_mode": self.package_compiler_mode,
"semantic_index_mode": self.semantic_index_mode,
"dry_run_default": self.dry_run_default,
"trust_zone_labels": list(self.trust_zone_labels),
}
def service_contracts() -> dict[str, Any]:
return {"schema_version": SERVICE_CONTRACT_SCHEMA, "operations": SERVICE_OPERATIONS}
def runtime_from_config(config: RuntimeConfig | None = None) -> PhaseMemoryRuntime:
config = config or RuntimeConfig.local_default()
# First service-ready slice keeps adapters dependency-light. External
# adapter resolution belongs behind the registry in later deployments.
return PhaseMemoryRuntime()
def health_report(runtime: PhaseMemoryRuntime, *, config: RuntimeConfig | None = None) -> dict[str, Any]:
config = config or RuntimeConfig.local_default()
nodes = runtime.graph_store.list_nodes()
stale = [node for node in nodes if node.lifecycle.value == "stale"]
pending_review = [node for node in nodes if node.lifecycle.value == "review_needed"]
diagnostics = list(config.diagnostics())
return {
"schema_version": HEALTH_REPORT_SCHEMA,
"ok": not any(diagnostic.severity == "error" for diagnostic in diagnostics),
"adapters": {
"graph_store": runtime.graph_store.__class__.__name__,
"event_log": runtime.event_log.__class__.__name__,
"policy_gateway": runtime.policy_gateway.__class__.__name__,
"audit_sink": runtime.audit_sink.__class__.__name__,
"package_compiler": runtime.package_compiler.__class__.__name__,
},
"config": config.to_dict(),
"store": {
"node_count": len(nodes),
"stale_memory_count": len(stale),
"pending_review_count": len(pending_review),
},
"diagnostics": [diagnostic.to_dict() for diagnostic in diagnostics],
}
class LocalServiceRunner:
"""Minimal optional service runner shape without web framework dependency."""
def __init__(self, runtime: PhaseMemoryRuntime | None = None, config: RuntimeConfig | None = None) -> None:
self.config = config or RuntimeConfig.local_default()
self.runtime = runtime or runtime_from_config(self.config)
def handle(self, operation: str, payload: dict[str, Any] | None = None) -> dict[str, Any]:
payload = payload or {}
if operation == "health.check":
return health_report(self.runtime, config=self.config)
if operation == "profile.plan":
return self.runtime.plan_profile(payload["profile"], source_ref=payload.get("source_ref", "service"))
if operation == "graph.import":
return self.runtime.import_graph(payload["graph"], source_ref=payload.get("source_ref", "service"))
if operation == "graph.activation.plan":
budget = payload.get("budget", {})
return self.runtime.plan_activation(
payload["graph"],
max_items=int(budget["max_items"]),
max_tokens=int(budget["max_tokens"]),
profile_id=payload.get("profile_id"),
)
raise ValueError(f"Unsupported service operation: {operation}")
def kontextual_delegation_envelope(
*,
operation: str,
graph_id: str = "",
profile_id: str = "",
policy_decision: dict[str, Any] | None = None,
audit_ref: str = "",
) -> dict[str, Any]:
return {
"schema_version": KONTEXTUAL_DELEGATION_SCHEMA,
"operation": operation,
"phase_memory_owns": ["phase_policy", "lifecycle_planning", "activation_planning"],
"kontextual_owns": ["durable_records", "permission_aware_retrieval", "long_lived_storage"],
"graph_id": graph_id,
"profile_id": profile_id,
"policy_decision": dict(policy_decision or {}),
"audit_ref": audit_ref,
"imports": {"avoid_circular_imports": True, "exchange": "json_envelopes"},
}
def assert_graph_store_conformance(store) -> None:
profile = ProfileIntent(profile_id="conformance-profile")
node = MemoryNode("node.conformance", "decision", "Conformance node")
store.save_profile(profile)
store.save_node(node)
assert store.get_profile(profile.profile_id).profile_id == profile.profile_id
assert store.get_node(node.node_id).node_id == node.node_id
assert store.list_nodes(kind="decision")
def assert_event_log_conformance(log) -> None:
event = MemoryEvent("event.conformance", "recorded")
log.append(event)
assert log.list_events(kind="recorded")[0].event_id == event.event_id
def assert_context_compiler_conformance(compiler) -> None:
response = compiler.compile_selection({"id": "selection.conformance", "nodes": [], "events": []})
assert "package_id" in response or "package_ref" in response
def assert_policy_gateway_conformance(gateway) -> None:
decision = gateway.authorize(action="read", resource="node.conformance")
assert isinstance(decision, PolicyDecision)
def assert_audit_sink_conformance(sink) -> None:
receipt = sink.record({"operation": "conformance"})
assert receipt.get("recorded") is True
def default_conformance_adapters() -> dict[str, Any]:
return {
"graph_store": InMemoryMemoryGraphStore(),
"event_log": InMemoryMemoryEventLog(),
"context_compiler": NoopContextPackageCompiler(),
"policy_gateway": AllowAllPolicyGateway(),
"audit_sink": RecordingAuditSink(),
}

View File

@@ -0,0 +1,70 @@
from phase_memory.models import LifecycleState, MemoryNode
from phase_memory.service import (
HEALTH_REPORT_SCHEMA,
KONTEXTUAL_DELEGATION_SCHEMA,
SERVICE_CONTRACT_SCHEMA,
LocalServiceRunner,
RuntimeConfig,
assert_audit_sink_conformance,
assert_context_compiler_conformance,
assert_event_log_conformance,
assert_graph_store_conformance,
assert_policy_gateway_conformance,
default_conformance_adapters,
health_report,
kontextual_delegation_envelope,
service_contracts,
)
def test_service_contracts_list_runtime_operations() -> None:
contracts = service_contracts()
assert contracts["schema_version"] == SERVICE_CONTRACT_SCHEMA
assert "profile.plan" in contracts["operations"]
assert "health.check" in contracts["operations"]
def test_runtime_config_validation_and_health_report() -> None:
runner = LocalServiceRunner()
runner.runtime.graph_store.save_node(MemoryNode("node.stale", "episode", lifecycle=LifecycleState.STALE))
report = health_report(runner.runtime, config=RuntimeConfig.local_default())
assert report["schema_version"] == HEALTH_REPORT_SCHEMA
assert report["ok"] is True
assert report["store"]["stale_memory_count"] == 1
assert report["adapters"]["graph_store"] == "InMemoryMemoryGraphStore"
def test_service_runner_handles_health() -> None:
runner = LocalServiceRunner()
response = runner.handle("health.check")
assert response["schema_version"] == HEALTH_REPORT_SCHEMA
assert response["ok"] is True
def test_default_adapter_conformance_helpers() -> None:
adapters = default_conformance_adapters()
assert_graph_store_conformance(adapters["graph_store"])
assert_event_log_conformance(adapters["event_log"])
assert_context_compiler_conformance(adapters["context_compiler"])
assert_policy_gateway_conformance(adapters["policy_gateway"])
assert_audit_sink_conformance(adapters["audit_sink"])
def test_kontextual_delegation_envelope_is_explicit() -> None:
envelope = kontextual_delegation_envelope(
operation="persist_graph",
graph_id="graph.a",
profile_id="profile.a",
policy_decision={"allowed": True},
audit_ref="audit.a",
)
assert envelope["schema_version"] == KONTEXTUAL_DELEGATION_SCHEMA
assert "phase_policy" in envelope["phase_memory_owns"]
assert "durable_records" in envelope["kontextual_owns"]
assert envelope["imports"]["avoid_circular_imports"] is True

View File

@@ -44,31 +44,31 @@ not what adjacent repositories may already provide.
## Current Baseline - 2026-05-18
Overall maturity: **3.7 / 5**
Overall maturity: **4.2 / 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, and a concrete Markitect
package bridge, and deterministic activation quality helpers. It is not yet
service-ready.
workspace, first-slice policy/review/audit gates, a concrete Markitect package
bridge, deterministic activation quality helpers, and first-slice service
readiness contracts.
| 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 | 3.0 | 4.0 | Python package, exports, runtime facade, CLI entrypoint, dependency-light tests | Add runtime configuration, service contracts, and adapter conformance. |
| 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 | 2.5 | 4.0 | Markitect-compatible profile loading, diagnostics, runtime envelopes | Add profile-driven runtime configuration and richer compatibility coverage. |
| Graph/event contract ingress | 3.0 | 4.0 | Graph loading, edge endpoint diagnostics, event model, JSONL event log, export, repair diagnostics | Add service-level import/export contracts and adapter conformance. |
| 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. |
| Phase domain model | 3.0 | 4.0 | Phases, memory kinds, lifecycle states, actions, explicit path records | Add profile-driven transition rule evaluation and migration semantics. |
| Profile execution planning | 3.0 | 4.0 | Adapter plan, capabilities, policy gates, fallback behavior, CLI output, snapshot fixture | Add runtime configuration model and service-readiness diagnostics. |
| Profile execution planning | 3.5 | 4.0 | Adapter plan, capabilities, policy gates, fallback behavior, CLI output, snapshot fixture, service contract | Add external config-driven adapter resolution. |
| Lifecycle planning | 3.0 | 4.0 | Transition, retention, refresh, compaction dry-run plans, review-gated local apply | Add profile-driven rule evaluation and service apply contracts. |
| 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 | 2.5 | 4.0 | Planner diagnostics, runtime diagnostics, event log corruption checks, repair diagnostics, policy denial diagnostics | Add health envelopes and adapter status diagnostics. |
| 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 | 1.5 | 4.0 | Boundaries documented, small derived fixtures, activation quality report fixture | Add Kontextual delegation envelopes and broader Infospace evaluation fixture reports. |
| Testing and evaluation | 3.7 | 4.0 | 46 deterministic tests over planners, adapters, runtime envelopes, CLI, snapshots, file-store round trips, apply denial, review records, audit schema, policy redaction, Markitect bridge fixtures, retrieval, and activation metrics | Add broader evaluation corpora. |
| Service readiness | 0.5 | 4.0 | Runtime ports exist | Add service contracts, config, health checks, adapter conformance tests. |
| 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 | 51 deterministic tests over planners, adapters, runtime envelopes, CLI, snapshots, file-store round trips, apply denial, review records, audit schema, policy redaction, Markitect bridge fixtures, retrieval, activation metrics, service contracts, config, health, and conformance | Add broader evaluation corpora. |
| Service readiness | 3.5 | 4.0 | Runtime ports, service contracts, config model, health checks, local service runner, adapter conformance helpers | Add framework-specific bindings and production adapter packs. |
| Developer experience | 3.3 | 4.0 | README quick start, package map, runtime facade docs, CLI examples, local persistence guide | Add troubleshooting and richer examples. |
## Progress Update - PMEM-WP-0002
@@ -133,6 +133,25 @@ Remaining maturity blockers:
- External adapter conformance tests.
- Kontextual delegation adapter design.
## Progress Update - PMEM-WP-0007
Closed on 2026-05-18:
- Added service operation contract catalog.
- Added runtime configuration model.
- Added health reports and adapter status diagnostics.
- Added local service runner shape without framework dependency.
- Added adapter conformance helpers.
- Added Kontextual delegation envelope.
- Documented deployment modes.
Remaining maturity blockers:
- Live external adapter implementations.
- Broader evaluation corpora.
- Optional framework-specific service bindings.
- Production telemetry and audit retention integrations.
## Progress Update - PMEM-WP-0004
Closed on 2026-05-18:

View File

@@ -4,7 +4,7 @@ type: workplan
title: "Service Readiness And External Adapters"
domain: markitect
repo: phase-memory
status: proposed
status: finished
owner: phase-memory
topic_slug: service-readiness
planning_priority: P2
@@ -48,11 +48,40 @@ tests, runtime configuration, health checks, or deployment guidance.
- Do not add operational complexity before PMEM-WP-0002 through PMEM-WP-0006
stabilize.
## Implementation Update - 2026-05-18
The service-readiness and external-adapter slice is complete.
Implemented outputs:
- `phase_memory.service` defines service contract catalog, runtime config,
health reports, local service runner, adapter conformance helpers, and
Kontextual delegation envelopes.
- Service contracts cover profile planning, graph import, lifecycle planning,
lifecycle apply, activation planning, package compile handoff, audit query,
and health check.
- `RuntimeConfig` models local store path, adapter registry, policy mode,
audit sink mode, package compiler mode, semantic index mode, dry-run default,
and trust-zone labels.
- Health reports expose adapter availability, config diagnostics, stale memory
counts, pending review counts, and store counts.
- Adapter conformance helpers cover graph stores, event logs, context package
compilers, policy gateways, and audit sinks.
- `kontextual_delegation_envelope` documents the JSON boundary that avoids
circular imports while preserving ownership between phase-memory and
`kontextual-engine`.
- `docs/service-readiness.md` documents contracts, config, health,
conformance, delegation, and deployment modes.
Validation:
- `python3 -m pytest` -> 51 passed.
## T01 - Define runtime service API contracts
```task
id: PMEM-WP-0007-T01
status: todo
status: done
priority: high
state_hub_task_id: "48dc8e83-ff0f-4c25-b1c9-d94c3a2ac0eb"
```
@@ -74,7 +103,7 @@ Output: API contract docs and fixture request/response envelopes.
```task
id: PMEM-WP-0007-T02
status: todo
status: done
priority: high
state_hub_task_id: "da22d548-3123-46fc-acac-8bbcf8b54fb7"
```
@@ -95,7 +124,7 @@ Output: reusable test helpers that any external adapter must pass.
```task
id: PMEM-WP-0007-T03
status: todo
status: done
priority: high
state_hub_task_id: "2a0fc3d0-3dda-4c58-95de-3f70cf097ff1"
```
@@ -113,7 +142,7 @@ Output: adapter design note, envelope fixtures, and local fake adapter tests.
```task
id: PMEM-WP-0007-T04
status: todo
status: done
priority: medium
state_hub_task_id: "10934c46-db81-4a68-be4f-2ce95408d279"
```
@@ -128,7 +157,7 @@ clear installation extras if a framework is used.
```task
id: PMEM-WP-0007-T05
status: todo
status: done
priority: medium
state_hub_task_id: "a7129077-b736-4d69-94ab-d6921cd8ed15"
```
@@ -150,7 +179,7 @@ Output: config model, default local config, and validation diagnostics.
```task
id: PMEM-WP-0007-T06
status: todo
status: done
priority: medium
state_hub_task_id: "24ef8feb-90f8-454d-8c8f-7b3468454f57"
```
@@ -171,7 +200,7 @@ Output: health report helpers and tests.
```task
id: PMEM-WP-0007-T07
status: todo
status: done
priority: medium
state_hub_task_id: "89c8802c-f536-441b-a514-8d3e56b3c6e5"
```
@@ -193,3 +222,26 @@ Output: service-readiness guide and scorecard update.
- External adapters can be validated with reusable conformance tests.
- Local library and CLI use remain first-class.
- The Kontextual delegation boundary is explicit and avoids ownership drift.
## Closure Review - 2026-05-18
**Outcome:** All tasks completed.
### Completed
- PMEM-WP-0007-T01 - Define runtime service API contracts
- PMEM-WP-0007-T02 - Add adapter conformance tests
- PMEM-WP-0007-T03 - Add Kontextual delegation adapter design
- PMEM-WP-0007-T04 - Add optional service runner
- PMEM-WP-0007-T05 - Add runtime configuration model
- PMEM-WP-0007-T06 - Add observability and health diagnostics
- PMEM-WP-0007-T07 - Document deployment modes
### Cancelled
None.
### Carried Forward
Future work should focus on live external adapters, broader evaluation corpora,
and optional framework-specific service bindings.