Context
A live REM backlog drain hit an LMS over-cap generation during Tri-Vector extraction even after the turn-boundary chunker work from #12073 landed.
Evidence from the active run:
- Session
2d993feb-ea2f-4468-8fbd-c53e62365f4d (FleetManager Provisioning and WriteGuard Enforcement Verification) has 192 raw turns, 500586 chars, and an estimated /3 size of ~`168k` tokens.
- The new chunker split it into 2 Tri-Vector chunks. Chunk 1 completed; chunk 2 input is estimated around
70.5k prompt tokens, below the configured LMS chat safe band.
- LMS still climbed past
144k generated-context accounting and continued growing, with no graph commit/topology line written.
Live latest-open sweep: checked latest 20 open issues at 2026-06-25T00:45Z; no equivalent ticket found.
A2A claim sweep: checked latest 30 messages at 2026-06-25T00:45Z; no equivalent in-flight claim found.
Release classification: boardless urgent infrastructure hardening; blocks REM backlog drain but is not a release-board scope expansion.
The Problem
The REM input-side guardrail is now doing its job: it chunks by session turn boundary and keeps each prompt under the configured safe band. The remaining failure is output-side. SemanticGraphExtractor asks the LMS/OpenAI-compatible provider for schema-constrained JSON, but the provider wrapper streams until LMS reports completion and does not enforce a structured-output token budget.
That means a valid-sized REM chunk can still burn the chat model context through excessive generated JSON, schema rambling, or provider finish metadata loss.
The Architectural Reality
ai/services/graph/SemanticGraphExtractor.mjs calls provider.generate() for Tri-Vector schema extraction.
ai/provider/OpenAiCompatible.mjs accepts arbitrary OpenAI-compatible payload keys, so an LMS max_tokens/max_completion_tokens cap can be passed through, but the current caller does not provide one.
- The streaming path currently returns only
{content, raw: {message: {content}}}. It drops finish_reason, so length completion cannot be classified by SemanticGraphExtractor.getCompletionFinishReason().
- This is LMS/OpenAI-compatible chat generation. It is not an embedding truncation issue and should not silently truncate REM input.
The Fix
- Add an ADR-19-compliant
AiConfig.localModels.chat leaf for graph structured-output budget.
- Pass that budget into REM graph extraction provider calls as OpenAI-compatible output caps.
- Preserve OpenAI-compatible streaming finish metadata in
OpenAiCompatible.generate() so length/limit finishes fail loud instead of entering repair loops or silent non-commit states.
- Add chunk-level logging around LMS graph extraction calls so the active session/chunk is visible before the provider returns.
- Keep the backlog-drain behavior resumable: bounded output should produce valid JSON or a terminal failure descriptor, not an indefinite over-cap run.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback |
Docs |
Evidence |
AiConfig.localModels.chat |
ADR-0019 |
New typed leaf for graph structured-output token budget |
Fail loud if invalid |
Config comments |
Unit test asserts provider call receives cap |
OpenAiCompatible.generate() raw metadata |
Provider wrapper contract |
Preserve finish metadata from streaming chunks |
Return undefined when provider omits it |
JSDoc |
Unit test for finish_reason='length' detection |
| REM graph extraction |
Dream Pipeline / Memory Core |
Bounded LMS schema generation per chunk |
Terminal failure descriptor, no infinite generation |
Existing graph extractor comments |
Unit test with chunked extraction and length finish |
Decision Record impact
Aligned with ADR-0019. The fix must read typed AiConfig leaves directly; no hidden defaults, optional chaining, runtime env reads, or pass-through config objects.
Acceptance Criteria
Out of Scope
- Changing REM input chunking strategy.
- Silently truncating REM input.
- Provider model selection or parallelism tuning.
- Cloud deployment docs.
Related
#12073 — input-side chunking-aware Tri-Vector work, now closed.
- PR
#13976 — map-reduce Tri-Vector extraction.
- PR
#13966 — topology chunking by turn.
- PR
#13947 — separate Ollama embedding truncation handling.
Origin Session ID: 2d993feb-ea2f-4468-8fbd-c53e62365f4d
Retrieval Hint: "LMS REM structured output overrun 2d993feb Tri-Vector chunk 2 144k"
Context
A live REM backlog drain hit an LMS over-cap generation during Tri-Vector extraction even after the turn-boundary chunker work from
#12073landed.Evidence from the active run:
2d993feb-ea2f-4468-8fbd-c53e62365f4d(FleetManager Provisioning and WriteGuard Enforcement Verification) has192raw turns,500586chars, and an estimated/3size of ~`168k` tokens.70.5kprompt tokens, below the configured LMS chat safe band.144kgenerated-context accounting and continued growing, with no graph commit/topology line written.Live latest-open sweep: checked latest 20 open issues at 2026-06-25T00:45Z; no equivalent ticket found. A2A claim sweep: checked latest 30 messages at 2026-06-25T00:45Z; no equivalent in-flight claim found. Release classification: boardless urgent infrastructure hardening; blocks REM backlog drain but is not a release-board scope expansion.
The Problem
The REM input-side guardrail is now doing its job: it chunks by session turn boundary and keeps each prompt under the configured safe band. The remaining failure is output-side.
SemanticGraphExtractorasks the LMS/OpenAI-compatible provider for schema-constrained JSON, but the provider wrapper streams until LMS reports completion and does not enforce a structured-output token budget.That means a valid-sized REM chunk can still burn the chat model context through excessive generated JSON, schema rambling, or provider finish metadata loss.
The Architectural Reality
ai/services/graph/SemanticGraphExtractor.mjscallsprovider.generate()for Tri-Vector schema extraction.ai/provider/OpenAiCompatible.mjsaccepts arbitrary OpenAI-compatible payload keys, so an LMSmax_tokens/max_completion_tokenscap can be passed through, but the current caller does not provide one.{content, raw: {message: {content}}}. It dropsfinish_reason, so length completion cannot be classified bySemanticGraphExtractor.getCompletionFinishReason().The Fix
AiConfig.localModels.chatleaf for graph structured-output budget.OpenAiCompatible.generate()so length/limit finishes fail loud instead of entering repair loops or silent non-commit states.Contract Ledger Matrix
AiConfig.localModels.chatOpenAiCompatible.generate()raw metadatafinish_reason='length'detectionDecision Record impact
Aligned with ADR-0019. The fix must read typed
AiConfigleaves directly; no hidden defaults, optional chaining, runtime env reads, or pass-through config objects.Acceptance Criteria
OpenAiCompatible.generate()preserves provider finish metadata from streaming responses.finish_reasonequivalent to length/max-token termination is classified as terminal context/output overflow before JSON repair.Out of Scope
Related
#12073— input-side chunking-aware Tri-Vector work, now closed.#13976— map-reduce Tri-Vector extraction.#13966— topology chunking by turn.#13947— separate Ollama embedding truncation handling.Origin Session ID:
2d993feb-ea2f-4468-8fbd-c53e62365f4dRetrieval Hint: "LMS REM structured output overrun 2d993feb Tri-Vector chunk 2 144k"