Context
This ticket comes from operator-directed debt reduction after the v13.1 PR review queue cleared. The named surface is ai/services/graph/TopologyInferenceEngine.mjs, which sits in the Dream Pipeline graph-service family next to SemanticGraphExtractor.mjs.
The immediate friction is not a new feature request. It is a source-quality and service-boundary cleanup so future agents can understand and safely modify REM topology extraction without re-deriving old decisions.
Live latest-open sweep: checked latest 20 open issues at 2026-06-27T23:29:26Z; no equivalent TopologyInferenceEngine cleanup ticket found. Recent A2A in-flight claim sweep: checked latest 30 all-status messages; no competing [lane-claim] / [lane-intent] for this scope found.
Prior-art sweep:
- #12073 covered hierarchical chunking for Tri-Vector / REM extraction and is closed. It does not own this source-boundary cleanup.
- #12065 covered the broader Orchestrator-as-SSOT REM pipeline epic and is closed. It names topology failure semantics, but not this local service documentation/refactor debt.
- #13663 covered handoff path test isolation and is closed. It does not own helper placement or Anchor & Echo documentation here.
learn/agentos/KnowledgeBaseEnhancement.md is the current source-doc strategy: class-level Anchor & Echo documentation should make intent, invariants, and local boundary language retrievable by both humans and the Knowledge Base.
The Problem
TopologyInferenceEngine.mjs is a load-bearing REM graph consumer, but it is harder to audit than its sibling SemanticGraphExtractor.mjs:
- The class-level JSDoc is only
@class / @extends / @singleton; it does not anchor the service's role in Phase 2 topological-conflict inference, ConsumerFriction emission, or handoff writes.
- Several module-scope helpers (
estimateTopologyTokens, assertRequiredChatNumber, assertRequiredChatString, getCompletionFinishReason, isLengthTruncatedCompletion) sit outside the service even though they encode service-local semantics. This makes the file read like a bag of private functions instead of a Neo service with documented internal boundaries.
- The custom
assertRequiredChatNumber/String(value, configPath) helpers defensively validate AiConfig leaves after values are already threaded into them. ADR 0019 says AiConfig is the Provider SSOT and consumers should read resolved leaves at the use site without re-implementing or defending against the SSOT. This needs to be corrected or explicitly justified in a local Anchor & Echo block if any validation remains genuinely service-local rather than config re-resolution.
- The
getTopologyConflictCount() prose still says extractTopology() returns undefined for no-conflicts and provider-error paths, while the current method returns a summary object for the no-conflicts path. That stale note makes the observability story look deprecated/conflicting even though the runtime code changed.
The Architectural Reality
Current source anchors on dev at ticket creation:
ai/services/graph/TopologyInferenceEngine.mjs:40-74 defines five module-scope helpers before the class.
ai/services/graph/TopologyInferenceEngine.mjs:77-82 has the thin class JSDoc with no @summary Anchor & Echo intent surface.
ai/services/graph/TopologyInferenceEngine.mjs:213-251 reads aiConfig.localModels.chat leaves and passes them into local assertion helpers.
ai/services/graph/TopologyInferenceEngine.mjs:264-489 owns the provider invocation loop, ConsumerFriction emissions, parse-failure handling, and sandman_handoff.md write path.
ai/services/graph/TopologyInferenceEngine.mjs:491-517 documents Axis D counting but carries a stale divergence note about extractTopology() returning undefined on the no-conflicts path.
ai/services/graph/SemanticGraphExtractor.mjs:40-180 is the nearby pattern: helper logic is documented as service methods with @summary Anchor & Echo, and provider construction reads resolved AiConfig leaves at the use site.
The Fix
Tighten TopologyInferenceEngine.mjs without changing REM behavior:
- Add a class-level Anchor & Echo JSDoc summary that names the durable service role: turn-aligned topology-conflict inference, bounded provider output, ConsumerFriction emission, and Sandman handoff alert writes.
- Move service-local module helpers into documented internal/protected methods where that improves ownership clarity. Keep true constants module-local.
- Replace or reshape the AiConfig assertion helpers so they comply with ADR 0019. The preferred outcome is direct resolved-leaf reads at the use site with no custom config-leaf resolver/defensive SSOT layer.
- Update stale
getTopologyConflictCount() documentation so it matches current extractTopology() return behavior and still documents the remaining provider-error / caught-error ambiguity accurately.
- Keep the change source-only unless a helper relocation creates a testable behavior boundary; do not alter prompt text, chunking behavior, friction payload shape, or handoff write semantics in this debt slice.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
TopologyInferenceEngine class JSDoc |
learn/agentos/KnowledgeBaseEnhancement.md; sibling SemanticGraphExtractor.mjs pattern |
Add Anchor & Echo intent and invariants for REM topology-conflict inference |
If wording risks over-claiming behavior, keep it to source-verified responsibilities only |
Class JSDoc |
Source diff plus syntax check |
| Internal topology helper methods |
Existing TopologyInferenceEngine.mjs helpers |
Service-owned helper semantics are documented inside the class when they are not shared module primitives |
Keep constants module-local; do not add new files for this cleanup |
Method JSDoc @summary |
Source diff; targeted unit/syntax checks |
| AiConfig leaf reads |
ADR 0019 |
Read resolved AiConfig leaves at use sites without local resolver/pass-through defense |
Loud runtime failure is acceptable if the SSOT is malformed; no hidden defaults |
Inline Anchor & Echo only if validation remains |
ADR 0019 review; source diff |
getTopologyConflictCount() docs |
Current extractTopology() implementation |
Stale no-conflicts/undefined prose is corrected to current return semantics |
Preserve provider-error ambiguity note if still true |
Method JSDoc |
Source diff; optional existing REM state tests |
Decision Record Impact
Aligned with ADR 0019. This ticket should remove or narrow a local config-defensive pattern; it does not amend ADR 0019.
Acceptance Criteria
Out of Scope
- Changing topology prompt wording or model behavior.
- Raising graph output token limits or context-window policy.
- Adding a new MCP tool.
- Reopening #12065 or #12073.
- Broadly refactoring all graph services. This is one debt-rich file, scoped to one PR.
Avoided Traps
- Do not turn documentation cleanup into a runtime rewrite.
- Do not hide old behavior behind
@deprecated; either remove stale methods/prose or make the current contract explicit.
- Do not add config defaults, env reads, or fallback math outside AiConfig.
- Do not block this on Contract Ledger perfection. The ledger here is a planning aid; the implementation evidence is source truth and targeted validation.
Related
- #12065 - closed REM pipeline epic, prior topology-state context.
- #12073 - closed chunking-aware REM extraction sub.
- #13663 - closed handoff path test-isolation slice.
- ADR 0019 - AiConfig Provider SSOT.
learn/agentos/KnowledgeBaseEnhancement.md - Anchor & Echo strategy.
Retrieval Hint: TopologyInferenceEngine Anchor Echo ADR 0019 service helper docs getTopologyConflictCount stale divergence note
Context
This ticket comes from operator-directed debt reduction after the v13.1 PR review queue cleared. The named surface is
ai/services/graph/TopologyInferenceEngine.mjs, which sits in the Dream Pipeline graph-service family next toSemanticGraphExtractor.mjs.The immediate friction is not a new feature request. It is a source-quality and service-boundary cleanup so future agents can understand and safely modify REM topology extraction without re-deriving old decisions.
Live latest-open sweep: checked latest 20 open issues at 2026-06-27T23:29:26Z; no equivalent TopologyInferenceEngine cleanup ticket found. Recent A2A in-flight claim sweep: checked latest 30 all-status messages; no competing
[lane-claim]/[lane-intent]for this scope found.Prior-art sweep:
learn/agentos/KnowledgeBaseEnhancement.mdis the current source-doc strategy: class-level Anchor & Echo documentation should make intent, invariants, and local boundary language retrievable by both humans and the Knowledge Base.The Problem
TopologyInferenceEngine.mjsis a load-bearing REM graph consumer, but it is harder to audit than its siblingSemanticGraphExtractor.mjs:@class/@extends/@singleton; it does not anchor the service's role in Phase 2 topological-conflict inference, ConsumerFriction emission, or handoff writes.estimateTopologyTokens,assertRequiredChatNumber,assertRequiredChatString,getCompletionFinishReason,isLengthTruncatedCompletion) sit outside the service even though they encode service-local semantics. This makes the file read like a bag of private functions instead of a Neo service with documented internal boundaries.assertRequiredChatNumber/String(value, configPath)helpers defensively validate AiConfig leaves after values are already threaded into them. ADR 0019 says AiConfig is the Provider SSOT and consumers should read resolved leaves at the use site without re-implementing or defending against the SSOT. This needs to be corrected or explicitly justified in a local Anchor & Echo block if any validation remains genuinely service-local rather than config re-resolution.getTopologyConflictCount()prose still saysextractTopology()returnsundefinedfor no-conflicts and provider-error paths, while the current method returns a summary object for the no-conflicts path. That stale note makes the observability story look deprecated/conflicting even though the runtime code changed.The Architectural Reality
Current source anchors on
devat ticket creation:ai/services/graph/TopologyInferenceEngine.mjs:40-74defines five module-scope helpers before the class.ai/services/graph/TopologyInferenceEngine.mjs:77-82has the thin class JSDoc with no@summaryAnchor & Echo intent surface.ai/services/graph/TopologyInferenceEngine.mjs:213-251readsaiConfig.localModels.chatleaves and passes them into local assertion helpers.ai/services/graph/TopologyInferenceEngine.mjs:264-489owns the provider invocation loop, ConsumerFriction emissions, parse-failure handling, andsandman_handoff.mdwrite path.ai/services/graph/TopologyInferenceEngine.mjs:491-517documents Axis D counting but carries a stale divergence note aboutextractTopology()returningundefinedon the no-conflicts path.ai/services/graph/SemanticGraphExtractor.mjs:40-180is the nearby pattern: helper logic is documented as service methods with@summary Anchor & Echo, and provider construction reads resolved AiConfig leaves at the use site.The Fix
Tighten
TopologyInferenceEngine.mjswithout changing REM behavior:getTopologyConflictCount()documentation so it matches currentextractTopology()return behavior and still documents the remaining provider-error / caught-error ambiguity accurately.Contract Ledger Matrix
TopologyInferenceEngineclass JSDoclearn/agentos/KnowledgeBaseEnhancement.md; siblingSemanticGraphExtractor.mjspatternTopologyInferenceEngine.mjshelpers@summarygetTopologyConflictCount()docsextractTopology()implementationDecision Record Impact
Aligned with ADR 0019. This ticket should remove or narrow a local config-defensive pattern; it does not amend ADR 0019.
Acceptance Criteria
TopologyInferenceEnginehas class-level Anchor & Echo documentation that explains its Dream Pipeline role and durable invariants without ticket archaeology.getTopologyConflictCount()documentation matches currentextractTopology()behavior.node --check ai/services/graph/TopologyInferenceEngine.mjs; if helper movement affects tests, run the nearest graph/Dream unit tests instead of default Playwright.Out of Scope
Avoided Traps
@deprecated; either remove stale methods/prose or make the current contract explicit.Related
learn/agentos/KnowledgeBaseEnhancement.md- Anchor & Echo strategy.Retrieval Hint:
TopologyInferenceEngine Anchor Echo ADR 0019 service helper docs getTopologyConflictCount stale divergence note