Orchestrata Ultimate › Plans › spec-audit › C-knowledge-memory
Cluster C — Knowledge, memory, context & compliance
Source: plans/spec-audit/C-knowledge-memory.md at 8a2cb42ad · last changed 2026-07-27 · first committed 2026-07-04 · 285 lines.
Prose pending.
Plan text
Cluster C — Knowledge, memory, context & compliance
Investigation agent C. Read-only audit of SPEC-008, 009, 010, 019, 026 against
actual code in src/orchestrata_ultimate/. Evidence-based; requirements.md
markers ignored per _METHOD.md. Builds on docs/spec-gap-register.md (which
already audited 008/009/010/026) and docs/context-injection.md.
SPEC-008 Knowledge Graph — DIVERGENCE (semantic layer now wired; only :Component + tree-sitter remain)
- Specifies: Neo4j graph as shared institutional memory — 13 node types, 19 relationship types, temporal props, context-injection Cypher patterns, tree-sitter code parsing (Python + Prolog), file-watch sync.
- Verified:
- Schema (FR-001/002): ✅ all node labels present incl. the semantic layer —
schema.py:19-35(LABEL_DECISION/ARTIFACT/VOLK/INTENT/PIPELINE_RUN/COMPONENT), W5 provenance edgesschema.py:89-101(EXECUTED_BY/TRIGGERED/GENERATED_BY/MADE/APPLIES_TO/JUSTIFIED_BY). - Semantic layer now wired (was flagged in register as unwritten): ✅
sync_intent_to_kgwrites(:Intent)-[:TRIGGERED]->(:PipelineRun)(pipeline_kg.py:223, calledpipeline.py:267);sync_artifacts_to_kgwrites(:Artifact)-[:GENERATED_BY]->(:Volk),(:PipelineRun)-[:EXECUTED_BY]->(:Volk)(pipeline_kg.py:201-213, called via getattrpipeline_session.py:236);sync_decisions_to_kgwrites(:Volk)-[:MADE]->(:Decision)-[:APPLIES_TO]->(:File)-[:JUSTIFIED_BY]->(:Requirement)(pipeline_kg.py:263, called via getattrpipeline_session.py:261). All best-effort/idempotent at runtime.
:Component: ⚠️ schema-only — constraintschema.py:127, read byquery_engine.py:201, but no writer anywhere (grep MERGE …:Component→ 0 outside schema). Never populated at runtime.- Code parsing (FR-006/007): ⚠️ DIVERGENCE —
code_parser.py:8 import ast(stdlib), zerotree_sitterimports. Files/Class/Function/Module/Package extracted (sync.pyproduces:Class ×8, :Function ×4, :Module ×6, :Package ×2). Mechanism differs from spec; capability present. (Confirms register FR-006/007.) - Context injection (FR-004): ✅
ContextInjectorwired inpipeline_kg.py:63; semantic enrichment via_query_semantic_context(context_injector.py:466,(sym)-[:TRACES_TO]->(:Requirement)+ decisions). - File watch sync (FR-008): ✅
FileWatcher(file_watcher.py:33) wired intosynchronizer.py:700-712.
- Schema (FR-001/002): ✅ all node labels present incl. the semantic layer —
- Remaining work / plan:
- Scope: (a) Update SPEC-008 FR-006/007 to record the deliberate stdlib-
astdivergence (no code work). (b) Decide whether:Componentis wanted — if so, add a writer (map ported package dirs →:ComponentwithPART_OFProject), else drop the label + constraint + query. Low value either way. - Target files:
specs/008-knowledge-graph/spec.md; if implementing,knowledge_graph/sync.py(add Component MERGE) orschema.py(remove). - Approach: The provenance/semantic layer the register flagged is done; this is now purely a spec-hygiene + one-label decision.
- Dependencies:
:Componentcouples to nothing live (query_engine reads it but no consumer depends on it). Tree-sitter divergence is self-contained. - Effort: S. Risk: low.
- Scope: (a) Update SPEC-008 FR-006/007 to record the deliberate stdlib-
- Evidence log:
refs sync_decisions_to_kg→ DEF only (0 AST uses) but grep shows getattr callpipeline_session.py:261(dynamic dispatch — AST refs miss it).grep -oE ':(Volk|Intent|PipelineRun|Artifact|Decision|Component)'(non-schema): writers inpipeline_kg.py:201-323,pipeline_session.py:233-252;:Componentappears only as reads inquery_engine.py:201.grep 'import ast|tree_sitter' code_parser.py→import ast, no tree-sitter.refs FileWatcher→ wired in synchronizer.
SPEC-009 Volk Context Injection — DIVERGENCE + ORPHANED (consolidation decision already made)
- Specifies:
VolkContextInjector.inject_for_task(volk_alias, task_type, task_params)— task-typed (BUILD/REFACTOR/DEBUG/DOCUMENT/TEST/REVIEW) KG queries returningVolkContextwithto_dict()/to_markdown(), importable from…knowledge.volk_context_injector. - Verified:
VolkContextInjector(FR-001): ⚠️ exists but ORPHANED —volk_context_injector.py:160;refs VolkContextInjector→ 0 imports, 0 uses outside tests. The named class + task-typed API +VolkContextdict/markdown are all implemented but never called in production.- Actual live delivery vehicle: ✅ different path — the file-based
ContextInjector(context_injector.py:213, wiredpipeline_kg.py:63) is what injects context, keyed on target files (not task type). It now also carries the spec/requirement layer SPEC-009 wanted via_query_semantic_context(requirements + prior decisions) — seedocs/context-injection.md§"two injectors". - So the spec's intent (inject spec/requirement context into volks) is met, but by the file-based injector, not the task-typed one the spec names. The task-typed axis (BUILD vs REFACTOR vs DEBUG param-driven queries) is not delivered.
- Remaining work / plan:
- Scope: This is a documented resolution (
context-injection.md: "enrich the one wired path rather than stand up a second competing injector"). Two options: (a) Keep-as-reference + update spec — rewrite SPEC-009 to state context injection is delivered byContextInjector's semantic layer; keepvolk_context_injector.pyas the future task-typed consumer (as the register's dead-code note already parks it). (b) Deletevolk_context_injector.pyif the task-typed axis is judged unwanted. (c) Wire it if task-typed injection is genuinely required — real work: a caller at volk-spawn that maps role→TaskType (the exact map the memory note "Two context injectors" warns not to blind-wire). - Target files:
specs/009-volk-context-injection/spec.md;knowledge_graph/volk_context_injector.py(delete or keep); if wiring:agents/kernel_handler.py/orchestrator/pipeline_runner.py. - Approach: Recommend (a) — cheapest, matches the recorded decision. Only pursue (c) if a task-typed consumer is actually specified.
- Dependencies: KG (SPEC-008) schema — both injectors read it. Task-typed wiring (c) couples to the goal/role system (SPEC-023) for the role→TaskType mapping and to the two-injector consolidation decision (still open per memory note).
- Effort: S (option a/b) / M (option c). Risk: low (a/b), med (c — role→TaskType is a design call, easy to get wrong).
- Scope: This is a documented resolution (
- Evidence log:
refs VolkContextInjector→ 1 def, 0 import, 0 use.refs ContextInjector→ wiredpipeline_kg.py:63/66.docs/context-injection.md:44-76documents the resolution.
SPEC-010 Memory Engine — PARTIAL (engine wired for recall; 3 register gaps confirmed still open)
- Specifies: DI-registered
IMemoryEngine(ComponentType.MEMORY_ENGINE), dual Redis-hot/Neo4j-cold storage, embedding search, summarize/budget, hot→cold migration on session close, PAM++ governance + audit on every op. - Verified:
- Interface + 8 ops (FR-002–005, 016, delete, migrate): ✅ all present —
IMemoryEngine(core/interfaces/memory.py:182), concreteMemoryEngine(memory_engine.py:56) implements store/retrieve/search/summarize/update/delete/ migrate_to_cold (memory_engine.py:139-296). Hot (hot_store.py), cold (cold_store.py), compressor, embedding, token_counter modules all present. - Engine now live for recall (W2, not in old register): ✅
register_memory/recall/unregister_memory(agents/volks/memory_context.py:35/75); bound per thread inagents/kernel_handler.py:125andorchestrator/pipeline_runner.py:231; consumed byagents/volks/base.py:182(recallfolded intograph_context). - GAP-1 FR-008 hot→cold migration: ❌
refs migrate_to_cold→ 0 uses.initialize()(memory_engine.py:105) subscribes to nothing; the spec'sevent_bus.subscribe("session.closed", …)is not implemented → migration never fires. (Confirms register.) - GAP-2 FR-001 DI resolution: ❌
refs IMemoryEngine→ 0 uses. A realDIContainerexists (di/container.py:98withregister/resolve) but nothing registers/resolves a memory engine —MemoryEngine()is constructed directly (gateway/handlers/memory.py:44,pipeline_runner.py:229). ComponentType.MEMORY_ENGINE is only referenced as the engine's own health metadata (memory_engine.py:99). - GAP-3 FR-013 PAM++ governance: ❌ zero memory predicates —
grep can_store_memory|can_read_memory|can_share_memory|can_delete_memory→ 0 files (.py and .pl). No ownership/permission check or audit in any memory op. - Minor: US-003 kernel injection wants
search+summarize(budget)→CXPmetadata["memory_context"]. The W2 recall path does search+prepend but not budget-awaresummarizeor CXP-metadata packing (summarizeatmemory_engine.py:232exists but is unused outside the engine).
- Interface + 8 ops (FR-002–005, 016, delete, migrate): ✅ all present —
- Remaining work / plan:
- Scope: (1) Subscribe
migrate_to_coldtosession.closedon a persistent engine (current engines are request/run-scoped so can't hold a long-lived subscription — needs a persistent-engine lifecycle decision). (2) Register+resolveIMemoryEnginevia the DIContainer and convert the two direct constructors. (3) Add PAM++ predicates (can_store/read/share/delete_memory) to the governance Prolog base + enforce+audit in each op. - Target files:
memory/memory_engine.py(initialize subscription, PAM calls),di/bootstrap (register),gateway/handlers/memory.py+pipeline_runner.py(resolve instead of construct),prolog/governance.pl(+ new predicates), session/event-bus wiring forsession.closed. - Approach: Do the persistent-engine + DI decision first (unblocks 1 & 2 together — a DI-singleton engine can both be resolved and hold the EventBus subscription), then layer PAM++ as an enforcement wrapper.
- Dependencies: DI (SPEC-024) for register/resolve; PAM/governance (SPEC-060)
for the Prolog predicate base + janus adapter; EventBus / session lifecycle
(SPEC-001/006) for
session.closeddelivery. This is the cluster's most cross-coupled item. - Effort: L (all three + the lifecycle decision). Risk: med — persistent-engine scope change touches gateway + pipeline; PAM predicates must run on janus (not pyswip) per project rule.
- Scope: (1) Subscribe
- Evidence log:
refs migrate_to_cold(0 uses),refs IMemoryEngine(0 uses),refs MemoryEngine(direct construction at gateway/pipeline),refs register_memory/recall(W2 wiring),grep MEMORY_ENGINE(enum + health only),grep can_*_memory(0).
SPEC-019 Intent Impact Analyzer — PARTIAL (classify+deps+plan wired; interface-verification & circular-dep unwired/absent)
- Specifies: ImpactAnalyzer → classification + dependency impact + test discovery + interface verification (breaking-change detection) + parallel planning, as pipeline step 2.5 in the intent broker.
- Verified:
- Classification (US-001, FR-001/002/003): ✅
IntentClassifier(classifier.py:114) — regex/keyword scoring, entity/package/module extraction, confidence=maxscore/5(classifier.py:190). Deterministic. ⚠️ caveat: keyword heuristic, not the ≥0.85-accuracy classifier the spec implies (quality NFR, not a structural gap). - Dependency analysis (US-002, FR-004/005): ✅
DependencyAnalyzerqueries Neo4j upstream/downstream/tests (dependency_analyzer.py:60-211). Queries:Package/:Module/ :HAS_MODULE/:HAS_FILE— which the KG does populate (sync.py:255/281), so the coupling is real, not dangling. - Test discovery (FR-006): ✅
_find_tests(dependency_analyzer.py:193). - Parallel planning (US-005, FR-010/011/012/013): ✅
ParallelPlanner.create_plan, branches, agent assignment (U:Cd/U:Qa/U:Ar), speedup, checkpoints (planner.py:33-291). - Pipeline wiring: ✅
IntentImpactAnalyzerwired into intent broker as step 2 (validate→impact→route), enabled by default —intent_broker.py:71-72,151-166; ImpactReport summary stashed inintent.parameters["_impact_report"]. - GAP-1 Interface verification (US-004, FR-007/008/009, ERR-007): ❌ no
InterfaceVerifierclass exists (symbols InterfaceVerifier→ 0). The dataclasses (InterfaceContract,ClassInterface,FunctionInterface,BreakingChange,InterfaceVerification) all exist intypes.py:107-193but have no producer.ImpactReport.interface_verification(types.py:266) is always None —analyze()never sets it.DependencyAnalyzer.find_interface(dependency_analyzer.py:214) extracts a contract but is never called byanalyze(). No breaking-change prediction / backward-compat verification anywhere → ERR-007 (block on breaking change) can never fire. - GAP-2 Circular-dependency detection (FR-015, ERR-006): ❌
find_circular_deps(dependency_analyzer.py:253) exists but is not invoked byanalyze()/find_affected_scope→ cycles never appear in the ImpactReport.
- Classification (US-001, FR-001/002/003): ✅
- Remaining work / plan:
- Scope: (1) Build an
InterfaceVerifier(extract current contract via the existingfind_interface, predictBreakingChanges from the classification/intent, produceInterfaceVerification), wire it intoanalyze()and populateImpactReport.interface_verification; enforce ERR-007 block in the intent broker. (2) Callfind_circular_depsinside_find_impact_scopeand surface cycles (severity CRITICAL per ERR-006). (3) Optional: replace/augment the keyword classifier if ≥0.85 accuracy is required. - Target files: new
orchestrator/impact/interface_verifier.py;orchestrator/impact/analyzer.py(wire verifier + circular deps);orchestrator/intent_broker.py(ERR-007 gate). - Approach: The types and the KG extraction (
find_interface) already exist — this is assembly + wiring, not greenfield. Breaking-change prediction from intent text is the genuinely hard part (needs a diff model or a heuristic on the classification entities); scope it as heuristic first. - Dependencies: KG (SPEC-008) for interface/circular-dep queries (already populated); agents (U:Cd/U:Qa/U:Ar aliases). Independent of memory/DI/PAM.
- Effort: M. Risk: med — breaking-change prediction accuracy (FR-008 says ≥95%) is hard from intent text alone; deliver the wiring + a conservative heuristic, flag the accuracy target as aspirational.
- Minor: most methods carry placeholder
@traces(spec="SPEC-100", reqs=["REQ-100-001"])rather than SPEC-019 reqs (classifier.py:121+,analyzer.py:36+,types.py), so RTM coverage for SPEC-019 requirements is understated — a traceability-hygiene fix.
- Scope: (1) Build an
- Evidence log:
symbols InterfaceVerifier(0),refs BreakingChange(only a self-use in types.py, no producer),refs IntentImpactAnalyzer(wiredintent_broker.py:71), read ofanalyzer.py(analyze() = classify→scope→plan only, no interface/circular step),refs ParallelPlanner(used by analyzer),grep :Package|:Module writers→sync.py:255/281.
SPEC-026 Compliance Volk (U:Cp) — DONE
- Specifies: Deterministic
ComplianceVolk(U:Cp) running a 7-step pipeline after U:Qa: scan@traces→ load requirements from KG → generate RTM → sync code → check compliance → persist RTM (disk+KG) → produce report; graceful KG degradation; zero LLM. - Verified:
ComplianceVolk(compliance/volk.py:51): ✅ wired into the live pipeline —refs ComplianceVolk→ imported + usedpipeline.py:156.- 7-step pipeline (FR-002/003): ✅ all pure functions present in
_pipeline.py—scan_traces:40,load_requirements:79,generate_rtm:133,sync_code:175,check_compliance:208,persist_rtm:247(+_calculate_results). - Lazy KG init + graceful degradation (FR-033/034, ERR-001): ✅ per
volk.py(get_kg_store→KGDesignStore, non-fatal on failure). - Determinism (NFR-006): ✅ zero LLM calls in the compliance path.
- Register verdict: clean. CONFIRMED — no gaps found this pass.
- Remaining work / plan: none. (Minor signature drift vs spec text —
load_requirements(kg_store, task_summary)vs spec'sload_requirements(kg_store)— is functional, not a gap.) - Evidence log:
refs ComplianceVolk(wiredpipeline.py:156),symbols scan_traces|load_requirements|generate_rtm|sync_code|check_compliance|persist_rtm→ all inagents/volks/compliance/_pipeline.py.
Cluster summary table
| Spec | Class | Effort | Hard deps |
|---|---|---|---|
| SPEC-008 Knowledge Graph | DIVERGENCE (+ tiny :Component gap) |
S | none live (self-contained) |
| SPEC-009 Volk Context Injection | DIVERGENCE + ORPHANED | S (a/b) · M (c) | KG(008); role→TaskType wiring couples to goal-system(023) |
| SPEC-010 Memory Engine | PARTIAL | L | DI(024) + PAM/governance(060) + EventBus/session(001/006) |
| SPEC-019 Intent Impact Analyzer | PARTIAL | M | KG(008) [populated]; agents(U:Cd/Qa/Ar) |
| SPEC-026 Compliance Volk | DONE | — | — |
Real remaining work (this cluster)
- SPEC-010 — the substantive one: hot→cold migration wiring, DI registration, PAM++ memory governance (all confirmed still open; needs a persistent-engine lifecycle decision).
- SPEC-019 — build+wire
InterfaceVerifier(FR-007/008/009) and wire the existingfind_circular_deps(FR-015); both currently unwired/absent though the types + KG queries exist. - SPEC-008 / SPEC-009 — mostly spec-hygiene: record the tree-sitter divergence, decide
:Componentkeep/drop, and formalize the two-injector resolution +volk_context_injector.pykeep/delete. Small. - SPEC-026 — DONE, no work.
Cross-cluster dependencies noticed
- memory (010) ↔ DI (024) ↔ PAM (060) — the DIContainer (
di/container.py) and the Prolog governance base (must run on janus, not pyswip) are both prerequisites for SPEC-010's DI-resolution and PAM++ gaps. migrate_to_cold additionally needs the EventBus/session.closed lifecycle (SPEC-001/006) — a persistent memory engine is the common unblock for DI + migration. - context-injection (009) ↔ KG (008) ↔ goal-system (023) — both injectors read the KG; the documented "session/goal/project state never injected" gap and any task-typed (role→TaskType) wiring couple to the SPEC-023 goal/role system. Do not blind-wire a role→TaskType map (per the standing memory note on the two orphaned injectors).
- impact-analyzer (019) ↔ KG (008) — DependencyAnalyzer depends on
:Package/:Modulenodes, which the KG sync does populate (sync.py:255/281); no cross-cluster blocker there.