Skip to content

Add session context compaction flow - #166

Merged
gnanam1990 merged 7 commits into
mainfrom
feat/context-compaction-system
Jun 11, 2026
Merged

Add session context compaction flow#166
gnanam1990 merged 7 commits into
mainfrom
feat/context-compaction-system

Conversation

@Vasanthdev2004

@Vasanthdev2004 Vasanthdev2004 commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds metadata-bearing agent compaction results and preserves active plan, loaded tools/skills, and project instructions across repeated compactions.
  • Adds persisted session compaction events plus rehydrated replay so resume/fork prompts replace compacted-away history with a summary.
  • Wires /compact in the TUI to perform real session compaction, using the active provider for a compact summary when available, and adds a conservative model-switch compaction guard.

Why

Long Zero sessions already had automatic agent compaction, but manual/session replay compaction was still mostly a shell. This makes /compact affect the actual future context path instead of only reporting status.

Validation

  • go test ./...
  • go vet ./...
  • go run ./cmd/zero-release build
  • go run ./cmd/zero-release smoke

Notes

  • Full session event logs remain available via ReadEvents; prompt replay uses ReadRehydratedEvents/ReadReplayEvents so compacted raw events are not re-sent.
  • Provider-backed /compact records only the returned summary; no-provider sessions use a deterministic fallback summary.

Summary by CodeRabbit

  • New Features

    • Rich session compaction: preserved structured state (plans, tools/schemas, skills, project instructions), persistent compaction events, replay/rehydration, model/mode compaction gating, /compact UI with status, and a pluggable session compactor API.
  • Bug Fixes

    • Validation for missing summarizers, trimmed/injected summaries, explicit no-op handling, clearer error propagation, and safer fallback when compaction payloads are malformed.
  • Tests

    • Expanded unit/integration coverage for compaction metadata, preserved-state behavior, replay/rehydration, and TUI compaction flows.

@Vasanthdev2004
Vasanthdev2004 marked this pull request as ready for review June 11, 2026 08:04
@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Zero automated PR review

Verdict: No blockers found

Blockers

  • None found.

Validation

  • [pass] Diff hygiene: git diff --check
  • [pass] Tests: go test ./...
  • [pass] Build: go run ./cmd/zero-release build
  • [pass] Smoke build: go run ./cmd/zero-release smoke

Scope

Head: b9b740b05a58
Changed files (15): internal/agent/compaction.go, internal/agent/compaction_metadata_test.go, internal/agent/compaction_preserve.go, internal/agent/compaction_preserve_test.go, internal/sessions/exec_session.go, internal/sessions/replay.go, internal/sessions/replay_test.go, internal/tui/command_center.go, internal/tui/model.go, internal/tui/model_test.go, internal/tui/options.go, internal/tui/rendering.go, and 3 more

This deterministic review checks validation status and basic diff hygiene. A human reviewer still owns product judgment and design quality.

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 33e52030-0d12-48ff-a596-c5f3ce663925

📥 Commits

Reviewing files that changed from the base of the PR and between 13cfd51 and b9b740b.

📒 Files selected for processing (2)
  • internal/sessions/exec_session.go
  • internal/sessions/replay_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/sessions/exec_session.go
  • internal/sessions/replay_test.go

Walkthrough

Adds a CompactionResult API and richer preserved-state handling, records compaction events with replay/rehydration that replaces compacted prefixes with a summary event, and integrates manual/provider and model-switch compaction flows and UI into the TUI with tests.

Changes

Agent Compaction Metadata and State Preservation

Layer / File(s) Summary
CompactionResult metadata API
internal/agent/compaction.go, internal/agent/compaction_metadata_test.go
Introduces exported CompactionResult and CompactMessages; Compact delegates to it. CompactMessages validates options, returns structured metadata for no-op and success cases, and propagates summarizer errors. Tests cover manual compaction, no-op, and error propagation.
Expanded preserved-state tracking: tools, plan, instructions
internal/agent/compaction_preserve.go, internal/agent/compaction_preserve_test.go
Preserved-state now includes plan, deferred tool_search schemas (tools), loaded skills, and project_instructions. Plan formatting accepts step/notes; tool/schema and instruction extraction, parsing, merge, and helper conversions updated; tests cover schemas, instructions, repeated compaction, plan aliases/notes, and skill argument aliasing.

Session Event Replay and Rehydration

Layer / File(s) Summary
Compaction event recording and payload construction
internal/sessions/replay.go
Adds RecordCompaction and CompactionPayload/RecordCompactionInput construction (summary, counts, compacted-through markers, event refs, prompt sizing, truncation).
Event rehydration and replay logic
internal/sessions/replay.go, internal/sessions/exec_session.go, internal/sessions/replay_test.go
Adds ReadRehydratedEvents/ReadReplayEvents, RehydrateEvents/ReplayEvents that detect EventCompaction and rebuild event lists by skipping compacted events and inserting the compaction event once. PrepareExec now reads rehydrated events (with fallback) and extractText prefers map summary entries; tests validate payloads, malformed-payload fallback, and rehydration behavior.

TUI Model-Switch Compaction Gate and Session Compaction Command

Layer / File(s) Summary
Compaction request policy and guard mechanism
internal/tui/command_center.go
Adds model-switch compaction request/decision types, policy interface, default threshold policy, and a package-level guard; integrates checks into /model and /mode handlers.
SessionCompactor interface and CompactRequest/CompactResult types
internal/tui/session_controls.go, internal/tui/options.go
Defines exported SessionCompactor, CompactRequest, and CompactResult; Options gains SessionCompactor to inject manual/async compaction handlers.
Compact command handler, session-backed flow, and rendering
internal/tui/session_controls.go, internal/tui/rendering.go, internal/tui/session.go
Reworks /compact to support /compact status, starts async compaction (injected or session-store path), records results via compactResultMsg, renders running/complete cards (renderCompactRunningCard, renderCompactCompleteCard), and adds transcript rows for persisted compaction summaries.
TUI wiring and tests
internal/tui/model.go, internal/tui/model_test.go, internal/tui/session_controls_test.go
Model stores compaction UI/execution state, blocks submission during in-flight compaction, advances compact UI on spinner ticks, applies compacted snapshots on completion; tests cover guard behavior, /compact flows (manual/provider/session-backed), spinner updates, and rendered status cards.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • gnanam1990
  • anandh8x
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.99% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main objective: adding a session context compaction flow to handle summarization and replay of long conversation histories.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/context-compaction-system

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
internal/tui/command_center.go (1)

155-159: 💤 Low value

Unused policy implementation flagged by linter.

The noopModelSwitchCompactionPolicy type and its BeforeModelSwitch method are unused. Consider one of:

  • Remove if not needed
  • Use in tests or production code
  • Document its purpose with a comment if it's intentional for future use or external injection
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/tui/command_center.go` around lines 155 - 159, The
noopModelSwitchCompactionPolicy type and its
BeforeModelSwitch(modelSwitchCompactionRequest) modelSwitchCompactionDecision
method are unused and should either be removed or documented/used; remove the
noopModelSwitchCompactionPolicy declaration and its method if it's not required,
or alternatively add a comment explaining it’s an intentional no-op for future
injection or wire it into the codebase (e.g., use
noopModelSwitchCompactionPolicy where a model switch compaction policy is
required) or add tests that instantiate noopModelSwitchCompactionPolicy to
justify keeping it.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/sessions/replay.go`:
- Around line 181-187: RecordCompaction currently permits an empty
input.Plan.SessionID which lets stray compaction plans be recorded; update the
validation in RecordCompaction to require that input.Plan.SessionID is non-empty
and exactly equals the provided sessionID (i.e., reject when
strings.TrimSpace(input.Plan.SessionID) == "" or when it != sessionID) and
return a clear error (referencing RecordCompaction and input.Plan.SessionID)
when the check fails.
- Around line 230-243: RehydrateEvents currently decodes every EventCompaction
in the events slice but only keeps the last result; change it to find the last
compaction event first and decode only that one to avoid failing on malformed
historical payloads: in function RehydrateEvents, instead of decoding inside the
forward loop, scan events from the end (or track the index of the last
EventCompaction) to locate the final compaction event and then call
decodeCompactionPayload just once for that event, assigning index and payload
only from that single decode.

---

Nitpick comments:
In `@internal/tui/command_center.go`:
- Around line 155-159: The noopModelSwitchCompactionPolicy type and its
BeforeModelSwitch(modelSwitchCompactionRequest) modelSwitchCompactionDecision
method are unused and should either be removed or documented/used; remove the
noopModelSwitchCompactionPolicy declaration and its method if it's not required,
or alternatively add a comment explaining it’s an intentional no-op for future
injection or wire it into the codebase (e.g., use
noopModelSwitchCompactionPolicy where a model switch compaction policy is
required) or add tests that instantiate noopModelSwitchCompactionPolicy to
justify keeping it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 99a93d00-d203-4b52-aad6-e8b69c4067aa

📥 Commits

Reviewing files that changed from the base of the PR and between f32d07b and f6f46af.

📒 Files selected for processing (14)
  • internal/agent/compaction.go
  • internal/agent/compaction_metadata_test.go
  • internal/agent/compaction_preserve.go
  • internal/agent/compaction_preserve_test.go
  • internal/sessions/exec_session.go
  • internal/sessions/replay.go
  • internal/sessions/replay_test.go
  • internal/tui/command_center.go
  • internal/tui/model.go
  • internal/tui/model_test.go
  • internal/tui/options.go
  • internal/tui/session.go
  • internal/tui/session_controls.go
  • internal/tui/session_controls_test.go

Comment thread internal/sessions/replay.go
Comment thread internal/sessions/replay.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/tui/model.go (1)

1454-1461: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing compactInFlight guard allows queued prompts to launch during compaction.

launchQueuedMessageIfReady checks m.pending and other modal states but not m.compactInFlight. If a prompt is queued while a run is active, then the run finishes and /compact is invoked, the queued message will launch during compaction when agentResponseMsg calls this function at line 786. This bypasses the guard at lines 1190–1196 and could corrupt compaction state or session/transcript snapshots.

🔒 Proposed fix to add compactInFlight check
 func (m model) launchQueuedMessageIfReady() (model, tea.Cmd) {
-	if !m.hasQueuedMessage() || m.pending || m.exiting || m.pendingPermission != nil || m.pendingAskUser != nil || m.pendingSpecReview != nil {
+	if !m.hasQueuedMessage() || m.pending || m.compactInFlight || m.exiting || m.pendingPermission != nil || m.pendingAskUser != nil || m.pendingSpecReview != nil {
 		return m, nil
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/tui/model.go` around lines 1454 - 1461, The
launchQueuedMessageIfReady function lacks a guard for compaction and can start
queued prompts during a compactInFlight; update launchQueuedMessageIfReady to
include m.compactInFlight in its initial conditional (alongside m.pending,
m.exiting, m.pendingPermission, m.pendingAskUser, m.pendingSpecReview) so it
returns without launching when a compaction is in flight, preventing queued
prompts from running during compaction and corrupting compaction/session state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@internal/tui/model.go`:
- Around line 1454-1461: The launchQueuedMessageIfReady function lacks a guard
for compaction and can start queued prompts during a compactInFlight; update
launchQueuedMessageIfReady to include m.compactInFlight in its initial
conditional (alongside m.pending, m.exiting, m.pendingPermission,
m.pendingAskUser, m.pendingSpecReview) so it returns without launching when a
compaction is in flight, preventing queued prompts from running during
compaction and corrupting compaction/session state.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e2b16134-3580-475b-a0a4-c0a9352580d1

📥 Commits

Reviewing files that changed from the base of the PR and between f6f46af and 7f60b62.

📒 Files selected for processing (3)
  • internal/tui/model.go
  • internal/tui/session_controls.go
  • internal/tui/session_controls_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/tui/session_controls.go
  • internal/tui/session_controls_test.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/tui/session_controls.go (1)

228-232: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Propagate the cleared session snapshot when reload fails after compaction.

Once Line 489 records the compaction, the persisted session is already mutated. If Line 498 then fails, compactActiveSession clears m.sessionEvents in its local copy, but Line 231 drops that snapshot and returns only the error. The live model can then keep pre-compaction context in memory and re-send history that was just compacted away. This is the same persisted-vs-in-memory contract that handleRewindCommand explicitly protects earlier in this file.

Possible fix
 	return func() tea.Msg {
 		next, result, err := m.compactActiveSession()
 		if err != nil {
-			return compactResultMsg{err: err}
+			return compactResultMsg{
+				err:                err,
+				activeSession:      next.activeSession,
+				sessionEvents:      append([]sessions.Event{}, next.sessionEvents...),
+				transcript:         append([]transcriptRow{}, next.transcript...),
+				hasSessionSnapshot: true,
+			}
 		}
 		return compactResultMsg{
 			result:             result,
 			activeSession:      next.activeSession,

Also applies to: 498-501

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/tui/session_controls.go` around lines 228 - 232,
compactActiveSession currently clears the in-memory m.sessionEvents but when it
returns an error the code at the anonymous function (return func() tea.Msg)
drops that cleared snapshot and only returns compactResultMsg{err: err}, leaving
the live model with stale events; modify the contract so compactActiveSession
(and callers at the other failing-return sites noted) returns the cleared
session snapshot and extend compactResultMsg to carry that snapshot (e.g., a
sessionEvents or sessionSnapshot field) and populate it even on error, then
ensure the caller applies that snapshot to m.sessionEvents when handling the
compactResultMsg (similar to handleRewindCommand's persisted-vs-in-memory
update).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/tui/session_controls.go`:
- Around line 391-395: Update the UI message returned by the function that
builds the compressing session text (the return that currently joins
"Compressing session", "Keep typing - messages will queue and send after
compression finishes.", and m.compactAnimationLine()) to accurately reflect that
submissions are blocked during compaction; replace the second line with copy
such as "You can keep editing your draft; submissions are disabled until
compression finishes." so the text matches the actual behavior (referencing the
same return and m.compactAnimationLine()).

---

Outside diff comments:
In `@internal/tui/session_controls.go`:
- Around line 228-232: compactActiveSession currently clears the in-memory
m.sessionEvents but when it returns an error the code at the anonymous function
(return func() tea.Msg) drops that cleared snapshot and only returns
compactResultMsg{err: err}, leaving the live model with stale events; modify the
contract so compactActiveSession (and callers at the other failing-return sites
noted) returns the cleared session snapshot and extend compactResultMsg to carry
that snapshot (e.g., a sessionEvents or sessionSnapshot field) and populate it
even on error, then ensure the caller applies that snapshot to m.sessionEvents
when handling the compactResultMsg (similar to handleRewindCommand's
persisted-vs-in-memory update).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: dae57d8a-f5e6-4780-b1c5-a325e4332f9d

📥 Commits

Reviewing files that changed from the base of the PR and between d943ce1 and 95ac09f.

📒 Files selected for processing (3)
  • internal/tui/rendering.go
  • internal/tui/session_controls.go
  • internal/tui/session_controls_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/tui/session_controls_test.go

Comment thread internal/tui/session_controls.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
internal/sessions/replay_test.go (1)

342-351: Confirm CompactionPayload “minimal” fields are sufficient for RehydrateEvents

RehydrateEvents only requires payload.Summary to be non-empty (decodeCompactionPayload), and rehydrateEventsWithCompaction decides what to skip using payload.CompactableEvents (by ref.ID). It falls back to payload.CompactedThroughSequence only when CompactableEvents is empty—so omitted fields like PreserveLast, PromptChars, Truncated, PreservedEvents, etc. won’t affect this test’s outcome.
Optional: add a test covering the fallback path by using a payload with CompactableEvents: [] and a non-zero CompactedThroughSequence.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/sessions/replay_test.go` around lines 342 - 351, The test's
CompactionPayload can be minimal because RehydrateEvents (via
decodeCompactionPayload) only requires payload.Summary to be non-empty and
rehydrateEventsWithCompaction uses payload.CompactableEvents (by ref.ID) to
decide what to skip; ensure the CompactionPayload in the test includes a
non-empty Summary and the desired CompactableEvents (as in the existing entry)
and you may optionally add a separate test case where CompactableEvents is empty
and CompactedThroughSequence is set to exercise the fallback path used by
rehydrateEventsWithCompaction.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/sessions/replay_test.go`:
- Around line 342-351: The test's CompactionPayload can be minimal because
RehydrateEvents (via decodeCompactionPayload) only requires payload.Summary to
be non-empty and rehydrateEventsWithCompaction uses payload.CompactableEvents
(by ref.ID) to decide what to skip; ensure the CompactionPayload in the test
includes a non-empty Summary and the desired CompactableEvents (as in the
existing entry) and you may optionally add a separate test case where
CompactableEvents is empty and CompactedThroughSequence is set to exercise the
fallback path used by rehydrateEventsWithCompaction.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: aeb20e88-34c9-4134-b098-e55731d7e5be

📥 Commits

Reviewing files that changed from the base of the PR and between 723765d and 13cfd51.

📒 Files selected for processing (4)
  • internal/sessions/replay.go
  • internal/sessions/replay_test.go
  • internal/tui/session_controls.go
  • internal/tui/session_controls_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • internal/sessions/replay.go
  • internal/tui/session_controls_test.go

@gnanam1990 gnanam1990 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this — solid, well-tested feature, and the JSON-block approach to lossless preserved state is well-reasoned. Requesting changes on one correctness item that affects a core path (resume/fork); the rest are non-blocking suggestions.

Blocking

Resume/fork can now hard-fail where it previously couldn't

exec_session.go switches PrepareExec from ReadEventsReadRehydratedEvents (both the fork and resume paths). RehydrateEventsdecodeCompactionPayload returns an error on a malformed / empty-summary latest EventCompaction (replay.go), and that error propagates straight out of PrepareExec. So a single bad compaction event makes the entire session unresumable and unforkable — a failure mode ReadEvents never had.

RecordCompaction validates before writing, so it shouldn't happen on the happy path. But a torn tail (which the store already hardens against elsewhere — see b28a1e7 / ea2920a) or an event written by a different build would brick resume for that session. Please degrade gracefully: on decode failure, log and fall back to the raw events rather than failing the whole prepare. A regression test that resumes a session whose latest compaction payload is malformed would lock this in.

Non-blocking

Brittle cross-module string coupling (silent degradation)

  • loadedToolEntriesFromOutput keys off the literal "Loaded " prefix + "Full schemas follow" substring of the tool_search result text.
  • projectInstructionBlock keys off "# " + " instructions for " + the <INSTRUCTIONS>/</INSTRUCTIONS> tags.

These hard-code the rendered output format of other subsystems. If that wording/template ever changes, preservation silently stops working — and the tests won't catch it because they embed the same literals. Consider exporting these as shared constants from the producing packages (or documenting the contract) so a format change breaks at the source.

Only commandPrompt is blocked during in-flight compaction

handleSubmit blocks new prompts while compactInFlight, but /model, /mode, and /rewind still execute. When the background compactResultMsg lands it overwrites activeSession, sessionEvents, and transcript from its snapshot — which could clobber an intervening /rewind or reset a model-switch note. Consider blocking session-mutating commands while compacting, or guarding the snapshot-apply against an intervening session change.

Model-switch guard is a confusing half-feature

requestCompactionBeforeModelSwitch increments compactRequests, prints "Context compaction requested…", and aborts the switch — but never actually triggers compaction. The user must manually /compact then re-run /model. The message ("unchanged until compaction can run") implies something automatic will happen when nothing does. Either wire it to enqueue the actual compaction, or reword to make the manual step explicit.

Apparently-unused duplicate API

ReadReplayEvents and ReplayEvents are exact aliases of ReadRehydratedEvents / RehydrateEvents and don't appear to be called anywhere in this PR. If there's no caller, drop them.

Nits

  • projectInstructionBlock shadows the builtin close — rename to closeIdx.
  • Compacted: len(events) < len(beforeEvents)+1 is a fragile "did anything change" check; the plan already knows via plan.CompactableCount — prefer deriving from that.
  • Two token estimators now coexist (estimateTokens vs estimateTranscriptTokens); a one-line comment noting they're intentionally separate would help.

Tests & security

Coverage is strong and adversarial (malformed earlier payload, markdown round-trip, repeated-compaction carry-forward, TUI flows). Worth adding the two cases above: a malformed latest compaction payload through PrepareExec, and a /model or /rewind issued while compactInFlight. No new security exposure — preserved state is verbatim content that ReadEvents already returned, redaction still applies at display, and the summarizer prompt asks to omit secrets.

@Vasanthdev2004

Copy link
Copy Markdown
Collaborator Author

@gnanam1990 fixed the blocking resume/fork issue in b9b740b.

What changed:

  • PrepareExec now reads rehydrated events normally, but if compaction rehydration fails it logs the failure and falls back to raw ReadEvents instead of making the session unresumable/unforkable.
  • Added a regression test covering both resume and fork when the latest session_compaction payload is malformed.

Validation:

  • go test ./internal/sessions
  • git diff --check
  • go test ./...
  • go run ./cmd/zero-release build
  • go run ./cmd/zero-release smoke

Please re-review when you get a chance.

@gnanam1990 gnanam1990 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed after b9b740b. My blocking item — resume/fork hard-failing when the latest compaction payload is malformed — is resolved: readExecContextEvents now falls back to raw ReadEvents when rehydration fails, with a regression test covering both resume and fork. That's exactly the graceful degradation I asked for. CodeRabbit had already approved and CI is green, so lifting my requested changes.

A few non-blocking follow-ups remain (tracked in #173), none of which should hold up merge:

  • String literals in compaction_preserve.go couple preserved-state parsing to other subsystems' rendered output (silent-degradation risk).
  • ReadReplayEvents/ReplayEvents in replay.go are unused aliases.
  • projectInstructionBlock shadows the builtin close.
  • The resume-fallback log.Printf can smudge the TUI frame; consider a quieter sink.

Nice work on the compaction flow. Approving.

@gnanam1990
gnanam1990 merged commit 0f10317 into main Jun 11, 2026
6 checks passed
@Vasanthdev2004
Vasanthdev2004 deleted the feat/context-compaction-system branch June 28, 2026 08:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants