"""Service-readiness contracts, config, health, and conformance helpers.""" from __future__ import annotations from dataclasses import dataclass, field from pathlib import Path from typing import Any from .adapters import ( AllowAllPolicyGateway, FileBackedMemoryGraphStore, InMemoryMemoryEventLog, InMemoryMemoryGraphStore, InMemoryRuntimeRegistry, InMemorySemanticIndex, JsonlAuditSink, JsonlMemoryEventLog, 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", "profile?"], "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_gateway": "allow-all", "audit_sink": "recording", "package_compiler": "noop", "semantic_index": "disabled", "runtime_registry": "memory", } ) policy_mode: str = "allow-all" audit_sink_mode: str = "recording" package_compiler_mode: str = "noop" semantic_index_mode: str = "disabled" runtime_registry_mode: str = "memory" dry_run_default: bool = True trust_zone_labels: tuple[str, ...] = ("local",) @classmethod def local_default(cls) -> "RuntimeConfig": return cls() @classmethod def from_profile(cls, profile: ProfileIntent | dict[str, Any], *, local_store_path: str | None = None) -> "RuntimeConfig": profile_intent = profile if isinstance(profile, ProfileIntent) else ProfileIntent.from_mapping(profile) base = cls.local_default() runtime_metadata = _runtime_metadata(profile_intent) stores = dict(profile_intent.stores) policy = dict(profile_intent.policy) activation = dict(profile_intent.activation) observability = dict(profile_intent.observability) store_path = str( local_store_path or runtime_metadata.get("local_store_path") or profile_intent.metadata.get("local_store_path") or stores.get("local_store_path") or base.local_store_path ) policy_mode = _mode_from(policy, ("mode", "policy_mode", "gateway"), default=base.policy_mode) audit_sink_mode = _mode_from(observability, ("audit_sink", "audit_sink_mode"), default=_store_mode(stores, ("audit_sink", "audit"), base.audit_sink_mode)) package_compiler_mode = _mode_from( runtime_metadata, ("package_compiler", "package_compiler_mode"), default=_store_mode(stores, ("package_compiler", "context_package", "package"), base.package_compiler_mode), ) semantic_index_mode = _mode_from( activation, ("semantic_index", "semantic_index_mode"), default=_store_mode(stores, ("semantic_index", "semantic"), base.semantic_index_mode), ) runtime_registry_mode = _mode_from( observability, ("runtime_registry", "runtime_registry_mode"), default=_mode_from(runtime_metadata, ("runtime_registry", "runtime_registry_mode"), default=base.runtime_registry_mode), ) graph_store_mode = _infer_graph_store_mode(stores, default=base.adapter_registry["graph_store"]) event_log_mode = _infer_event_log_mode(stores, default=base.adapter_registry["event_log"]) dry_run_default = _bool_from(policy, "dry_run_default", default=base.dry_run_default) trust_zone_labels = _string_tuple( policy.get("trust_zone_labels") or policy.get("trust_zones") or policy.get("required_labels") or base.trust_zone_labels ) return cls( local_store_path=store_path, adapter_registry={ "graph_store": graph_store_mode, "event_log": event_log_mode, "policy_gateway": policy_mode, "audit_sink": audit_sink_mode, "package_compiler": package_compiler_mode, "semantic_index": semantic_index_mode, "runtime_registry": runtime_registry_mode, }, policy_mode=policy_mode, audit_sink_mode=audit_sink_mode, package_compiler_mode=package_compiler_mode, semantic_index_mode=semantic_index_mode, runtime_registry_mode=runtime_registry_mode, dry_run_default=dry_run_default, trust_zone_labels=trust_zone_labels, ) def adapter_mode(self, adapter: str) -> str: legacy = { "graph_store": "memory", "event_log": "memory", "policy_gateway": self.policy_mode, "audit_sink": self.audit_sink_mode, "package_compiler": self.package_compiler_mode, "semantic_index": self.semantic_index_mode, "runtime_registry": self.runtime_registry_mode, } return _normalize_mode(self.adapter_registry.get(adapter) or legacy.get(adapter) or "") 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")) for adapter, allowed_modes in _SUPPORTED_ADAPTER_MODES.items(): mode = self.adapter_mode(adapter) if mode not in allowed_modes: diagnostics.append( Diagnostic( "error", "unsupported_adapter_mode", "Unsupported adapter mode.", f"adapter_registry.{adapter}", {"adapter": adapter, "mode": mode, "allowed_modes": sorted(allowed_modes)}, ) ) elif mode == "external": diagnostics.append( Diagnostic( "warn", "external_adapter_declared", "External adapter mode is declared and must be supplied by the caller.", f"adapter_registry.{adapter}", {"adapter": adapter}, ) ) 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, "runtime_registry_mode": self.runtime_registry_mode, "dry_run_default": self.dry_run_default, "trust_zone_labels": list(self.trust_zone_labels), } @dataclass(frozen=True) class RuntimeAdapterBundle: graph_store: Any event_log: Any package_compiler: Any policy_gateway: Any audit_sink: Any semantic_index: Any | None = None runtime_registry: Any | None = None diagnostics: tuple[Diagnostic, ...] = () def to_runtime(self) -> PhaseMemoryRuntime: errors = [diagnostic for diagnostic in self.diagnostics if diagnostic.severity == "error"] if errors: codes = ", ".join(diagnostic.code for diagnostic in errors) raise ValueError(f"Runtime adapters are not ready: {codes}") return PhaseMemoryRuntime( graph_store=self.graph_store, event_log=self.event_log, package_compiler=self.package_compiler, policy_gateway=self.policy_gateway, audit_sink=self.audit_sink, ) def to_dict(self) -> dict[str, Any]: return { "graph_store": self.graph_store.__class__.__name__, "event_log": self.event_log.__class__.__name__, "package_compiler": self.package_compiler.__class__.__name__, "policy_gateway": self.policy_gateway.__class__.__name__, "audit_sink": self.audit_sink.__class__.__name__, "semantic_index": self.semantic_index.__class__.__name__ if self.semantic_index is not None else "disabled", "runtime_registry": self.runtime_registry.__class__.__name__ if self.runtime_registry is not None else "disabled", "diagnostics": [diagnostic.to_dict() for diagnostic in self.diagnostics], } def service_contracts() -> dict[str, Any]: return {"schema_version": SERVICE_CONTRACT_SCHEMA, "operations": SERVICE_OPERATIONS} def resolve_runtime_adapters(config: RuntimeConfig | None = None, *, external_adapters: dict[str, Any] | None = None) -> RuntimeAdapterBundle: config = config or RuntimeConfig.local_default() external_adapters = dict(external_adapters or {}) diagnostics = list(config.diagnostics()) root = Path(config.local_store_path) graph_store = _resolve_adapter( "graph_store", config.adapter_mode("graph_store"), external_adapters, diagnostics, local_factories={ "memory": InMemoryMemoryGraphStore, "file": lambda: FileBackedMemoryGraphStore(root), }, ) event_log = _resolve_adapter( "event_log", config.adapter_mode("event_log"), external_adapters, diagnostics, local_factories={ "memory": InMemoryMemoryEventLog, "jsonl": lambda: JsonlMemoryEventLog(root / "events.jsonl"), }, ) package_compiler = _resolve_adapter( "package_compiler", config.adapter_mode("package_compiler"), external_adapters, diagnostics, local_factories={"noop": NoopContextPackageCompiler}, ) policy_gateway = _resolve_adapter( "policy_gateway", config.adapter_mode("policy_gateway"), external_adapters, diagnostics, local_factories={"allow-all": AllowAllPolicyGateway}, ) audit_sink = _resolve_adapter( "audit_sink", config.adapter_mode("audit_sink"), external_adapters, diagnostics, local_factories={ "recording": RecordingAuditSink, "jsonl": lambda: JsonlAuditSink(root / "audit.jsonl"), }, ) semantic_index = _resolve_adapter( "semantic_index", config.adapter_mode("semantic_index"), external_adapters, diagnostics, local_factories={ "disabled": lambda: None, "memory": InMemorySemanticIndex, }, ) runtime_registry = _resolve_adapter( "runtime_registry", config.adapter_mode("runtime_registry"), external_adapters, diagnostics, local_factories={"memory": InMemoryRuntimeRegistry}, ) return RuntimeAdapterBundle( graph_store=graph_store, event_log=event_log, package_compiler=package_compiler, policy_gateway=policy_gateway, audit_sink=audit_sink, semantic_index=semantic_index, runtime_registry=runtime_registry, diagnostics=tuple(diagnostics), ) def runtime_from_config(config: RuntimeConfig | None = None, *, external_adapters: dict[str, Any] | None = None) -> PhaseMemoryRuntime: return resolve_runtime_adapters(config, external_adapters=external_adapters).to_runtime() 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.""" SUPPORTED_OPERATIONS = tuple(SERVICE_OPERATIONS) def __init__( self, runtime: PhaseMemoryRuntime | None = None, config: RuntimeConfig | None = None, *, external_adapters: dict[str, Any] | None = None, ) -> None: self.config = config or RuntimeConfig.local_default() self.runtime = runtime or runtime_from_config(self.config, external_adapters=external_adapters) @classmethod def supported_operations(cls) -> tuple[str, ...]: return cls.SUPPORTED_OPERATIONS 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.lifecycle.plan": parameters = payload.get("parameters", {}) if payload.get("profile"): return self.runtime.plan_lifecycle_with_profile( payload["profile"], payload["graph"], source_ref=payload.get("source_ref", "service"), profile_source_ref=payload.get("profile_source_ref", "service-profile"), refresh_digests=dict(parameters.get("refresh_digests") or {}), compact_node_ids=tuple(parameters.get("compact_node_ids") or ()), ) return self.runtime.plan_lifecycle( payload["graph"], source_ref=payload.get("source_ref", "service"), stale_after_days=parameters.get("stale_after_days"), delete_after_days=parameters.get("delete_after_days"), refresh_digests=dict(parameters.get("refresh_digests") or {}), compact_node_ids=tuple(parameters.get("compact_node_ids") or ()), ) 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"), priority_node_ids=tuple(payload.get("priority_node_ids") or ()), include_events=bool(payload.get("include_events", True)), policy_context=dict(payload.get("policy_context") or {}), ) if operation == "package.compile": return self.runtime.compile_package( payload["selection"], source_ref=payload.get("source_ref", "service"), ) if operation == "lifecycle.apply": return self.runtime.apply_lifecycle_actions( payload["actions"], approval_marker=str(payload.get("approval_marker") or ""), review_record=payload.get("review_record"), source_ref=payload.get("source_ref", "service"), ) if operation == "audit.query": return self.runtime.query_audit( dict(payload.get("filters") or {}), source_ref=payload.get("source_ref", "service"), ) 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 assert sink.query(operation="conformance")[0]["operation"] == "conformance" def assert_semantic_index_conformance(index) -> None: node = MemoryNode("node.semantic", "decision", "Conformance search target", metadata={"graph_id": "graph.conformance"}) receipt = index.upsert_nodes([node]) results = index.query(graph_id="graph.conformance", query="search target", limit=5) assert receipt.get("upserted") == 1 assert results and results[0]["id"] == node.node_id def assert_runtime_registry_conformance(registry) -> None: envelope = {"operation_id": "op.conformance", "operation": "conformance"} receipt = registry.publish_runtime_envelope(envelope) fetched = registry.fetch_runtime_envelope(receipt["reference"]) assert receipt["published"] is True assert fetched["operation_id"] == "op.conformance" def default_conformance_adapters() -> dict[str, Any]: return { "graph_store": InMemoryMemoryGraphStore(), "event_log": InMemoryMemoryEventLog(), "context_compiler": NoopContextPackageCompiler(), "policy_gateway": AllowAllPolicyGateway(), "audit_sink": RecordingAuditSink(), "semantic_index": InMemorySemanticIndex(), "runtime_registry": InMemoryRuntimeRegistry(), } _SUPPORTED_ADAPTER_MODES = { "graph_store": {"memory", "file", "external"}, "event_log": {"memory", "jsonl", "external"}, "policy_gateway": {"allow-all", "external"}, "audit_sink": {"recording", "jsonl", "external"}, "package_compiler": {"noop", "external"}, "semantic_index": {"disabled", "memory", "external"}, "runtime_registry": {"memory", "external"}, } _MODE_ALIASES = { "allow_all": "allow-all", "allowall": "allow-all", "local-allow-all": "allow-all", "file-backed": "file", "file-backed-graph-store": "file", "local-graph-store": "file", "local-json": "file", "json-lines": "jsonl", "local-event-log": "jsonl", "jsonl-event-log": "jsonl", "recording-audit": "recording", "jsonl-audit": "jsonl", "noop-compiler": "noop", "markitect-context-package": "noop", "in-memory": "memory", "local-memory": "memory", "off": "disabled", "none": "disabled", "false": "disabled", "true": "memory", } def _resolve_adapter( adapter: str, mode: str, external_adapters: dict[str, Any], diagnostics: list[Diagnostic], *, local_factories: dict[str, Any], ) -> Any: if mode == "external": if adapter in external_adapters: return external_adapters[adapter] diagnostics.append( Diagnostic( "error", "missing_external_adapter", "External adapter mode was requested but no adapter instance was supplied.", f"adapter_registry.{adapter}", {"adapter": adapter}, ) ) return None factory = local_factories.get(mode) if factory is None: diagnostics.append( Diagnostic( "error", "unresolved_adapter_mode", "Adapter mode cannot be resolved by the local runtime.", f"adapter_registry.{adapter}", {"adapter": adapter, "mode": mode}, ) ) return None return factory() def _runtime_metadata(profile: ProfileIntent) -> dict[str, Any]: runtime = profile.metadata.get("runtime") return dict(runtime) if isinstance(runtime, dict) else {} def _mode_from(mapping: dict[str, Any], keys: tuple[str, ...], *, default: str) -> str: for key in keys: if key in mapping: return _normalize_mode(mapping[key]) return _normalize_mode(default) def _store_mode(stores: dict[str, str], keys: tuple[str, ...], default: str) -> str: for key in keys: if key in stores: return _normalize_mode(stores[key]) return _normalize_mode(default) def _infer_graph_store_mode(stores: dict[str, str], *, default: str) -> str: specific = _store_mode(stores, ("graph_store", "memory_graph", "graph"), "") if specific: return specific values = {_normalize_mode(value) for value in stores.values()} if "file" in values: return "file" return _normalize_mode(default) def _infer_event_log_mode(stores: dict[str, str], *, default: str) -> str: specific = _store_mode(stores, ("event_log", "events", "conversation"), "") if specific: return specific values = {_normalize_mode(value) for value in stores.values()} if "jsonl" in values: return "jsonl" return _normalize_mode(default) def _normalize_mode(value: Any) -> str: text = str(value).strip().lower().replace("_", "-") return _MODE_ALIASES.get(text, text) def _bool_from(mapping: dict[str, Any], key: str, *, default: bool) -> bool: if key not in mapping: return default value = mapping[key] if isinstance(value, bool): return value return str(value).strip().lower() in {"1", "true", "yes", "on"} def _string_tuple(value: Any) -> tuple[str, ...]: if value is None: return () if isinstance(value, str): return (value,) return tuple(str(item) for item in value)