Repository navigation
plan: A4 compaction — phase 5 — living docs (source #552) #952
Description
Plan header
| Field | Value |
|---|---|
| Status | IMPLEMENTED — PR #957 (open, not merged) |
| Date | 2026-09-05 |
| Type | phase 5 |
| Parent | #947 — #947 |
| Source issue | #552 — #552 (A4 compaction engine) |
| Layers | Living docs only |
| Reusability impact | none — describe the existing per-session, selected-model behavior without origin-specific configuration |
| Production mutate? | no — documentation only; no schema, Redis, Blob data, environment, secret, or deployed-runtime change |
| Cloud ops path | N/A — no Production mutate |
| Living docs | docs/session-model.md, docs/harness-limits.md, docs/feature-divide.md, AGENTS.md; README / SECURITY / .env.example reviewed N/A below |
Review notes (2026-09-07)
| # | Finding | Severity | Resolution |
|---|---|---|---|
| 1 | The limits list omitted the shipped combined start() compact-argument ceiling, and did not distinguish the 2.25 MiB checkpoint object ceiling from the 2 MiB span/input ceiling. |
Major | Document all six existing compaction limits, including COMPACTION_START_MAX_BYTES; lock their current values and carriers in the caps table. No value changes. |
| 2 | The proposed simple seed order “checkpoint → modelMessages → legacy sidecar” would be inaccurate after a successful compact: a valid live model-messages projection beginning with the honesty row is preferred so a later non-compacting turn retains post-compact work. | Major | Lock the exact selector: honest live model-messages projection → valid checkpoint → ordinary model-messages projection → legacy promptHistory; all Blob ids are session-bound and reads re-validate/re-pair. |
| 3 | “Markdown renders; links resolve” was not an implementable verification plan, and did not require the repository gates. | Minor | Add source-to-doc semantic review, link/diff review, and the required cloud-workspace/CI typecheck, full test, and build gates. |
Verdict: HANDOFF-READY — all findings are resolved in this issue. Phase 4 is merged through PR #956, so this phase documents shipped behavior only. No cap is added, raised, or lowered; no human cap decision is required.
Intent lock
In scope: capture the shipped compaction behavior in the four living-document surfaces assigned by the parent. The text must be timeless product/agent truth: it explains what the current system does, its ownership boundaries, data safety, and existing limits without phase/issue history.
Out of scope: application code, tests, migrations, deployment configuration, schema/Redis/Blob data changes, cap changes, and visitor-front-door or secret-policy changes.
Forbidden: phase/issue theater in product docs; “see issue #NN” as an explanation; a simplified seed-order statement that loses the honest-live-projection preference; laptop-primary ops notes; describing compaction as deleting transcript or model-history objects; suggesting that the Wasm harness or DOM host runs the summarizer.
Depends on
Phase 4 is merged (PR #956), following merged Phases 1–3. The documentation therefore describes current main behavior, not an intended or speculative design.
Current baseline (live code @ main)
| Claim | Path / symbol | Notes |
|---|---|---|
| Trigger and seed selection occur before durable workflow start | app/api/turns/route.ts |
Reads bound checkpoint/model-message objects, applies the pre-trim trigger, and passes serializable compact args only |
| Compaction is a server-side first workflow step | lib/workflows/compactionStep.ts, lib/workflows/turnLoop.ts |
Same selected model, BYOK re-resolved in-step, tools off; no DOM/Wasm summarizer |
| Checkpoint shape, honesty row, cut/re-pair, and payload rails | lib/agent/compaction.ts |
{summary, filesTouched, retainedTail}; user-role honesty row; logical compaction does not delete Blob history |
| Pointer carrier, validation, and host-omit copy-forward | lib/sessions/sessionStore.ts |
meta.compactionPointer is a reserved session-bound Blob pointer, copied forward by envelope upsert when the host omits it |
| Current compaction caps | lib/sessionCloudCaps.ts |
Six existing values in the caps table below; no value changes in this phase |
| Current session/Blob and host responsibilities | docs/session-model.md, docs/feature-divide.md, AGENTS.md |
These docs currently cover the model-messages projection and its rails but need compaction’s exact durable semantics |
Documentation design
docs/session-model.md — new timeless Compaction section
Document the model-facing compaction lifecycle alongside the existing durable-turn model-messages section:
- Checkpoint carrier and durability.
meta.compactionPointeris only a Redis-safe, session-bound object id. Its Blob object holds{ summary, filesTouched, retainedTail }; the checkpoint body never rides envelopemeta. A host flatten envelope PUT omits this worker-authored key, and the store copy-forwards the existing valid value; a new successful compaction replaces it. The transcript and earlier model-messages objects remain retained in Blob — compaction changes the model seed, not stored history. - Trigger and cut. At
POST /api/turns, the backend resolves the selected model’s joined catalog window and its existing fold budget, evaluates the pre-trim seed, and compacts only when that seed exceeds the budget and a safe user-turn boundary exists. The cut is re-paired so an assistant tool call cannot be separated from its tool result. If there is no legal cut, the established route trim remains the fail-open fallback; no turn is blocked. - Server-only summary. The Workflow runs one tools-off summarizer step before the first normal model round, with the selected model and in-step authorization. The Wasm harness and DOM host do not summarize, receive credentials, or gain a new bridge/protocol surface. A failed, empty, or unusable summary fails open to the ordinary bounded seed and must not create a fabricated checkpoint.
- Exact next-turn seed selection. Validate every pointer against the requesting
{tenantId, userId, sessionId}and re-validate/re-pair decoded rows. Prefer a valid model-messages object when its first row is the labeled compaction honesty row (the post-compact live warehouse, preserving work done after compaction); otherwise prefer a valid checkpoint rendered as[summaryRow, ...retainedTail]; then use an ordinary valid model-messages projection; finally fall back to the host’s legacypromptHistorysidecar. Unbound, missing, malformed, or invalid objects do not seed another session and fall through safely. - Honesty. The summary is a
user-role row beginning exactlySummary of earlier session (compacted, not live assistant prose):; it is context, never a claim that the assistant just said it live.filesTouchedsupplies the boundedFiles read/modified:context.
Update the reserved-meta/key table and full-replace exception text to include compactionPointer with the other worker-authored pointers, including its clear/copy-forward behavior. Keep explanations timeless; do not mention plans, phases, PRs, or issue numbers.
docs/harness-limits.md — compaction limits rows
Add a Compaction limits row/table near the existing durable model-message rails. State units, carrier, and behavior, not only constant names. Distinguish a Blob-object ceiling from workflow-start-argument and summarizer-input ceilings. Cross-reference the existing model window/fold-budget and model-seed rails rather than duplicating their values.
docs/feature-divide.md — ownership table
Add a durable-turn/compaction ownership row, or extend the current durable-turn row, that makes the split explicit: Vercel backend reads the bound seed, decides/cuts, runs the server-side workflow summary, and writes the checkpoint; DOM remains the thin submit/bridge host; Wasm remains the primary transcript/composer and never runs the summary or accesses Blob/Redis/Gateway credentials. Preserve the no-dual-chat rule.
AGENTS.md — timeless “Where to change” entry
Add one compact, durable table row covering the compaction seam: lib/agent/compaction*.ts, app/api/turns/route.ts, lib/workflows/{compactionStep,turnLoop}.ts, lib/agent/turnPersistSeam.ts, and lib/sessions/sessionStore.ts. State the pre-trim selected-model trigger, server-only tools-off step, session-bound meta.compactionPointer, honest summary row, copy-forward, and logical-not-physical deletion rules. Reference the docs by name, not phase/issue history. Do not duplicate the full implementation map already present elsewhere in AGENTS.
Other durable surfaces reviewed
| Surface | Decision | Rationale |
|---|---|---|
README.md |
N/A | Visitor entry, setup, and top-level feature taxonomy do not change; the compaction mechanism is an internal durable-turn continuity detail covered by session and architecture guides. |
SECURITY.md |
N/A | No new secret, client exposure, endpoint authorization class, or trust boundary: the checkpoint uses the existing session-bound Blob/envelope pattern. Existing session security language already covers server-only, bound object reads. |
.env.example |
N/A | No environment variable or configuration seam is introduced. |
Caps table
This documentation phase adds and changes no cap. It documents these already-shipped values exactly; the existing CONTEXT_* fold-budget and MODEL_MSG_SEED_* rails remain documented at their own locations and are not redefined here.
| Existing cap / ceiling | Current value | Rationale / carrier | Code location |
|---|---|---|---|
COMPACTION_RESERVE_TOKENS |
16,384 tokens | Name for the existing completion reserve already accounted for by foldBudgetTokens; the trigger must not subtract it twice |
lib/sessionCloudCaps.ts, lib/agent/compactionBudget.ts |
COMPACTION_SUMMARY_MAX_CHARS |
8,000 characters | Bounded persisted summary with explicit truncation marker | lib/sessionCloudCaps.ts, lib/agent/compaction.ts |
COMPACTION_FILES_TOUCHED_MAX |
256 paths | Bounded newest-first checkpoint file list with an honest omitted-count marker | lib/sessionCloudCaps.ts, lib/agent/compaction.ts |
COMPACTION_CHECKPOINT_MAX_BYTES |
MODEL_MSG_SEED_MAX_BYTES + 256 KiB = 2.25 MiB |
Own session-bound checkpoint Blob ceiling; it accommodates the legal retained tail plus summary/file metadata and is not envelope meta or a Function body |
lib/sessionCloudCaps.ts, lib/agent/turnPersistSeam.ts |
COMPACTION_SPAN_MAX_BYTES |
2 MiB | Serialized input ceiling for the pre-loop summarizer; cut walk can move to an older boundary or prefix-clip rather than send an unbounded span | lib/sessionCloudCaps.ts, lib/agent/compaction.ts |
COMPACTION_START_MAX_BYTES |
3 MiB | Combined serialized compact start() arguments ceiling (span + retained tail + metadata), leaving margin under the Vercel Function wire |
lib/sessionCloudCaps.ts, app/api/turns/route.ts |
Cap governance: all values above already exist on main. This plan only documents them; it raises, lowers, adds, and removes none. No human cap decision is requested.
Implementation order
- Update
docs/session-model.mdwith the exact durable checkpoint, trigger, fail-open, seed-selection, honesty, and copy-forward semantics. - Add the six existing compaction-cap rows to
docs/harness-limits.md, cross-linking existing model-window/seed rails instead of duplicating or changing them. - Update the
docs/feature-divide.mdownership table to place compaction fully on the Vercel backend and explicitly exclude Wasm/DOM summarization. - Add one concise timeless compaction-seam entry to the AGENTS “Where to change” table.
- Review the complete documentation diff for source accuracy, relative-link correctness, no phase/issue/process language, and consistency across all four surfaces; then run the required repository gates in a cloud agent workspace or CI.
Testing / verification
| # | Case | Layer | Type | Command / method |
|---|---|---|---|---|
| 1 | Every lifecycle claim matches the current route, loop, compaction helper, session-store, and cap source; exact honest-live-model-messages preference is preserved | docs | source-to-doc review | Read and compare app/api/turns/route.ts, lib/workflows/{compactionStep,turnLoop}.ts, lib/agent/compaction*.ts, lib/sessions/sessionStore.ts, and lib/sessionCloudCaps.ts |
| 2 | Docs state server-only ownership, session-bound reads, no secrets/client/Wasm summary, logical-not-physical deletion, and all six limits with correct units/carriers | docs | content review | Review the four edited rendered Markdown files together |
| 3 | All new/changed relative Markdown links resolve and no new text refers to phase/issue/PR process history | docs | diff/link review | Inspect rendered relative links and run git diff --check |
| 4 | Required repository gates | all | gate | npm run typecheck, npm test, npm run build in a cloud agent workspace or CI |
Definition of done
-
docs/session-model.mdaccurately documents checkpoint carrier, session binding, pre-trim trigger, safe/re-paired cut, fail-open behavior, honest summary row, exact seed selector, and logical compaction without physical deletion -
docs/harness-limits.mddocuments all six existing compaction limits with units, code locations, and Blob/workflow carrier distinction; no cap value changed -
docs/feature-divide.mdassigns compaction entirely to the Vercel backend and preserves Wasm-primary/no-dual-chat ownership -
AGENTS.mdhas one concise, timeless compaction seam entry includingmeta.compactionPointerand copy-forward rules - The documentation contains no phase/issue/PR narrative;
README.md,SECURITY.md, and.env.exampleremain N/A for the reasons above - Source-to-doc review, link/diff review,
npm run typecheck,npm test, andnpm run buildare green in a cloud agent workspace or CI and recorded in the PR - Parent Phase 5 and parent living-doc DoD items can be checked off after the merged documentation PR
- Cloud ops: N/A — no Production mutate
Parent adherence
| Parent Phase 5 deliverable | This phase locks |
|---|---|
docs/session-model.md |
Timeless lifecycle and exact selector documentation |
docs/harness-limits.md |
Existing compaction caps, including the combined-start rail omitted from the initial draft |
docs/feature-divide.md |
Backend-only summary/seed ownership; Wasm remains display/composer only |
AGENTS.md |
Timeless source-of-truth change map and pointer ownership guidance |
| No code/test work | Documentation only; verification is source review plus standard repository gates |
Corrections / refinements vs parent
| Topic | Parent said | This phase locks |
|---|---|---|
| Checkpoint seed wording | “checkpoint + retained tail” / fallback to model messages | Exact current selector: honest live model-messages projection first; otherwise checkpoint; then ordinary model messages; then legacy sidecar. This preserves post-compact work without weakening scope binding. |
| Checkpoint object ceiling | Parent cap index retained an early 1 MiB value | Current main value is the existing 2.25 MiB expression (MODEL_MSG_SEED_MAX_BYTES + 256 KiB); docs correct the description only, not the cap. |
| Transport documentation | Parent names transport safety generally | Document the existing 3 MiB combined compact-start rail in addition to the separate 2 MiB span and 2.25 MiB Blob checkpoint rails. |
| Documentation style | Four surfaces plus N/A rows | Identical ownership; all new prose is timeless and contains no phase/issue/PR narrative. |
IMPLEMENTED (2026-09-07) — PR #957: #957
All locked doc rows shipped in the four parent surfaces: docs/session-model.md (timeless Compaction section + compactionPointer reserved-key carrier), docs/harness-limits.md (all six existing caps incl. COMPACTION_START_MAX_BYTES), docs/feature-divide.md (backend-only ownership), AGENTS.md (one compaction-seam row). Source-to-doc review done against live main (seed preference route.ts:498–505, caps sessionCloudCaps.ts:631–721, fail-closed checkpoint write, fail-open loop). Gates: typecheck clean, full vitest 177 files / 3527 tests green, npm run build green in this cloud workspace; git diff --check clean; all new relative links resolve. No caps changed; no Production mutate. Awaiting adversarial review.
Activity
Merge pull request #957 from btipling/plan/a4-compaction-p5-living-docs