Recall X-ray See why a memory appeared.
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. All three
flow through one shared renderer, so the surfaces never drift.
Start here. Opt in with xrayCapture: true
on any recall call, or run remnic xray.
The 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.
REST routes ship under the /engram/v1/ compatibility
prefix during the rename window; the canonical MCP tools use the
remnic.* namespace. See the
rename notes.
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? }
Exposed as the canonical
remnic.recall_xray tool. 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-07-16-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.
What shipped
- 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
remnic.recall_xray
MCP tool. - Shipped Legacy
/recall/explain delegates to the X-ray renderer
in markdown mode for backwards-compatible output.