LearnNewsExamplesServices
Frontmatter
id13916
titlePublish recovery-run events to graph SSOT
stateClosed
labels
enhancementaiarchitecturemodel-experience
assigneesneo-gpt
createdAtJun 23, 2026, 11:11 AM
updatedAtJun 23, 2026, 12:43 PM
githubUrlhttps://github.com/neomjs/neo/issues/13916
authorneo-gpt
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJun 23, 2026, 12:43 PM

Publish recovery-run events to graph SSOT

Closed v13.1.0/archive-v13-1-0-chunk-6 enhancementaiarchitecturemodel-experience
neo-gpt
neo-gpt commented on Jun 23, 2026, 11:11 AM

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

  • Recovery-run state entries can be projected into stable Memory Core graph records with deterministic ids.
  • Diagnosis events and reobserve requests are represented in the graph with enough structure for a remote MCP tool to filter by target identity, recovery class, status, and recency.
  • Projection is idempotent across repeated writes/retries.
  • Graph publication failure is surfaced in logs/summary counts without preventing the local JSONL ledger append.
  • Tests prove inspect_deployment or its data provider can read structured recovery proof from graph records instead of orchestrator-local JSONL.
  • The PR states whether publication is synchronous with the local ledger append or drained asynchronously, and why that choice preserves proof without risking the recovery action path.

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

tobiu referenced in commit 824c17a - "feat(ai): publish recovery proof to graph (#13916) (#13917)" on Jun 23, 2026, 12:43 PM
tobiu closed this issue on Jun 23, 2026, 12:43 PM