REM State Model
5-Axis Snapshot
The REM pipeline exposes a read-only Memory Core MCP tool, get_rem_pipeline_state,
for operator-visible pipeline health. Phase 1b is a projection layer over the
Phase 1a helpers; it does not write graph nodes or mutate Sandman state.
{
undigested : Number, // Chroma summaries without graphDigested true
digested : Number, // Chroma summaries with graphDigested true
sessionNodes : Number, // SQLite SESSION nodes
topologyConflicts: Number, // handoff conflict entries
recentCycles : [{
runId : String,
wallClockMs : Number,
cycleOverflowSignal: Boolean,
cycleOverflowRatio : Number,
outcome : String
}],
perSession : {
sessionId : String,
entityCount: Number // inbound graph entity/provenance edges
}
}The key diagnostic is divergence between digested and sessionNodes. A healthy
REM digest path keeps those counts close within a batch window; a wide gap means
Chroma believes sessions were digested while the Native Edge Graph did not receive
matching SESSION nodes. This is the failure class surfaced by the 76x divergence
anchor from Epic #12065 Sub 2.
Source Anchors
PR #12081 shipped the Phase 1a helper JSDocs that define the current axis
contracts in ChromaManager, GraphService, and TopologyInferenceEngine.
This MCP projection intentionally composes those helpers instead of redefining
their semantics at the server layer.
PR #12077 shipped the Sub 1 Sandman silent-failure forensics runbook. Use that runbook when the axis snapshot indicates drift; this page only explains how to read the snapshot and where Phase 2 state will attach.
MCP Usage
Use the deployment-wide snapshot first:
await callTool('get_rem_pipeline_state', {});When a specific session looks suspicious, pass sessionId to include Axis C:
await callTool('get_rem_pipeline_state', {
sessionId: '550e8400-e29b-41d4-a716-446655440000'
});Operator Cookbook
Check undigested to confirm the queue is actually draining. If it remains high
across multiple REM cycles, the graph extraction arm is not catching up or the
provider is not completing enough sessions per cycle.
Compare digested with sessionNodes. If digested rises but sessionNodes
does not, the pipeline is setting graphDigested without durable SESSION-node
growth. That is a silent-failure signal and should be investigated before trusting
the Sandman handoff.
Use topologyConflicts as the handoff-conflict count only. A zero value does not
prove topology extraction succeeded; it can also mean the provider/error path
returned no durable conflict entries. Use recentCycles for the cycle-level
outcome and overflow signal before treating zero conflicts as a clean run.
Use perSession.entityCount to inspect extraction yield for a single session. A
digested session with zero inbound entity/provenance edges is suspicious when the
payload clearly contained entities.
Active Tri-Vector Call Diagnostics
While a REM Tri-Vector provider call is in flight, SemanticGraphExtractor
writes a gitignored active-call snapshot under NEO_REM_RUN_STATE_DIR. This is
the operator correlation surface for provider-visible counters such as LM Studio's
token meter. The snapshot includes the session id, asset/chunk id, chunk index,
provider/model, prompt estimate, output cap, context cap, and bounded request
message fingerprints.
requestMessageFingerprints[] deliberately avoids payload logging. Each entry is
limited to:
{
index : Number,
role : String,
bytes : Number,
tokensEstimate : Number,
contentSha256Prefix: String // first 16 hex chars only
}Use these fields to correlate one Neo request with one provider counter without leaking full session content. A second REM call should show new fingerprints for its own message slots, not the prior session's payload.
Durable Cycle State
DreamService.executeRemCycle() writes one append-only JSONL artifact per run
under the gitignored NEO_REM_RUN_STATE_DIR directory
(.neo-ai-data/rem-runs by default). The file name is the sanitized runId, and
each line is a complete cycle snapshot:
{
runId,
reason,
startedAt,
completedAt,
wallClockMs,
configuredCadenceMs,
cycleOverflowSignal,
cycleOverflowRatio,
outcome,
reasonCode,
failurePhase,
failureReason,
lastSuccessfulPhase,
cycleScopePhases,
perPhaseStates,
perSessionStates
}The JSONL store is deliberately off-graph. A broken REM run can diagnose itself
without mutating the Native Edge Graph control plane. get_rem_pipeline_state
projects only the recent cycle summary fields so operator dashboards stay small;
inspect the JSONL artifact directly when phase-level or per-session failure
reasons are needed.
Retention
Each REM cycle appends one new <runId>.jsonl artifact, so the directory would grow
without bound across a deployment's lifetime (~24 files/day at the default 1h dream
cadence). appendRemRunState applies a write-side retention cap on every append:
after writing, it prunes the oldest artifacts beyond remRunRetentionLimit
(NEO_REM_RUN_RETENTION_LIMIT, default 200). Retention sorts by mtime, removing the
oldest and never the recent window.
Bounding the artifact count on the write side also bounds the read path:
readRecentRemRunStates — and therefore every get_rem_pipeline_state / healthcheck call
— readdirs + stats the directory, so its cost is capped by the retention bound rather
than growing with the total number of cycles ever run.
A write-side cap is preferred over an orchestrator cleanup lane: the JSONL store stays self-contained and filesystem-durable, with no cross-service boundary and no graph mutation during a REM run.
Disabled mode: a non-positive or non-finite remRunRetentionLimit disables pruning —
appendRemRunState then keeps every artifact (unbounded). Use this only for short-lived
forensic captures where you want the full history and will clean the directory yourself.
Set NEO_ORCHESTRATOR_DREAM_OVERFLOW_THRESHOLD to tune the overflow warning
ratio against the configured dream cadence. The default is 0.8, meaning a
cycle that consumes more than 80% of its cadence is flagged as an overlap risk.