Design notes

One X-ray. Every recall. Every surface.

Debugging an agent-memory recall used to mean stitching three separate outputs together: the tier-explain block, the audit log, and the MMR diversification trace. Remnic now emits a single RecallXraySnapshot that fuses all three. The same snapshot is available from the CLI, HTTP, and MCP — CLI, HTTP, and MCP all flow through one shared renderer, so the three surfaces never drift.

Shipping slice-by-slice under issue #570 . This page updates as each slice lands.

TL;DR. Opt-in via xrayCapture: true on any recall call, or use the new remnic xray CLI. The captured snapshot includes the served-by tier, a filter trace with considered/admitted counts per gate, the score decomposition (vector + bm25 + importance + tier prior + MMR penalty) per result, the graph path (when applicable), and the audit entry id.

What the X-ray answers

Which tier served this?

Each result is tagged with servedBy: direct-answer, hybrid, graph, recent-scan, procedural, or review-context. No more guessing whether QMD or the fallback path materialized a given memory.

What dropped candidates?

The filter trace records every gate the recall walked — namespace, trust zone, confidence, result limit — with the candidate pool it saw and the number it admitted. considered − admitted tells you exactly where the dropped candidates went.

Why this score?

Every result carries a score decomposition: final, vector, bm25, importance, mmrPenalty, tierPrior. You can see the MMR penalty applied for diversity, the importance prior applied by the tier, and the raw ranking signals.

Which audit entry?

Each result is cross-linked to its recall-audit entry id, so you can jump from the X-ray back to the durable audit ledger for forensic inspection or regression bisect.

Three surfaces, one renderer

CLI, HTTP, and MCP all call the same recall-xray-renderer.ts module. The surfaces cannot drift — a fix to the renderer fixes all three simultaneously.

CLI

remnic xray "what editor do I use" \
  --format markdown \
  --namespace workspace-a \
  --out ./xray.md

Flags: --format (text|markdown|json), --budget, --namespace, --out. Invalid flag values throw listed-options errors rather than silently defaulting.

HTTP

GET /engram/v1/recall/xray?q=…
Authorization: Bearer <token>

Returns the snapshot as JSON. Namespace scope is enforced against the authenticated principal before the recall fires; a mismatch returns {snapshotFound: false} so cross-tenant data cannot leak.

MCP

tools/call
  name: "remnic.recall_xray"
  arguments: { query, namespace?, budget? }

Available under both the canonical remnic.* and the legacy engram.* name (dual-naming invariant). Returns identical JSON to the HTTP surface.

Renderer

renderXray(snapshot, "markdown")
renderXray(snapshot, "text")
renderXray(snapshot, "json")

Pure function in packages/remnic-core/src/recall-xray-renderer.ts. Golden-file tests guard the output shape on every commit.

Opt-in, zero overhead by default

Capture is gated on xrayCapture: true. Without the flag, recall is byte-for-byte identical to the non-X-ray path. There is no additional retrieval cost when a snapshot is captured — the data is composed from values the orchestrator has already computed. Capture failures are caught and logged; they never propagate into the primary recall path.

What's in the snapshot

{
  "schemaVersion": "1",
  "snapshotFound": true,
  "query": "what editor do I use",
  "snapshotId": "…uuid…",
  "capturedAt": 1700000000000,
  "sessionKey": "sess-42",
  "namespace": "workspace-a",
  "traceId": "trace-xyz",
  "tierExplain": { "tier": "direct-answer", … },
  "filters": [
    { "name": "namespace", "considered": 120, "admitted": 30 },
    { "name": "recall-result-limit",
      "considered": 30, "admitted": 5, "reason": "cap=5" }
  ],
  "results": [
    {
      "memoryId": "mem-1",
      "path": "facts/tools/editor.md",
      "servedBy": "direct-answer",
      "scoreDecomposition": {
        "final": 0.87, "vector": 0.81, "bm25": 0.42,
        "importance": 0.9, "mmrPenalty": 0.05, "tierPrior": 0.2
      },
      "admittedBy": ["namespace", "trustZone", "importance"],
      "graphPath": ["mem-root", "mem-1"],
      "auditEntryId": "audit-2026-04-20-abc"
    }
  ],
  "budget": { "chars": 4096, "used": 1234 }
}

The schemaVersion field is version-gated so downstream consumers can pin their parsers. The renderer handles missing / out-of-range fields (including non-finite capturedAt) gracefully rather than crashing.

Honest status

  • Shipped Schema + in-process capture (xrayCapture: true) — PR #578.
  • Shipped Shared renderer (renderXray) with text / markdown / JSON formats and golden-file tests.
  • Shipped remnic xray CLI command.
  • Shipped GET /engram/v1/recall/xray HTTP route (bearer auth + namespace scope).
  • Shipped engram.recall_xray / remnic.recall_xray MCP tool.
  • In flight Legacy /recall/explain delegates to the xray renderer in markdown mode for backwards-compatible output.

Status reflects the merge state of issue #570's 7 slices. A slice flips from "In flight" to "Shipped" the moment its PR merges into main.