Context · Occupancy & compaction

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.

Audience: implementers The /compact slash command can already be sent Idle auto-compaction: product decision pending; not implemented

Scope

Included

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.

Excluded

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.

Counting entry point

session_stats.compute_breakdown accepts only rendered_history. Count Messages after folding, including thinking/tool blocks. This supplies the ring and panel breakdown.

Compaction report

tokens_before / tokens_after uses totals from the same entry point (estimate_total_used), rather than estimate_history_tokens.

Server cache

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.

Compacting an already compacted session with /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

PathViewRule
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.

prepare
Build the prompt from rendered history and calculate budget_pct
→
Compact if ≥ 80%
The LLM writes a summary; append a summary node
→
snip fallback
Trim outgoing history only if the summary still exceeds the limit
→
Prepare again
This request reads the summary and tail

Four outcomes

OutcomeConditionEvent
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
Three triggers remain: manual /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

Normal

Neutral base ring with a progress arc colored by occupancy ratio. Click to open the panel. Numbers use the same metric.

Compacting

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.

EventEnglishChinese
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

  1. After successful compaction, the ring and panel totals drop substantially and use the same basis as compaction_finished.tokens_after.
  2. 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”.
  3. Neither low nor high occupancy shows a suggestion border. The progress arc remains ratio-colored. Clicking “Compact now” sends compact.
  4. During compaction the ring is busy and the button disabled; completion restores the normal state.

Files

openprogram/context/ ├── session_stats.py occupancy metric (rendered_history) ├── engine.py compact / no_op / tokens_before|after ├── summarize.py find_cut_index; may short-circuit before the LLM call └── persistence.py rendered_history; insert retains old turns apps/server/openprogram_server/ ├── server.py occupancy generation, refresh, measured reuse └── _webui/ ├── ws_actions/chat.py refresh after manual compact completes ├── _execute/chat.py refresh after automatic compaction └── routes/tree.py GET /context apps/web/ ├── components/chat/context-badge.tsx ├── components/chat/context-breakdown-panel.tsx └── lib/runtime-bridge/chat-handlers.ts

Page path: docs/reference/design/context/occupancy-status.html

Implementation status

The body describes the target design. Implementation progress: