Orchestrata Ultimate › Plans › context-injection-phase2-plan
Phase 2 KG Enhancements — Comprehensive Execution Plan
Source: plans/context-injection-phase2-plan.md at 8a2cb42ad · last changed 2026-07-27 · first committed 2026-07-14 · 323 lines.
Prose pending.
Plan text
Phase 2 KG Enhancements — Comprehensive Execution Plan
Continuation of plans/context-injection-tracked-plan.md. This plan closes the seven pending Phase 2 items (2.2–2.8) that enrich the graph context injected into volk prompts and the feedback loop around it.
Status: planning
Target completion: one commit per numbered task, tests + health_check.py green before moving on.
0. Principles and constraints
- Real infrastructure only. Every test must exercise real Neo4j/Memgraph or degrade gracefully; no mocks, stubs, or
monkeypatch. - No schema guesswork. New node/edge types or property shapes must be added to
knowledge_graph/canonical_schema.py(identity + relationships) andknowledge_graph/schema.py(constraints/indexes) before any writer/query uses them. - Project-scoped. All new KG queries respect
project_idwhen theContextInjectorwas configured with one; default behaviour remains project-agnostic. - Backward compatibility. Existing
graph_contextprompt shape must not break; new sections are additive. - No
pytest-xdist. Usescripts/run_tests.pyor the streaming runner. - Traceability. New public functions/classes get
@traceswith real REQ ids from the spec they implement (SPEC-008 / SPEC-106 / SPEC-107).
1. Proposed execution order
| Order | Task | Why this order |
|---|---|---|
| 2.2 | Recent-changes section | Self-contained; no dependencies on other Phase 2 items. |
| 2.3 | Similar-patterns section | Self-contained; can be done in parallel with 2.2. |
| 2.5 | Semantic-search bridge | Reusable bridge component; later items can call it. |
| 2.8 | Unify Requirement/RequirementItem |
Cleans the data model used by 2.4 and 2.6. |
| 2.4 | Requirement closure | Builds on unified requirement identity. |
| 2.7 | Runtime Tier-H ingestion | Wires existing RuntimeSignalWriter into live pipeline paths. |
| 2.6 | Validator feedback loop | Touches the LLM call path; do last so the richer context from 2.2–2.5 is available to retry with. |
2. Task 2.2 — Recent-changes section
Current state
GraphContext.recent_changesexists as a field but is never populated.ContextInjector._query_contextqueries files, relationships, tests, and semantic context, but skips recent changes.- Neo4j already stores
Filenodes; KG sync writesMODIFIED/CHANGED_IN/NEXT_VERSIONrelationships in some flows, but they are not read for context.
Goal
Populate GraphContext.recent_changes with the most recent modifications to the target files and their direct neighbours, then render them in GraphContext.to_prompt().
Design
- Add a
_query_recent_changes(file_paths, context)helper toContextInjector. - Query options (try in order, stop when results found):
a.
(:File)-[:CHANGED_IN]->(:Change {timestamp, description})if Change nodes exist. b.(:File)-[:NEXT_VERSION]->(:File)timestamp diff. c. Fallback: use the file node’s ownupdated_at/synced_atproperty. - Sort by timestamp desc, cap at 5 entries.
- Each entry:
{"file": path, "change": description, "timestamp": iso, "author": "..."}. - Render in
_format_recent_changes_section()(already exists; just needs data).
Files to touch
src/orchestrata_ultimate/knowledge_graph/context_injector.py_query_context: call_query_recent_changes.- new
_query_recent_changes.
src/orchestrata_ultimate/knowledge_graph/canonical_schema.py- Add
LABEL_CHANGE = "Change"andREL_CHANGED_IN/REL_NEXT_VERSIONif missing from canonical vocabulary.
- Add
src/orchestrata_ultimate/knowledge_graph/schema.py- Add constraint/index for
Change.id/Change.timestampif using Change nodes.
- Add constraint/index for
Acceptance criteria
ContextInjector.inject()returns agraph_contextstring containing## Recent Changeswhen target files have recent modifications.- Empty result omits the section (no "Recent Changes" heading with no bullets).
- Project-scoped queries do not leak changes from other projects.
- Degrades gracefully when no timestamp/change data exists.
Tests
tests/context/providers/test_graph_context.py(new) or extendtests/context/test_assembler.py:- Seed Neo4j with
File+Changenodes, callContextInjector, assert recent changes appear. - Test project-scoped filtering.
- Test graceful empty result.
- Seed Neo4j with
3. Task 2.3 — Similar-patterns section
Current state
GraphContext.similar_patternsexists as a field but is never populated.- No query currently finds files with similar interfaces (shared bases, similar method sets, same protocol implementation).
Goal
Populate GraphContext.similar_patterns with files/classes that implement the same protocols or inherit from the same bases as the target files, so volks know where to look for analogous code.
Design
- Add
_query_similar_patterns(file_paths, context)helper. - For each target file, find its classes and their base classes / implemented protocols.
- Query Neo4j for other
Filenodes whose classes share at least one base/protocol. - Rank by number of shared bases and then by path similarity.
- Cap at 5 files.
- Render as a new
## Similar Patternssection into_prompt().
Files to touch
src/orchestrata_ultimate/knowledge_graph/context_injector.py_query_context: call_query_similar_patterns.- new
_query_similar_patterns. _format_similar_patterns_section.
src/orchestrata_ultimate/knowledge_graph/canonical_schema.py- Ensure
REL_IMPLEMENTS/REL_INHERITSare in canonical vocabulary.
- Ensure
Acceptance criteria
graph_contextcontains## Similar Patternslisting files/classes that share bases/protocols with target files.- Does not include the target files themselves.
- Project-scoped.
- Empty result omits the section.
Tests
- Seed Neo4j with two files implementing the same protocol; assert the second appears as similar pattern for the first.
- Test exclusion of self and project scoping.
4. Task 2.5 — Semantic-search bridge
Current state
context/providers/semantic_context.pyalready performs the bridge: it queries Memgraph chunks, maps chunkfile_pathback to Neo4jFileroots, and renders a markdown block.- However, the bridge is only used as the
semanticlayer ofUnifiedContextAssembler; it is not exposed as a reusable component forContextInjectororGraphContext.
Goal
Extract the Memgraph-chunk → Neo4j-root logic into a reusable bridge and optionally make it available to ContextInjector so GraphContext can include semantic-search results.
Design
- Create
src/orchestrata_ultimate/knowledge_graph/semantic_bridge.pywith:search_semantic_roots(query: str, target_files: list[str], *, project_id, config, limit) -> list[dict[str, Any]]- Returns Neo4j
Filerecords + matching chunk snippets.
- Refactor
context/providers/semantic_context.pyto import and usesemantic_bridge.search_semantic_roots. - Optionally add
_query_semantic_bridge(file_paths, context)inContextInjectorto append semantic roots tocontext.similar_patternsor a newcontext.semantic_filesfield.
Files to touch
- New:
src/orchestrata_ultimate/knowledge_graph/semantic_bridge.py src/orchestrata_ultimate/context/providers/semantic_context.py(refactor)src/orchestrata_ultimate/knowledge_graph/context_injector.py(optional integration)
Acceptance criteria
semantic_bridge.search_semantic_rootshas dedicated unit tests.semantic_context.render_semantic_contextstill passes existing tests with no functional regression.- Bridge degrades to empty list when Memgraph or Neo4j is unreachable.
Tests
tests/knowledge_graph/test_semantic_bridge.py(new):- Seed Memgraph with chunks for two files and Neo4j with
Filenodes; assert bridge returns correct roots. - Test unreachable backends return empty list.
- Seed Memgraph with chunks for two files and Neo4j with
5. Task 2.8 — Unify Requirement/RequirementItem
Current state
session/kg_sync/_stage_specs_traces.pywrites(:Spec)-[:DEFINES_REQUIREMENT]->(:Requirement)-[:HAS_ITEM]->(:RequirementItem).- Code symbols trace to
RequirementItemviaTRACES_TO. ContextInjector._query_semantic_contextalready walksHAS_ITEMto surface the parentRequirement.- However, queries coalesce divergent id/description keys (
r.id,r.req_id,r.text,r.description) and there is no single helper for requirement identity.
Goal
Create a canonical requirement-resolution helper so every KG query treats Requirement and RequirementItem as one logical requirement chain.
Design
- Add
knowledge_graph/requirement_resolution.py:resolve_requirement_chain(ri_ids: list[str], repo, project_id) -> list[dict]— returns merged Requirement+Item records with stableid,description,specs.
- Update
ContextInjector._query_semantic_contextto use the helper. - Update
context_pack/relevance.pyparent-requirement query to use the same helper (or at least the same canonical keys). - Add a
RequirementChaindataclass to avoid dict drift.
Files to touch
- New:
src/orchestrata_ultimate/knowledge_graph/requirement_resolution.py src/orchestrata_ultimate/knowledge_graph/context_injector.pysrc/orchestrata_ultimate/knowledge_graph/context_pack/relevance.pysrc/orchestrata_ultimate/tools/context_tools.py(uses requirement queries)
Acceptance criteria
- A symbol tracing to either a
RequirementItemor its parentRequirementyields the same resolved requirement record. - All requirement-related Cypher queries use canonical keys from
canonical_schema.py. - Existing requirement-context tests still pass.
Tests
tests/knowledge_graph/test_requirement_resolution.py(new):- Seed Requirement + RequirementItem + traced symbol; assert chain resolution.
- Test item-only and requirement-only edge cases.
6. Task 2.4 — Requirement closure
Current state
ContextInjector._query_semantic_contextsurfaces directTRACES_TOrequirements for target symbols.- It does not walk the requirement hierarchy (e.g. parent requirements, sub-requirements, or requirements linked via
REQUIRES/DEPENDS_ON).
Goal
Walk the TRACES_TO / HAS_ITEM / REQUIRES hierarchy so volks see not only the exact requirement a symbol traces to, but also its siblings, parents, and dependencies.
Design
- Extend
requirement_resolution.pywith:closure_requirement_chain(seed_ids, repo, project_id, max_depth=2) -> list[dict]
- Use the closure in
ContextInjector._query_semantic_context. - Cap total returned requirements at 20 to avoid prompt bloat.
- Mark each requirement in the rendered output with its distance from the seed (
direct,parent,dependency).
Files to touch
src/orchestrata_ultimate/knowledge_graph/requirement_resolution.pysrc/orchestrata_ultimate/knowledge_graph/context_injector.pysrc/orchestrata_ultimate/knowledge_graph/canonical_schema.py— addREL_REQUIRESif not already canonical.
Acceptance criteria
graph_contextincludes parent/dependent requirements when they exist.- No duplicates from multiple traversal paths.
- Project-scoped and depth-limited.
Tests
- Seed a requirement hierarchy; assert closure includes parent and child requirements.
- Test depth limit and deduplication.
7. Task 2.7 — Runtime Tier-H ingestion
Current state
knowledge_graph/runtime_writer.pyexists withwrite_message,write_gateway_event,write_decision,write_tool_call.PipelineKGMixininstantiatesRuntimeSignalWriterand callswrite_gateway_eventonce per context injection.- Messages, decisions made during a run, and tool calls are NOT written as Tier-H signals.
context_pack/relevance.pyalready readsMENTIONSedges from any signal node within the recent window for Prolog relevance.
Goal
Wire runtime signal writing into the live pipeline so messages, decisions, and tool calls become queryable context for DEBUG/retry scenarios.
Design
- Messages: In
orchestrator/pipeline_runner.pyandkernel/pipeline.py, after a CXP packet is dispatched/received, callruntime_writer.write_message()with sender/recipient/subject and any file/entity mentions extracted from the payload. - Decisions:
PipelineKGMixin.sync_decisions_to_kgalready writes durableDecisionnodes; also callruntime_writer.write_decision()(or unify the two) so the decision appears as a Tier-H signal. - Tool calls: Add a hook in
BaseAgent._call_llm_with_tools/ tool execution to callruntime_writer.write_tool_call()with the tool name, arguments, result, and mentioned files/entities. - Entity extraction helper: Add
knowledge_graph/signal_extraction.pyto extract file paths and code entity ids from a payload/text using regex and the AST index, reusable by all three writers. - Ensure writes are best-effort and never raise.
Files to touch
- New:
src/orchestrata_ultimate/knowledge_graph/signal_extraction.py src/orchestrata_ultimate/kernel/pipeline_kg.pysrc/orchestrata_ultimate/kernel/pipeline.pysrc/orchestrata_ultimate/orchestrator/pipeline_runner.pysrc/orchestrata_ultimate/agents/volks/base.py
Acceptance criteria
- After a pipeline stage exchange, Neo4j contains a
Messagenode withMENTIONSedges to relevant files. - After a tool call, a
ToolCallsignal node exists. KgRelevanceRetrieverboosts nodes mentioned by these signals within the recent window.- Failures are logged, not raised.
Tests
tests/knowledge_graph/test_runtime_signals.py(new):- Run a minimal pipeline stage and assert Message/ToolCall nodes exist in Neo4j.
- Test relevance boost with a recent signal.
8. Task 2.6 — Validator feedback loop
Current state
GraphContextValidatorchecks that LLM responses reference injected entities and parses an "Entities used" citation block.- It can identify missing core citations.
- There is no automated retry/re-pack path: callers would have to inspect
ValidationResultmanually.
Goal
When validation fails because core entities are missing from the response, automatically expand the context (larger k-hop, include TESTED_BY / TRACES_TO neighbours) and retry the LLM call, up to a limit.
Design
- Add
knowledge_graph/context_validator_loop.py:ValidatorLoop(injector, validator, max_retries=2)run(payload, call_llm, *, task_type)calls LLM, validates, and on missing citations expands context viainjector.inject(..., expansion=...)and retries.
- Add an
expansion: ContextExpansionparameter toContextInjector.inject/inject_for_task:k_hopincrease- include
TESTED_BYneighbours - include requirement closure
- Wire the loop into
BaseAgent._call_llmas an opt-in path for subjects inINJECT_SUBJECTS. - Keep the default
ContextInjector.inject()unchanged so existing callers are unaffected.
Files to touch
- New:
src/orchestrata_ultimate/knowledge_graph/context_validator_loop.py src/orchestrata_ultimate/knowledge_graph/context_injector.pysrc/orchestrata_ultimate/knowledge_graph/graph_context_validator.py(minor: exposemissing_core_citations)src/orchestrata_ultimate/agents/volks/base.py
Acceptance criteria
- A response missing a core entity triggers a retry with expanded context.
- After retry, the validator passes or max retries is exhausted.
- Each retry logs the expansion applied.
- No infinite loops; expansion is bounded.
Tests
tests/knowledge_graph/test_context_validator_loop.py(new):- Use a fake LLM that first omits a required entity, then includes it; assert loop retries once.
- Test max-retries exhaustion.
9. Cross-cutting work
Schema
- Any new node label or relationship added during these tasks must land in
canonical_schema.pyandschema.pyfirst. - Run
python scripts/neo4j_test_preflight.pyafter schema changes.
Performance
- All new Cypher queries must have
LIMITclauses and use indexed properties (path,id,name,timestamp). - Avoid cross-product explosions when traversing from files → classes → requirements.
Documentation
- Update
docs/context-injection.mdas each section goes live. - Update
plans/context-injection-tracked-plan.mdstatus/commit column per task. - Do not hand-edit
docs/rtm.mdordocs/rtm.json; they are regenerated.
Verification per task
uv run pytest tests/<relevant>/ -qpasses.python scripts/health_check.pypasses.uv run ast-syncregenerates AST files if source layout changed.git add -A && git commit -m "P2.X ..." && git push origin master.
10. Definition of done for Phase 2
- [ ] Tasks 2.2–2.8 are all
[x]inplans/context-injection-tracked-plan.md. - [ ] Each task has an observed test covering real Neo4j behaviour.
- [ ]
python scripts/health_check.pyis green after the final commit. - [ ]
docs/context-injection.mdreflects all new context layers. - [ ] No new orphan modules (Rule 8 ratchet passes).