Skip to content

Phase 1: delete local NARS+GraphSnapshot, single atomic commit #33

Description

@AdaWorldAPI

Why

q2 currently maintains its own GraphSnapshot, GraphNode, GraphEdge, GraphHealth, and NarsInference data structures, plus its own run_nars_deduction(min_confidence, max_hops) (2-hop traversal with f = f1*f2, c = f1*f2*c1*c2) inside crates/cockpit-server/src/graph_engine.rs.

This violates Architectural Commitment #4 in MedCare-rs/CLAUDE.md"thinking lives only in lance-graph". The local NARS deduction is a duplicate-thinking trap: a temporary local impl that becomes permanent the moment any code path falls back to it.

When A1 (lance-graph#331) lands lance_graph::TripletGraph upstream, q2's local impl must be deleted in the same commit as the switch to upstream — no fallback branch, no parallel impl path, no "we'll clean it up later" PR. The "temporary local impl that becomes permanent" pattern is the highest-debt risk on the Phase 1 critical path.

Deletion atomicity is the contract: either the spine wiring lands and the local impl is gone, or neither lands. A PR that introduces the upstream call while leaving the local NARS code in-tree (even behind a cfg or env flag) is a regression of this issue, not a partial fix.

What

In a single atomic commit in AdaWorldAPI/q2:

File: crates/cockpit-server/src/graph_engine.rs

Structs to delete (replace with re-exports from lance_graph where shape matches, otherwise inline lance_graph::TripletGraph directly):

  • GraphSnapshot (lines ~30-41: nodes, edges, node_count, edge_count, scene_version, scene_name, health, nars_inferences)
  • GraphNode (lines ~43-…)
  • GraphEdge
  • GraphHealth — health endpoint must report spine state, not local cache state
  • NarsInference

Function to delete:

  • run_nars_deduction(min_confidence: f32, max_hops: usize) -> Vec (lines ~196-247) — entire 2-hop body with f = f1*f2, c = f1*f2*c1*c2 is removed; deduction is delegated to lance_graph::TripletGraph calls.

File: crates/cockpit-server/src/main.rs

Fallback branches to delete:

  • Line ~154-156: tracing::warn!(" /api/graph/* → fallback mode (hydration failed: {e})")
  • Line ~159: tracing::warn!(" ... → fallback mode (AIWAR_DATA_PATH not set)")

After this commit, there is no fallback path. If the spine fails to initialize, the server fails to start (or the /api/graph/health endpoint reports unhealthy and the inference/snapshot routes return 503) — but the binary never silently degrades to a local NARS impl.

Architecture

The double-buffer pattern stays, but it wraps lance_graph::TripletGraph directly — not a q2-local newtype:

static LIVE_GRAPH: LazyLock>> = ...;

Do NOT introduce a wrapper newtype "for serde convenience" or "to add q2-specific helpers". Wrappers around upstream graph types are half-twins that drift — within two PRs the wrapper accretes methods, the upstream type evolves, and the wrapper becomes a second source of truth. If lance_graph::TripletGraph doesn't serialize the way q2's HTTP responses need, the fix is upstream (a serde feature on lance-graph), not a q2 wrapper.

Health endpoint contract:

  • GET /api/graph/health reports the spine's state (last successful TripletGraph load, spine version, triplet count from TripletGraph::len(), last inference latency from spine).
  • It does not report a local cache count, local hydration status, or any q2-internal field.

Routes preserved (shape-stable — same JSON schema, same status codes, same headers):

  • GET /api/graph/snapshotgraph_engine::graph_snapshot_handler
  • POST /api/graph/infergraph_engine::nars_infer_handler
  • GET /api/graph/healthgraph_engine::graph_health_handler

Existing API consumers must not observe a behavioral change beyond "data is now sourced from the spine" — same field names, same types, same ordering.

Acceptance criteria

  • Single atomic commit — the deletion of local structs/NARS function and the switch to lance_graph::TripletGraph are in one commit, not split across two.
  • No fallback path remaininggrep -ri "fallback" crates/cockpit-server/src/ returns no graph-engine matches; no cfg(feature = "local-nars"), no env-flagged local impl, no commented-out local code.
  • Health reflects spine/api/graph/health body fields are sourced from TripletGraph state; no q2-local GraphHealth struct exists in graph_engine.rs.
  • Routes survive shape-stable/api/graph/snapshot, /api/graph/infer, /api/graph/health return identical JSON shape (golden-file or schema test) before vs after the commit.
  • No new wrapper newtype around lance_graph::TripletGraphLIVE_GRAPH holds the upstream type directly.
  • run_nars_deduction symbol removed from the crate (cargo doc / rg "fn run_nars_deduction" returns zero hits).
  • cargo build -p cockpit-server succeeds against the upstream lance-graph revision that lands A1.

Out of scope

  • candle/burn integration — that's E1/E2, separate critical path.
  • Cypher last-mile — that's A2, and it lives in lance-graph, not q2.
  • Refactoring of hydrate_from_aiwar_json beyond what's needed to feed the spine (full hydration pipeline rework is a separate ticket).
  • Changes to top-level /health (line ~944 in main.rs) — that's a different handler.

Dependencies

Blocks on: AdaWorldAPI/lance-graph#331 (A1 — lance_graph::TripletGraph public API land).

This issue cannot start until A1's TripletGraph is published with a stable signature; once A1 lands, this work is unblocked and must complete in a single atomic commit as described.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions