One occupancy metric; ineffective compaction must not report success; the ring reflects ongoing compaction; the panel supports immediate compaction; automatic and manual compaction share four system-message variants.
Occupancy and compaction
For implementers of the occupancy ring, /context panel, and compaction pipeline. The ring, panel, and compaction report read the same view: what the model actually receives next turn. Compaction does not move HEAD, so an unchanged HEAD does not mean an unchanged graph.
Scope
Do not change insert_summary_node semantics that retain old turns. No idle auto-compaction or DAG rendering changes. snip still trims only history for the outgoing request and does not write back to the DAG.
01 · One measurement basis
The model reads rendered_history: the currently effective summary plus the uncovered tail. The ring, panel, and compaction report all measure this view.
session_stats.compute_breakdown accepts only rendered_history. Count Messages after folding, including thinking/tool blocks. This supplies the ring and panel breakdown.
tokens_before / tokens_after uses totals from the same entry point (estimate_total_used), rather than estimate_history_tokens.
Compaction does not move HEAD. The occupancy cache cannot depend on HEAD alone. A graph change, including persisted compaction, requires recomputation instead of retaining pre-compaction measured/category snapshots.
/compact may produce a 40.7k report while the panel still shows Messages 125k. This means the cache retained pre-folding measurements, not that the summary was not stored.
Views used by each consumer
| Path | View | Rule |
|---|---|---|
session_stats.compute_breakdown |
rendered_history |
The sole occupancy metric, used by both ring and panel. |
engine.compact |
Totals from the same metric | Estimate once before and once after compaction, writing to compaction_finished. |
refresh_context_stats |
Recompute breakdown and total | Run after successful compaction or completion of manual compaction. Change basis to estimated. |
GET /api/sessions/{id}/context |
Current occupancy snapshot | Reuse snapshots only when the graph is unchanged. Compaction changes the graph even when HEAD does not move. |
session_context_stats measured reuse |
Last actual request | Retain only when HEAD is unchanged and the occupancy generation is unchanged. Compaction increments the generation. |
_get_messages / get_branch |
Unfolded branch | Chat-history cache, not occupancy. Occupancy calculations must not use this path. |
02 · When compaction counts as successful
A session has at most one active summary. Subsequent compaction replaces it rather than stacking summaries. The DAG is append-only: old nodes remain and HEAD does not move.
Build the prompt from rendered history and calculate budget_pct
The LLM writes a summary; append a summary node
Trim outgoing history only if the summary still exceeds the limit
This request reads the summary and tail
Four outcomes
| Outcome | Condition | Event |
|---|---|---|
| Started | The decision to call the summary LLM is made | compaction_started |
| Succeeded | Summary stored, summarised_count > 0 |
compaction_finished(without no_op) |
| Nothing to compact | rendered is already ≤ the retained tail, or cut_idx ≤ 0 / covers zero messages / insert returns None |
compaction_finished with no_op: true. If known before the LLM call, emit no started event and make no summary request. |
| Failed | Summary-call or pipeline exception | compaction_failed |
/compact or the panel button; budget ≥ 80% before a request; and recovery from 413 / prompt_too_long. Color the ring by occupancy ratio without a separate suggestion state.
03 · Ring lifecycle
Neutral base ring with a progress arc colored by occupancy ratio. Click to open the panel. Numbers use the same metric.
compaction_started through finished/failed: show the ring’s busy state and disable the panel button.
The panel footer offers “Compact now”. Clicking sends {action:"compact", session_id}, using the same WS path as slash command /compact. Disable it when no session exists or compaction is running.
04 · Four system-message variants
Automatic and manual compaction use the same chat-handlers.ts branch. Messages are bilingual.
| Event | English | Chinese |
|---|---|---|
| started | Compacting context… | Compacting context… |
| finished | Context compacted: covered N older messages, X → Y tokens | Context compacted: covered N old messages, X → Y tokens |
| failed | Context compaction failed[: error] | Context compaction failed[: error] |
finished + no_op |
Context is already compacted; no older messages to fold. | The current context is already compacted; no old messages remain to compact |
Acceptance
- After successful compaction, the ring and panel totals drop substantially and use the same basis as
compaction_finished.tokens_after. - Re-compacting an already compacted session does not call the summary LLM. The system message reports nothing to compact, rather than “covered 0 messages, X → X”.
- Neither low nor high occupancy shows a suggestion border. The progress arc remains ratio-colored. Clicking “Compact now” sends compact.
- During compaction the ring is busy and the button disabled; completion restores the normal state.
Files
Page path: docs/reference/design/context/occupancy-status.html
Implementation status
The body describes the target design. Implementation progress:
- Immediate execution of optional slash parameters, no-session feedback, and started/succeeded/failed system messages: implemented.
- Automatic compaction followed by snip: implemented.
- One metric (post-fold counting, post-compaction refresh, and the same basis as tokens_after): implemented.
- The no_op event and fourth system-message variant: implemented.
- Ring busy state and immediate panel compaction: implemented. The ring has no suggestion border.
- Idle auto-compaction: excluded.