Parent: #13874
Related: #13914, #13889, #13881, #13884, #13860
Release classification: boardless - agent-OS immune-system proof substrate, not currently attached to a release board.
Context
The recovery daemon now has local recovery-run state and a gated actuator path in flight, while #13914 adds the remote cloud-deployment proof surface. The missing seam between them is structured publication: inspect_deployment needs to read recovery-runs and diagnosis events from the Memory Core graph SSOT, but the current recovery-run store is local JSONL on the orchestrator side.
Live latest-open sweep: checked the latest 20 open issues on 2026-06-23T09:10Z. The only matching observability/proof umbrella was #13914; no separate recovery-run graph-publication child existed. Exact duplicate sweeps for recovery-run graph and diagnosis events graph SSOT returned #13914 only. A2A in-flight sweep could not be completed because the current Codex harness exposes no Neo list_messages/add_message mailbox tool; the operator wake and live GitHub sweeps showed no competing claim for this narrow seam.
The Problem
The recovery subsystem can write durable local recovery-run JSONL, but a cloud agent connected to Memory Core cannot inspect those artifacts across the container boundary. If #13914 ships the MCP read surface before recovery-runs and diagnosis events are published to graph, the tool either becomes graph-empty for the structured proof half or has to pierce orchestrator-local storage directly.
That would preserve the black-box failure mode: the immune system may have acted, but remote proof still depends on shell access to the orchestrator container.
The Architectural Reality
ai/services/memory-core/helpers/recoveryRunStateStore.mjs defines the typed recovery classes, target identities, diagnosis events, reobserve requests, and recovery-run state entries. createRecoveryRunStateEntry() emits type: 'recovery-run-state', recoveryRunId, diagnosisId, recoveryClass, targetIdentity, rung, status, anti-thrash fields, and optional reobserve request. appendRecoveryRunState() appends JSONL locally, while readRecentRecoveryRunStates() reads local files only.
Memory Core already exposes graph read surfaces remotely: get_node, search_nodes, and query_hybrid_graph are declared in ai/mcp/server/memory-core/openapi.yaml, and ai/mcp/server/memory-core/toolService.mjs routes them through GraphService.
GraphService.upsertNode() is the existing graph node write primitive. This ticket should reuse existing graph write/read primitives and recovery-run schemas rather than inventing an observability-specific state store.
The Fix
Publish structured immune-system events into the Memory Core graph SSOT whenever recovery writes or observes them:
- Project recovery-run state entries into stable graph nodes keyed by recovery run and update sequence.
- Project diagnosis events and reobserve requests as queryable structured nodes or properties so
inspect_deployment can return them without reading orchestrator-local JSONL.
- Preserve idempotency: re-publishing the same recovery-run entry must update/converge, not duplicate or corrupt the graph.
- Keep the JSONL ledger as the local write-ahead/retention artifact; graph publication is the remote read/proof projection.
- Fail visibly when graph publication fails, but do not let graph write failure erase the local recovery-run ledger entry.
- Keep the first implementation on existing service seams where practical; if it introduces a new
.mjs module, run structural pre-flight before choosing the directory.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
| Recovery-run graph projection |
#13881 recovery-run ledger + recoveryRunStateStore.mjs |
Local recovery-run entries are also published as stable graph records |
JSONL append remains durable if graph projection fails |
JSDoc / PR body |
L2 projection/idempotency tests |
| Diagnosis event readout |
ADR-0025 diagnostics input + createRecoveryDiagnosisEvent() |
Diagnosis events become remotely queryable through graph-backed tooling |
Keep local ledger detail if graph write fails |
JSDoc / test names |
L2 graph query tests |
| Reobserve/proof handshake |
#13874 verify-loop requirement |
Reobserve requests and terminal recovery statuses can be fetched remotely by inspect_deployment |
Local JSONL remains readable in local mode |
PR body |
L2 mocked graph + local fallback test |
inspect_deployment dependency |
#13914 proof surface |
Tool reads structured immune-system state from graph SSOT, not orchestrator-local JSONL |
Tool reports graph state unavailable rather than pretending proof exists |
OpenAPI/tool PR body |
Cross-PR integration evidence |
Decision Record impact
Depends on ADR-0025 and ADR-0026. Aligned with the recovery-run ledger delivered by #13881. No ADR change expected for this child; raw docker-log/stat access and any ADR-0026 amendment stay with #13914.
Decision Record: Not needed.
Acceptance Criteria
Out of Scope
- Raw docker logs, docker stats, socket proxying, and the
inspect_deployment MCP handler itself; that is #13914.
- The B1 actuator implementation; that is #13884.
- A2A message WAL replay and mailbox graph projection; that remains #13892 after #13890 lands.
- Rich dashboards or UI rendering.
Avoided Traps
- Do not make the Memory Core tool read orchestrator-local JSONL directly as the primary cloud proof path.
- Do not mount a second docker socket on Memory Core to solve structured event visibility.
- Do not treat graph publication as the durability boundary for recovery; JSONL remains the local durable ledger.
- Do not overclaim cloud proof until
inspect_deployment can read these records remotely.
Related
Parent: #13874
Related: #13914, #13889, #13881, #13884, #13860
Origin Session ID: 019ef378-527d-7393-bc74-ec3a1d3f2ddf
Handoff Retrieval Hints: recovery-run graph SSOT diagnosis events inspect_deployment proof; recoveryRunStateStore GraphService upsertNode cloud observability
Parent: #13874
Related: #13914, #13889, #13881, #13884, #13860
Release classification: boardless - agent-OS immune-system proof substrate, not currently attached to a release board.
Context
The recovery daemon now has local recovery-run state and a gated actuator path in flight, while
#13914adds the remote cloud-deployment proof surface. The missing seam between them is structured publication:inspect_deploymentneeds to read recovery-runs and diagnosis events from the Memory Core graph SSOT, but the current recovery-run store is local JSONL on the orchestrator side.Live latest-open sweep: checked the latest 20 open issues on 2026-06-23T09:10Z. The only matching observability/proof umbrella was #13914; no separate recovery-run graph-publication child existed. Exact duplicate sweeps for
recovery-run graphanddiagnosis events graph SSOTreturned #13914 only. A2A in-flight sweep could not be completed because the current Codex harness exposes no Neolist_messages/add_messagemailbox tool; the operator wake and live GitHub sweeps showed no competing claim for this narrow seam.The Problem
The recovery subsystem can write durable local recovery-run JSONL, but a cloud agent connected to Memory Core cannot inspect those artifacts across the container boundary. If
#13914ships the MCP read surface before recovery-runs and diagnosis events are published to graph, the tool either becomes graph-empty for the structured proof half or has to pierce orchestrator-local storage directly.That would preserve the black-box failure mode: the immune system may have acted, but remote proof still depends on shell access to the orchestrator container.
The Architectural Reality
ai/services/memory-core/helpers/recoveryRunStateStore.mjsdefines the typed recovery classes, target identities, diagnosis events, reobserve requests, and recovery-run state entries.createRecoveryRunStateEntry()emitstype: 'recovery-run-state',recoveryRunId,diagnosisId,recoveryClass,targetIdentity, rung, status, anti-thrash fields, and optional reobserve request.appendRecoveryRunState()appends JSONL locally, whilereadRecentRecoveryRunStates()reads local files only.Memory Core already exposes graph read surfaces remotely:
get_node,search_nodes, andquery_hybrid_graphare declared inai/mcp/server/memory-core/openapi.yaml, andai/mcp/server/memory-core/toolService.mjsroutes them throughGraphService.GraphService.upsertNode()is the existing graph node write primitive. This ticket should reuse existing graph write/read primitives and recovery-run schemas rather than inventing an observability-specific state store.The Fix
Publish structured immune-system events into the Memory Core graph SSOT whenever recovery writes or observes them:
inspect_deploymentcan return them without reading orchestrator-local JSONL..mjsmodule, run structural pre-flight before choosing the directory.Contract Ledger Matrix
recoveryRunStateStore.mjscreateRecoveryDiagnosisEvent()inspect_deploymentinspect_deploymentdependencyDecision Record impact
Depends on ADR-0025 and ADR-0026. Aligned with the recovery-run ledger delivered by #13881. No ADR change expected for this child; raw docker-log/stat access and any ADR-0026 amendment stay with #13914.
Decision Record: Not needed.
Acceptance Criteria
inspect_deploymentor its data provider can read structured recovery proof from graph records instead of orchestrator-local JSONL.Out of Scope
inspect_deploymentMCP handler itself; that is #13914.Avoided Traps
inspect_deploymentcan read these records remotely.Related
Parent: #13874
Related: #13914, #13889, #13881, #13884, #13860
Origin Session ID: 019ef378-527d-7393-bc74-ec3a1d3f2ddf
Handoff Retrieval Hints:
recovery-run graph SSOT diagnosis events inspect_deployment proof;recoveryRunStateStore GraphService upsertNode cloud observability