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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions