LearnNewsExamplesServices
Frontmatter
title>-
authorneo-opus-ada
stateOpen
createdAt1:05 PM
updatedAt1:05 PM
closedAt
mergedAt
branchesdevagent/14883-rem-metrics-plumbing
urlhttps://github.com/neomjs/neo/pull/14890
contentTrust
projected
quarantined0
signals[]
Open
neo-opus-ada
neo-opus-ada commented on 1:05 PM

Resolves #14883

#14882 shipped the honest-states classifier but wired the caller with a digested-history proxy (a summary% node count), so REM_STALLED could never fire — the render could only reach FRONTIER_UNANCHORED / UNATTRIBUTED. This fetches the real REM pipeline state — HealthService.getRemPipelineState() ({undigested, digested, recentCycles}) — at the already-async caller and passes it into classifyFrontierEmptyCause. A genuine stall (undigested backlog, no recent cycle) now renders REM_STALLED, and the real digested figure replaces the proxy. The async fetch lives in the caller (matching the existing await import('../../services/memory-core/…') pattern) to keep the SQLite fallback builder sync; a missing/failed remState degrades to the honest UNATTRIBUTED phrase.

Evidence: L2 (unit — GoldenPathSynthesizer + goldenPathPickupBridge specs, 55 passed; a new whitebox test drives REM_STALLED / FRONTIER_UNANCHORED / UNATTRIBUTED through the wiring) → L2 sufficient: the ACs are the classifier inputs + the render string, both unit-covered. Residual: a live sandman run confirming REM_STALLED under a real stall (post-merge check).

Deltas from ticket

  • The REM accessor is HealthService.getRemPipelineState() (default export → .getRemPipelineState(), the verified toolService pattern), dynamic-imported at the async caller — no cross-service static import at module top.
  • Non-breaking: buildDeclaredIntentFallback(remState = {}) — existing no-arg calls (and the two existing specs) still work, degrading to UNATTRIBUTED.

Test Evidence

npm run test-unit -- test/playwright/unit/ai/services/graph/GoldenPathSynthesizer.spec.mjs test/playwright/unit/ai/services/graph/goldenPathPickupBridge.spec.mjs55 passed (worktree, exit 0). New test buildDeclaredIntentFallback attributes the MEASURED REM cause…: a stall remState (undigested:40, recentCycles:[]) renders REM consolidation stalled and never REM-starved; a healthy-digestion-empty-anchor remState renders frontier unanchored; no remState renders unattributed.

Post-Merge Validation

  • After a live sandman run with a genuine consolidation backlog + no recent cycle, confirm the Computed GP fallback renders REM consolidation stalled….

Merge coordination

Both this PR and #14888 touch GoldenPathSynthesizer.mjs in different regions (this = the buildDeclaredIntentFallback method + its caller; #14888 = the Concept Slice append/write). No overlapping hunks, but merge #14888 first (it is green + cross-family-routed and ahead); this branch rebases trivially onto the result.

Decision Record

aligned-with ADR-0019 — reads aiConfig.goldenPathTopNodeRenderLimit at the use site (existing); the REM accessor is a service method, not config; no threading/mutation. No ADR amended.

Related: #14882 (parent fix this completes), #14472 (epic), #14565 (direction-attribution vocabulary).

Authored by Ada (Claude Opus 4.8, Claude Code). Session 9360840f-5d7a-4680-8110-86877722735b.