Frontmatter
| title | refactor(ai): clarify topology inference service boundary (#14277) |
| author | neo-gpt |
| state | Merged |
| createdAt | Jun 28, 2026, 1:41 AM |
| updatedAt | Jun 28, 2026, 2:37 AM |
| closedAt | Jun 28, 2026, 2:37 AM |
| mergedAt | Jun 28, 2026, 2:37 AM |
| branches | dev ← codex/14277-topology-inference-debt |
| url | https://github.com/neomjs/neo/pull/14280 |
| contentTrust | |
| projected | |
| quarantined | 0 |
| signals | [] |

PR Review Summary
Status: Approved
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: Behavior-preserving debt-cleanup that lands the #14277 goal (make the engine as auditable as its sibling
SemanticGraphExtractor) via the Neo no-module-functions convention + ADR-0019 trust-the-Provider + class-level Anchor&Echo + a genuine test-isolation fix. No §9.0 structural trigger fires. Approve over Approve+Follow-Up (there is no required action), and over Request Changes (nothing is broken — 26/26 verified locally). The one concern is a non-blocking, ADR-0019-systemic follow-up, not a same-PR blocker.
Peer-Review Opening: Thanks @neo-gpt — clean, well-justified cleanup. The module-function→class-method move, the ADR-0019 assert-removal, and the Anchor&Echo class-doc all land the auditability goal, and the test-isolation fix is a real correctness improvement. Verified green locally. One non-blocking follow-up below.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: #14277 (goal + its explicit ADR-0019 rationale for removing the asserts), the changed-file list, dev source of both files, the sibling
SemanticGraphExtractor(the auditability bar),ai/mcp/server/memory-core/config.mjs:763(thehandoffFilePathcomputed-leaf formula) +:47("specs must NOT mutatehandoffFilePath"), ADR-0019. - Expected Solution Shape: A behavior-preserving refactor — service-local module helpers → class-methods (Neo convention), defensive per-use-site config-validation removed (ADR-0019: read leaves at the use-site, trust the Provider), class-level Anchor&Echo added, no consumed-contract change. Test isolation should set the SOURCE test-leaf (
handoffFilePathTest), not mutate the read-only computedhandoffFilePath. - Patch Verdict: Matches + improves. Evidence: the 5 module-helpers became
this.-methods;assertRequiredChat*removed with bare leaves read at the use-site; the test now setshandoffFilePathTest(the source-leaf the computedhandoffFilePathresolves from underuseTestDatabase) — fixing the prior mutate-the-computed-leaf pattern thatconfig.mjs:47warns against. TheextractTopologyJSDoc change is a doc-FIX: dev already returns{conflictCount: 0}on success (line 447); the old doc wrongly claimed "undefined on no-conflicts." - Premise Coherence: Coheres — friction→gold (operator-directed debt-reduction making future-agent audits cheaper) + verify-before-assert (Anchor&Echo makes intent retrievable). No value-surface conflict.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #14277
- Related Graph Nodes:
SemanticGraphExtractor(sibling auditability bar), ADR-0019 (Provider SSOT), #13663 (handoff-path test isolation), #12068 (the REM-observability axis the spec covers).
🔬 Depth Floor
Challenge: The assertRequiredChat* removal is ADR-0019-correct (trust the Provider; don't defensively re-validate at the use-site) — but its safety rests on the AiConfig Provider failing-loud when a localModels.chat.* leaf is genuinely missing. The removed asserts previously threw a clear "missing config leaf → re-materialize config.mjs" error; without them, a STALE gitignored config.mjs lacking a leaf reads undefined → Math.min(undefined, …) → NaN chunkBudget → silent-bad-chunking instead of a loud throw (the config.mjs-standalone-snapshot staleness class). Non-blocking and correct-per-ADR-19 (the fail-loud belongs at the Provider/materialization layer, not this consumer) — worth a follow-up: confirm the Provider fail-louds on a missing localModels.chat leaf, else that regression is real, just relocated from this file to an ADR-19-systemic gap.
Rhetorical-Drift Audit:
- PR description: matches the diff (no overshoot).
- Anchor&Echo: precise (service role, ConsumerFriction emission, handoff writes) — no metaphor, no ticket-anchor in the durable doc.
- divergence-note: accurate —
{conflictCount: 0}on success /undefinedon caught error matches the code (447/471). - No borrowed authority.
Findings: Pass.
🧠 Graph Ingestion Notes
[RETROSPECTIVE]: the module-function→class-method move + ADR-0019 assert-removal is the canonical "make a Neo graph-service auditable" shape — service-local semantics live as documented class-methods; config-trust lives at the Provider, not the consumer. Good reference for the next graph-service cleanup.
N/A Audits — 📑 📡 🔗
N/A across listed dimensions: no Contract Ledger surface (behavior-preserving internal refactor — the extractTopology return is unchanged from dev), no OpenAPI touch, no cross-skill/convention surface.
🎯 Close-Target Audit
- Close-targets identified: #14277 (PR-body
Resolves). - #14277 confirmed NOT
epic-labeled (documentation/enhancement/ai/refactoring/architecture).
Findings: Pass.
🪜 Evidence Audit
N/A — close-target ACs (source-quality + service-boundary + test-isolation) are fully covered by unit tests (rem-observability.spec, 26/26) + static contract (behavior-preserving). No runtime surface the sandbox can't reach.
🧪 Test-Execution & Location Audit
- Branch checked out locally (
pr-14280@ce97daae8). - Canonical location: spec unchanged at
test/playwright/unit/ai/services/rem-observability.spec.mjs. - Ran the changed test:
UNIT_TEST_MODE=true npx playwright test rem-observability.spec.mjs→ 26/26 pass. - Code changed (engine refactor) — covered by the existing spec (the
getTopologyConflictCountaxis-helper + thehandoffFilePathTestisolation path).
Findings: Tests pass (26/26, my run — not just the CI claim).
📋 Required Actions
No required actions — eligible for human merge.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 95 — Neo no-module-functions + ADR-0019 trust-the-Provider + Anchor&Echo, all correctly applied; -5 only for the relocated-not-eliminated stale-config fail-loud question (ADR-19-systemic, not this PR's defect).[CONTENT_COMPLETENESS]: 95 — class-level Anchor&Echo added + every moved method documented (@protected); theextractTopologydivergence-note corrected to match reality.[EXECUTION_QUALITY]: 95 — behavior-preserving (verified 26/26); the test-isolation fix (mutate-computed-leaf → set-source-leaf) is a genuine improvement; -5 for the non-blocking silent-NaN follow-up.[PRODUCTIVITY]: 100 — fully delivers the #14277 auditability-parity goal.[IMPACT]: 55 — a load-bearing REM graph-consumer's auditability + a real test-isolation fix; debt-reduction, not user-facing.[COMPLEXITY]: 45 — mechanical helper-relocation + alias-rename + one test-isolation-semantics fix; low cognitive load, single-file core.[EFFORT_PROFILE]: Quick Win — high-ROI auditability cleanup, low complexity, behavior-preserving.
Approved. The follow-up (Provider fail-loud on a missing localModels.chat leaf) is worth a quick separate check but doesn't block — the assert-removal itself is ADR-0019-correct. Nice work.
Resolves #14277
Clarifies
TopologyInferenceEngineas a Dream Pipeline topology-conflict service: service-local helper logic now lives behind documented class methods, the local AiConfig leaf assertion layer is gone, and stalegetTopologyConflictCount()prose now matches the currentextractTopology()result contract. The related REM observability spec now overrides the active unit-test handoff leaf (handoffFilePathTest) instead of mutating the computedhandoffFilePathformula output.Evidence: L2 local static + unit coverage satisfies the #14277 source-boundary and observability-test ACs. Residual: CI must rerun the same unit surface on GitHub.
Deltas from ticket
Memory_Configservice-singleton import path forTopologyInferenceEngine. A direct config import was tested and rejected because the current REM observability spec mutates the shared services config singleton; the actual ADR 0019 cleanup is removal of the custom pass-through leaf assertion helpers and direct reads from resolved leaves at the use sites.rem-observability.spec.mjsoverride leaf after the targeted test exposed stalehandoffFilePathmutation. The config comments already say unit specs must use by-construction test handoff paths, and the pre-commitcheck-aiconfig-test-mutationgate accepted the updatedhandoffFilePathTestshape.Test Evidence
node --check ai/services/graph/TopologyInferenceEngine.mjsnode --check test/playwright/unit/ai/services/rem-observability.spec.mjsgit diff --checknpm run test-unit -- test/playwright/unit/ai/services/rem-observability.spec.mjs test/playwright/unit/ai/daemons/orchestrator/services/DreamService.spec.mjs- 61/61 passednpm run agent-preflight -- ai/services/graph/TopologyInferenceEngine.mjs test/playwright/unit/ai/services/rem-observability.spec.mjscheck-aiconfig-test-mutation,check-jsdoc-types,check-ticket-archaeology, andcheck-block-alignment.Post-Merge Validation
Commits
f70dc8d59b-refactor(ai): clarify topology inference service boundary (#14277)Evolution
Initial validation exposed that importing the memory-core config directly was not behavior-equivalent for the existing test harness. I kept the shared services singleton path, because this ticket is a service-boundary cleanup, not a config-object migration, and removed the actual local anti-pattern: custom AiConfig leaf validation helpers that defensively wrapped already-resolved Provider leaves.
Authored by Euclid (GPT-5, Codex Desktop). Session adf600d1-5f76-41d1-ad24-96722046d260.