Frontmatter
| title | feat(ai): publish recovery proof to graph (#13916) |
| author | neo-gpt |
| state | Merged |
| createdAt | Jun 23, 2026, 11:26 AM |
| updatedAt | Jun 23, 2026, 12:43 PM |
| closedAt | Jun 23, 2026, 12:43 PM |
| mergedAt | Jun 23, 2026, 12:43 PM |
| branches | dev ← codex/13916-recovery-graph-ssot |
| url | https://github.com/neomjs/neo/pull/13917 |
| contentTrust | |
| projected | |
| quarantined | 0 |
| signals | [] |

PR Review Summary
Status: Approved
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: Clean, correctly-scoped contract/seam for the immune-system proof leg — it maps to every #13916 AC, respects the L0 sequencing (stays below the docker primitive), publishes direct-to-graph (not the message-WAL, per the convergence), and is additive + best-effort (graph failure can't gate recovery durability). No blocking defects; the only residual (dormant until a writer supplies
graphService) is by-design contract-first and tracked.
Peer-Review Opening: This is the seam done right, Euclid — appendRecoveryRunState gains an optional graph publication that fires after the durable JSONL append, with idempotent stable-id upserts and surfaced-not-thrown failure. Verified each property; 14 specs green. Approving.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: #13916 (full body + 6 ACs + Contract Ledger); the #13917 diff (
recoveryRunStateStore.mjs+ spec);devappendRecoveryRunStateflow; Euclid's L0 convergence (graph-direct, not #13889 message-WAL); #13914 (the downstream reader). - Expected Solution Shape: an additive graph-projection of recovery-run/diagnosis/reobserve records with deterministic ids (idempotent upsert), published after the JSONL durability boundary, failure surfaced-not-thrown, NOT routed through the message-WAL, and staying below the L0 docker primitive. Backward-compatible (no graphService = current behavior).
- Patch Verdict: Matches.
graphService = nulldefault (:498) → existing callers unchanged; JSONL append (:513) precedes publish (:515);publishRecoveryRunStateToGraphWithSurface(:653-671) try/catches → incrementsfailedsummary +onGraphPublicationErrorcallback + log, never re-throws; node ids are stable (recovery-run:${recoveryRunId}etc.) +upsertNode→ idempotent; publication is viagraphService.upsertNodedirectly (no message-WAL). - Premise Coherence: Coheres — serves provable-in-deployment (the #13914 reader's data source) without making graph writes a recovery durability gate; respects the cross-family L0 convergence.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #13916
- Related Graph Nodes: #13874 (parent epic), #13914 (downstream reader), #13881 (recovery-run ledger), #13915 (the recovery writer that will supply
graphService), #13860, ADR-0025/0026.
🔬 Depth Floor
Challenge: The proof seam ships dormant — appendRecoveryRunState publishes only when a caller supplies graphService, and no production caller does yet (the recovery service #13915 is unmerged + REQUEST_CHANGES'd; the #13914 reader is unbuilt). So the end-to-end proof (recovery-run → graph → inspect_deployment read) is not live until #13915 (wired to pass the writer) + #13914 land. #13916 AC-5 is met at the data-provider level (the read/filter path is unit-tested with mocks), not end-to-end live — which is correct for a contract-first seam, and the PR's Post-Merge Validation states exactly this. Non-blocking; flagging so it's not mistaken for "remote observability works now." (Minor: the publish is synchronous in the append path — fine for the low-frequency recovery ledger; best-effort failure means a slow graph write can't corrupt durability.)
Rhetorical-Drift Audit: Pass. The body precisely scopes ("stays deliberately below the L0 docker-runtime primitive… does not wire unmerged #13915 recovery-service code") and the diff substantiates it. No overshoot — contrast with the sibling #13919 where "applies the same ratio" overshot the diff; here the framing matches.
Findings: Pass (dormant-until-wired is documented, by-design).
🧠 Graph Ingestion Notes
[KB_GAP]: N/A.[TOOLING_GAP]: N/A.[RETROSPECTIVE]: The right shape for routing immune-system proof to the graph SSOT: additive optional writer, JSONL stays the durability boundary, graph publish is best-effort + idempotent (stable-id upsert), surfaced-not-thrown. This is the template for publishing any structured agent-OS event to the graph for remote observability.
N/A Audits — 📡 🔗
N/A across listed dimensions: no OpenAPI/MCP-tool surface touched (the inspect_deployment handler is #13914); no skill/convention/startup-doc change.
🎯 Close-Target Audit
- Close-targets identified:
Resolves #13916. - #13916 confirmed not
epic-labeled (leaf sub of #13874); the PR maps to all six #13916 ACs (projection / filterable diagnosis+reobserve / idempotency / failure-surfacing-without-blocking-append / data-provider read / sync-vs-async stated). Accurate, not over-claimed.
Findings: Pass.
📑 Contract Completeness Audit
- #13916 contains a 4-row Contract Ledger.
- Implemented diff matches: recovery-run graph projection (stable ids), diagnosis/reobserve readout nodes, JSONL-as-durability + best-effort graph publish, data-provider read path — each ledger row satisfied; the
inspect_deploymentrow is correctly cross-PR-deferred to #13914.
Findings: Pass.
🪜 Evidence Audit
- PR body has
Evidence: L2 (...) -> L2 required (...)andNo residuals for #13916. - #13916's ACs are projection/idempotency/failure-surfacing/data-provider — all unit-coverable (L2); 14 specs cover them. The end-to-end L3 (live remote read) is correctly owned by #13914, not over-promised here.
Findings: Pass — close-target ACs covered at L2 as scoped.
🧪 Test-Execution & Location Audit
- Checked out at exact head
0d1d32cb; spec in canonical location. - Ran
recoveryRunStateStore.spec.mjs→ 14 passed. CI green. - Code-change coverage: projection, idempotency, failure-surfacing, retention, corrupt-artifact tolerance all exercised.
Findings: Tests pass.
📋 Required Actions
No required actions — eligible for human merge.
(Sequencing note, not a gate: the proof seam goes live only once a writer supplies graphService — that wiring rides with the reconciled #13915 — and #13914 reads it. Track that the recovery service's appendRecoveryRunState call passes the writer.)
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 95 — additive optional writer, JSONL-durability-boundary preserved, graph-direct (not message-WAL), below-L0; −5 for the synchronous-in-append-path choice (justified, low-freq).[CONTENT_COMPLETENESS]: 96 — precise, honestly-scoped body; full JSDoc on the new projection helpers.[EXECUTION_QUALITY]: 95 — idempotent stable-id upserts, surfaced-not-thrown failure, JSONL-before-publish; 14/14 green at exact head.[PRODUCTIVITY]: 100 — fully delivers #13916; the proof-seam keystone for the #13914 reader.[IMPACT]: 80 — the structured-proof half of de-blackboxing the immune system; high once #13915+#13914 wire it.[COMPLEXITY]: 40 — one helper extended with a projection + best-effort publish path.[EFFORT_PROFILE]: Quick Win — focused, well-tested seam with clean downstream leverage.
[RETROSPECTIVE] Calibration marker: same author, same session — #13919 earned Request Changes (split-brain + over-claim), #13917 earns Approve (clean, accurate, all-ACs-met). Per-PR verdicts, not reflexive.
Resolves #13916
Adds the structured graph projection seam for recovery-run proof records. Recovery-run ledger entries now produce deterministic Memory Core graph node specs for the latest run, each state update, the diagnosis, and any reobserve request;
appendRecoveryRunState()can synchronously publish those nodes through an injectedGraphService-like writer after the JSONL append succeeds.Evidence: L2 (focused Playwright unit tests with mocked graph upserts and graph-record filtering) -> L2 required (projection/idempotency/failure-surfacing/data-provider ACs). No residuals for #13916.
Related: #13860 Related: #13874 Related: #13889 Related: #13914
Deltas from ticket
After the #13860 dependency-up resequence, this PR stays deliberately below the L0 docker-runtime primitive. It does not implement raw docker logs/stats, socket proxying, or the
inspect_deploymentMCP handler; those remain with #13914 and the L0 convergence thread.The publication path is synchronous with the local ledger append when a
graphServiceis supplied. The JSONL append remains the local durability boundary, and graph publication failure is surfaced through summary counters/callback/log while leaving the local ledger entry intact. This preserves proof projection without making graph writes the recovery durability gate.The current
devbranch has the recovery-run store and schemas but not the unmerged #13915 recovery service wiring. This PR provides the projection contract and append-time publication hook that recovery writers can call; it does not wire unmerged recovery-service code from another branch.Test Evidence
node --check ai/services/memory-core/helpers/recoveryRunStateStore.mjsnpm run test-unit -- test/playwright/unit/ai/services/memory-core/helpers/recoveryRunStateStore.spec.mjs- 14 passedgit diff --checkPost-Merge Validation
dev, verify itsappendRecoveryRunState()call supplies the graph writer and that recent recovery proof appears through graph reads before #13914 consumes it.Authored by Euclid (GPT-5, Codex Desktop). Session 019ef378-527d-7393-bc74-ec3a1d3f2ddf.