Context
A live CPU deployment surfaced a prevention gap behind the stuck-Ollama recovery work: SessionService.summarizeSession() can send a very large raw session prompt into a local chat model because localModels.chat.safeProcessingLimitTokens is currently sized as a workload floor for high-context local deployments, not as a CPU-safe raw-synthesis cap.
Grace's recovery PR #13900 is the safety net for a runner that is already stuck. This ticket is the prevention half: avoid starting the pathological raw session-summary call when the existing compact fallback can produce an explicit degraded navigation summary instead.
Live latest-open sweep: checked latest 20 open issues at 2026-06-23T01:09:25Z; closest open hits were #13835, #13852, #13851, #13882, and #12073. None owns this narrow SessionService raw-synthesis cap.
A2A in-flight sweep: checked latest 30 messages at 2026-06-23T01:09:25Z; no competing [lane-claim] / [lane-intent] for this scope.
Historical/semantic sweep: KB and resources/content/** surfaced closed/adjacent #12819 and #13590 plus #13835. #12819 delivered the degraded fallback; #13590 handled summary/backfill scheduling prerequisites; #13835 handles repeated re-serving of un-digestible sessions. This ticket is the missing pre-call cap that chooses the already-existing degraded route before a CPU-hostile raw call starts.
The Problem
Current source reality:
ai/services/memory-core/SessionService.mjs joins every raw memory document into aggregatedContent and builds one summary prompt from it.
- The raw prompt is sent through
runGuardrailed(buildSummaryPrompt(aggregatedContent), ...) using aiConfig.localModels.chat.contextLimitTokens and safeProcessingLimitTokens.
- The degraded fallback already exists, but it only runs after the raw attempt is skipped by the current safe band or times out.
ai/config.template.mjs documents localModels.chat.contextLimitTokens / safeProcessingLimitTokens as role-level local-model limits; the default 100000 safe band can still be far too expensive for CPU raw synthesis.
That means an under-band-but-too-large-for-CPU session can enter the raw model call and monopolize the local runner for hours. Recovery can recycle the runner afterward, but prevention should avoid launching that call in the first place.
The Architectural Reality
SessionService.summarizeSession() owns session-summary source selection and summary metadata (sourceTier, degraded, rawCanonical).
consumerFrictionHelper.bytesToTokens() and invokeWithGuardrail() own the current 4-bytes/token estimate and friction envelope.
aiConfig.localModels.chat.* is the existing role-level context source of truth per ADR 0019. New config must be a leaf read at the use site, not a re-derived env lookup or runtime mutation.
- #12073 remains the deeper hierarchical summarization lane. This ticket must not implement map/reduce chunking or semantic-fidelity research.
The Fix
Add a session-summary-specific raw-synthesis cap or equivalent CPU/local policy before the raw model call. When the estimated raw summary prompt exceeds that cap, skip raw synthesis and directly build the existing compact degraded input from per-turn miniSummary rows, falling back to truncated raw snippets exactly as the current fallback does.
The preferred implementation shape is small:
- Reuse
bytesToTokens() or the same estimator used by invokeWithGuardrail().
- Add an env-backed
AiConfig leaf for the session-summary raw cap, or a clearly documented derived policy that can be overridden per deployment.
- Preserve raw one-shot summaries for small sessions and explicitly high-context deployments.
- Preserve
sourceTier, degraded, and rawCanonical metadata so consumers can distinguish raw summaries from degraded navigation indexes.
- Keep graph extraction on raw data; do not feed degraded session summaries into graph extraction as canonical evidence.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
SessionService.summarizeSession() raw-source selection |
This ticket; #12819 degraded fallback; current SessionService JSDoc |
Below the raw cap, use the existing raw summary path. Above the raw cap, bypass the raw model call and use the compact degraded source before any local chat invocation. |
If compact entries are absent or compact synthesis fails, preserve the existing null/failure behavior and friction logging; do not silently claim a raw summary. |
Update method comments around raw/degraded routing. |
Unit test proving over-cap raw does not call the raw model path and persists a degraded summary. |
| Session-summary raw cap config |
ADR 0019; ai/config.template.mjs local model role limits |
Provide an env-backed cap or equivalent deployment policy for session-summary raw synthesis, read via AiConfig at the use site. |
Operator can raise/disable the cap for high-context hosts; invalid config should fail loudly or fall back to the existing guardrail contract, not mutate globals. |
Config leaf JSDoc and template docs. |
Unit/config test for default and env override if a new leaf is added. |
| Summary provenance metadata |
#12819; SessionService summary metadata |
Pre-routed degraded summaries keep sourceTier as miniSummary or truncatedRaw, degraded: true, and rawCanonical: true. Raw summaries remain sourceTier: raw, degraded: false. |
Mixed miniSummary availability keeps the current best-effort behavior. |
Existing metadata comments stay accurate. |
Extend SessionSummaryDegradedFallback.spec.mjs. |
| Consumer-friction visibility |
consumerFrictionHelper visibility contract |
The pre-route should remain observable as an oversized/raw-cap decision through existing friction or an equivalently structured log/metadata note. |
Do not start the raw LLM call merely to get observability. |
Comment near the pre-route branch. |
Test or static assertion for the emitted note/metadata, depending on chosen seam. |
Decision Record impact
Aligned with ADR 0019: read resolved config leaves at the use site; do not re-implement env resolution or mutate AiConfig. No ADR amendment expected. If the implementation changes the broader local-model context contract rather than adding a session-summary-specific cap, re-evaluate ADR impact before PR.
Acceptance Criteria
Out of Scope
- Full hierarchical chunk-map/reduce summarization and semantic-fidelity research (#12073).
- Stuck-runner recovery after a runner is already wedged (#13900 / #13882).
- Digest-state exclusion for repeatedly un-digestible REM sessions (#13835).
- Model unload/eject lifecycle or Ollama self-load behavior.
Avoided Traps
- Do not lower the global chat safe band blindly if that breaks graph extraction or high-context hosts.
- Do not detect host RAM dynamically; the existing config says per-host tuning is explicit env policy.
- Do not treat the degraded summary as canonical graph evidence. It is a navigation index over raw canonical turns.
- Do not claim #12073 is solved by this prevention cap.
Related
- #13900 / #13882 — recovery half for resident-but-not-serving Ollama runner.
- #12073 — deeper hierarchical summarization lane.
- #12819 — delivered degraded session-summary fallback.
- #13590 — closed adjacent scheduling/prerequisite ticket.
- #13835 — adjacent repeated re-serve load-reducer.
- #13852 / #13851 — local model lifecycle/context readiness lanes.
Release classification: boardless operational fire-relief. Promote to Project 12 only if the operator classifies this as a release gate.
Retrieval Hint: query_raw_memories("CPU context cap oversized session summary raw synthesis gemma Ollama stuck runner prevention"); SessionService.summarizeSession raw cap miniSummary truncatedRaw safeProcessingLimitTokens.
Context
A live CPU deployment surfaced a prevention gap behind the stuck-Ollama recovery work:
SessionService.summarizeSession()can send a very large raw session prompt into a local chat model becauselocalModels.chat.safeProcessingLimitTokensis currently sized as a workload floor for high-context local deployments, not as a CPU-safe raw-synthesis cap.Grace's recovery PR #13900 is the safety net for a runner that is already stuck. This ticket is the prevention half: avoid starting the pathological raw session-summary call when the existing compact fallback can produce an explicit degraded navigation summary instead.
Live latest-open sweep: checked latest 20 open issues at 2026-06-23T01:09:25Z; closest open hits were #13835, #13852, #13851, #13882, and #12073. None owns this narrow SessionService raw-synthesis cap. A2A in-flight sweep: checked latest 30 messages at 2026-06-23T01:09:25Z; no competing
[lane-claim]/[lane-intent]for this scope. Historical/semantic sweep: KB andresources/content/**surfaced closed/adjacent #12819 and #13590 plus #13835. #12819 delivered the degraded fallback; #13590 handled summary/backfill scheduling prerequisites; #13835 handles repeated re-serving of un-digestible sessions. This ticket is the missing pre-call cap that chooses the already-existing degraded route before a CPU-hostile raw call starts.The Problem
Current source reality:
ai/services/memory-core/SessionService.mjsjoins every raw memory document intoaggregatedContentand builds one summary prompt from it.runGuardrailed(buildSummaryPrompt(aggregatedContent), ...)usingaiConfig.localModels.chat.contextLimitTokensandsafeProcessingLimitTokens.ai/config.template.mjsdocumentslocalModels.chat.contextLimitTokens/safeProcessingLimitTokensas role-level local-model limits; the default100000safe band can still be far too expensive for CPU raw synthesis.That means an under-band-but-too-large-for-CPU session can enter the raw model call and monopolize the local runner for hours. Recovery can recycle the runner afterward, but prevention should avoid launching that call in the first place.
The Architectural Reality
SessionService.summarizeSession()owns session-summary source selection and summary metadata (sourceTier,degraded,rawCanonical).consumerFrictionHelper.bytesToTokens()andinvokeWithGuardrail()own the current 4-bytes/token estimate and friction envelope.aiConfig.localModels.chat.*is the existing role-level context source of truth per ADR 0019. New config must be a leaf read at the use site, not a re-derived env lookup or runtime mutation.The Fix
Add a session-summary-specific raw-synthesis cap or equivalent CPU/local policy before the raw model call. When the estimated raw summary prompt exceeds that cap, skip raw synthesis and directly build the existing compact degraded input from per-turn
miniSummaryrows, falling back to truncated raw snippets exactly as the current fallback does.The preferred implementation shape is small:
bytesToTokens()or the same estimator used byinvokeWithGuardrail().AiConfigleaf for the session-summary raw cap, or a clearly documented derived policy that can be overridden per deployment.sourceTier,degraded, andrawCanonicalmetadata so consumers can distinguish raw summaries from degraded navigation indexes.Contract Ledger Matrix
SessionService.summarizeSession()raw-source selectionSessionServiceJSDocai/config.template.mjslocal model role limitsAiConfigat the use site.SessionServicesummary metadatasourceTierasminiSummaryortruncatedRaw,degraded: true, andrawCanonical: true. Raw summaries remainsourceTier: raw,degraded: false.SessionSummaryDegradedFallback.spec.mjs.consumerFrictionHelpervisibility contractDecision Record impact
Aligned with ADR 0019: read resolved config leaves at the use site; do not re-implement env resolution or mutate
AiConfig. No ADR amendment expected. If the implementation changes the broader local-model context contract rather than adding a session-summary-specific cap, re-evaluate ADR impact before PR.Acceptance Criteria
AiConfigsingleton in tests or runtime.sourceTier,degraded, andrawCanonicalmetadata in both Chroma summary metadata and graph node properties.Out of Scope
Avoided Traps
Related
Release classification: boardless operational fire-relief. Promote to Project 12 only if the operator classifies this as a release gate.
Retrieval Hint:
query_raw_memories("CPU context cap oversized session summary raw synthesis gemma Ollama stuck runner prevention");SessionService.summarizeSession raw cap miniSummary truncatedRaw safeProcessingLimitTokens.