Skip to content

fix: restore compaction session state - #78

Merged
delete-cloud merged 4 commits into
mainfrom
fix/restore-compaction-config
Apr 20, 2026
Merged

delete-cloud merged 4 commits into
mainfrom
fix/restore-compaction-config

Conversation

@delete-cloud

@delete-cloud delete-cloud commented Apr 20, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • restore Tape.window_start from the latest handoff anchor when sessions are rebuilt from serialized entry lists during checkpoint/compaction flows
  • sync restored session provider/model/base URL/max steps/approval mode back into the REPL config when switching to a restored managed session
  • add targeted regressions for both restore paths so compaction keeps the expected tape window and live CLI session settings

Verification

  • uv run pytest tests/agentkit/tape/test_tape.py -k \"from_list_restores_window_start or from_list_respects_explicit_window_start_kwarg or from_list_without_anchors_keeps_window_start_zero\" -v
  • uv run pytest tests/cli/test_repl.py -k \"switch_active_session_syncs_restored_model_into_repl_context\" -v

Summary by CodeRabbit

  • Bug Fixes

    • Restored session switching now synchronizes provider, model, base URL, max steps, and approval mode into the active REPL session; invalid restored fields surface clear runtime errors and leave the active session unchanged.
  • Refactor

    • Simplified tape windowing so implicit window start is consistently determined from entries.
  • Tests

    • Added tests covering session-switch validation and tape windowing behaviors.

delete-cloud and others added 2 commits April 20, 2026 11:11
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Copilot AI review requested due to automatic review settings April 20, 2026 03:12
@coderabbitai

coderabbitai Bot commented Apr 20, 2026 •

Copy link
Copy Markdown
Contributor

Caution

Review failed

Pull request was closed or merged during review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e41ae35f-7e5e-440d-afae-59b29be33174

📥 Commits

Reviewing files that changed from the base of the PR and between cddfa69 and dfa3474.

📒 Files selected for processing (2)
  • coding-agent/src/coding_agent/cli/repl.py
  • coding-agent/tests/cli/test_repl.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • coding-agent/tests/cli/test_repl.py

📝 Walkthrough

Walkthrough

Added a helper to compute tape window start from handoff anchors and refactored tape loading to use it. Added validation and sync logic when restoring REPL sessions so session config and context (model/provider/approval) are validated and applied before switching. New tests cover both areas.

Changes

Cohort / File(s) Summary
Tape Window-Start Refactor
coding-agent/src/agentkit/tape/tape.py, coding-agent/tests/agentkit/tape/test_tape.py
Added _scan_window_start(entries: list[Entry]) -> int. Tape.from_list() and Tape.load_jsonl() now default _window_start via this helper. Added three tests for handoff-anchor windowing, explicit _window_start override, and no-anchor behavior.
REPL Session Config Sync
coding-agent/src/coding_agent/cli/repl.py, coding-agent/tests/cli/test_repl.py
Added _sync_config_from_managed_session(managed_session) to validate restored session fields (provider_name allowlist, non-empty model_name, base_url type, non-negative integer max_steps) and map approval_policy to approval_mode. _switch_session() now calls this to apply config and update self.context["model"]. Tests added for successful sync and invalid-session cases.

Sequence Diagram(s)

sequenceDiagram
    participant ManagedSession
    participant InteractiveSession
    participant Config
    participant Runtime
    ManagedSession->>InteractiveSession: provide restored session data
    InteractiveSession->>InteractiveSession: _sync_config_from_managed_session(data)
    InteractiveSession-->>Config: set provider/model/base_url/max_steps/approval_mode
    InteractiveSession->>Runtime: rewire runtime using updated config
    Runtime-->>InteractiveSession: confirm switch complete
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I sniff the tape for handoffs and hop to the right start,

I tweak the REPL so sessions remember each part.
Models aligned, approvals set just so,
A happy little rabbit gives a tiny bravo! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: restore compaction session state' is concise and directly relates to the main changes: restoring tape window state and session configuration during compaction/restore flows.

✏️ 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 fix/restore-compaction-config

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
coding-agent/tests/cli/test_repl.py (1)

787-791: ⚠️ Potential issue | 🟠 Major

These mocked sessions no longer satisfy _switch_session’s required restored fields.

Both fakes now include model_name, but _switch_session also validates provider_name, approval_policy, and max_steps. These tests can fail before reaching the runtime-rebinding assertions.

Proposed fix (apply to both mocked session objects)
+from coding_agent.approval import ApprovalPolicy
@@
 return SimpleNamespace(
     id="session-b",
     model_name="gpt-4o-mini",
+    provider_name="openai",
+    base_url=None,
+    max_steps=30,
+    approval_policy=ApprovalPolicy.AUTO,
     runtime_pipeline=fake_pipeline,
     runtime_ctx=fake_ctx,
     runtime_adapter=fake_adapter,
 )

Also applies to: 849-853

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@coding-agent/tests/cli/test_repl.py` around lines 787 - 791, Tests create
mocked session objects used by _switch_session but they only set model_name;
update both mocked session dicts/objects (the ones passed into the test setup
that currently set model_name, runtime_pipeline, runtime_ctx, runtime_adapter)
to also include provider_name, approval_policy, and max_steps with valid values
so they satisfy _switch_session validation; keep provider_name matching the
expected provider in the test, set approval_policy to a valid policy
object/string used elsewhere in tests, and set max_steps to an integer (e.g., 0
or the default used by other tests).
🧹 Nitpick comments (1)
coding-agent/tests/agentkit/tape/test_tape.py (1)

447-523: Add a multi-handoff from_list test to lock “latest anchor wins.”

You already assert this behavior for JSONL; adding the same for from_list would directly protect the exact restore path this PR targets.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@coding-agent/tests/agentkit/tape/test_tape.py` around lines 447 - 523, Add a
new unit test in coding-agent/tests/agentkit/tape/test_tape.py that exercises
Tape.from_list with multiple handoff anchors to ensure the "latest anchor wins"
behavior on restoration: construct entries_data containing at least two anchor
entries with "anchor_type": "handoff" separated by messages, call
Tape.from_list(entries_data) and assert tape.window_start equals the index of
the last handoff anchor (and that windowed_entries() length matches
expectation); name the test e.g. test_from_list_latest_handoff_wins and
reference Tape.from_list and tape.window_start in the assertions so the exact
restore path is covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@coding-agent/src/coding_agent/cli/repl.py`:
- Around line 245-246: The code currently sets self.context["model"] =
managed_session.model_name before calling
_sync_config_from_managed_session(managed_session), which can leave context
partially mutated if the sync/validation fails; change the order so you first
call _sync_config_from_managed_session(managed_session) and only if it
returns/raises successfully assign self.context["model"] (or alternatively
perform the assignment into a temporary variable and only set self.context on
success), ensuring that _sync_config_from_managed_session and
managed_session.model_name are used to validate/sync first and that context
mutation happens only after successful sync.
- Around line 201-239: The _sync_config_from_managed_session function
dereferences managed_session attributes directly which can raise AttributeError
and copies max_steps without validation; update the function to first safely
extract attributes using getattr or hasattr checks for provider_name,
model_name, approval_policy, base_url and max_steps, validate provider_name
against the allowed_providers set, ensure model_name is a non-empty str, map
approval_policy using the ApprovalPolicy enum (or isinstance/getattr) and raise
the same RuntimeError on invalid/missing values, and validate max_steps is an
int within an acceptable range (e.g., non-negative) before assigning to
self.config.max_steps; only mutate self.config.provider, .model, .base_url,
.max_steps and .approval_mode after all validations succeed.

---

Outside diff comments:
In `@coding-agent/tests/cli/test_repl.py`:
- Around line 787-791: Tests create mocked session objects used by
_switch_session but they only set model_name; update both mocked session
dicts/objects (the ones passed into the test setup that currently set
model_name, runtime_pipeline, runtime_ctx, runtime_adapter) to also include
provider_name, approval_policy, and max_steps with valid values so they satisfy
_switch_session validation; keep provider_name matching the expected provider in
the test, set approval_policy to a valid policy object/string used elsewhere in
tests, and set max_steps to an integer (e.g., 0 or the default used by other
tests).

---

Nitpick comments:
In `@coding-agent/tests/agentkit/tape/test_tape.py`:
- Around line 447-523: Add a new unit test in
coding-agent/tests/agentkit/tape/test_tape.py that exercises Tape.from_list with
multiple handoff anchors to ensure the "latest anchor wins" behavior on
restoration: construct entries_data containing at least two anchor entries with
"anchor_type": "handoff" separated by messages, call
Tape.from_list(entries_data) and assert tape.window_start equals the index of
the last handoff anchor (and that windowed_entries() length matches
expectation); name the test e.g. test_from_list_latest_handoff_wins and
reference Tape.from_list and tape.window_start in the assertions so the exact
restore path is covered.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c881236f-dbe6-4f52-9367-489a81a2fc68

📥 Commits

Reviewing files that changed from the base of the PR and between 9ffe246 and f6ddd77.

📒 Files selected for processing (4)
  • coding-agent/src/agentkit/tape/tape.py
  • coding-agent/src/coding_agent/cli/repl.py
  • coding-agent/tests/agentkit/tape/test_tape.py
  • coding-agent/tests/cli/test_repl.py

Comment thread coding-agent/src/coding_agent/cli/repl.py
Comment thread coding-agent/src/coding_agent/cli/repl.py Outdated
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Copilot AI review requested due to automatic review settings April 20, 2026 03:25

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
coding-agent/tests/cli/test_repl.py (1)

979-1031: Extend missing-provider rollback assertions to all config fields.

This test currently checks only provider and model. Add base_url, max_steps, and approval_mode immutability checks to catch partial mutation regressions.

Suggested test hardening
         with pytest.raises(RuntimeError, match="invalid provider_name"):
             await session._switch_session("session-b")

         assert session.context["model"] == "current-model"
         assert session.config.provider == "openai"
         assert session.config.model == "current-model"
+        assert session.config.base_url == "http://current.local"
+        assert session.config.max_steps == 30
+        assert session.config.approval_mode == "auto"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@coding-agent/tests/cli/test_repl.py` around lines 979 - 1031, The test
currently only asserts rollback for session.context["model"] and
config.provider/model after InteractiveSession._switch_session raises; extend
the assertions to also verify that session.config.base_url,
session.config.max_steps, and session.config.approval_mode remain unchanged to
catch partial mutation regressions. Update the
test_switch_session_rejects_missing_restored_provider_without_mutating_state
test to add asserts comparing session.config.base_url == "http://current.local",
session.config.max_steps == 30, and session.config.approval_mode == "auto" (and
keep the existing context/model checks) so the test ensures no fields on
session.config or session.context are mutated when _switch_session fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@coding-agent/tests/cli/test_repl.py`:
- Around line 979-1031: The test currently only asserts rollback for
session.context["model"] and config.provider/model after
InteractiveSession._switch_session raises; extend the assertions to also verify
that session.config.base_url, session.config.max_steps, and
session.config.approval_mode remain unchanged to catch partial mutation
regressions. Update the
test_switch_session_rejects_missing_restored_provider_without_mutating_state
test to add asserts comparing session.config.base_url == "http://current.local",
session.config.max_steps == 30, and session.config.approval_mode == "auto" (and
keep the existing context/model checks) so the test ensures no fields on
session.config or session.context are mutated when _switch_session fails.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 07f11578-1f4e-46cb-aa7e-5860e6ab6307

📥 Commits

Reviewing files that changed from the base of the PR and between f6ddd77 and cddfa69.

📒 Files selected for processing (2)
  • coding-agent/src/coding_agent/cli/repl.py
  • coding-agent/tests/cli/test_repl.py
✅ Files skipped from review due to trivial changes (1)
  • coding-agent/src/coding_agent/cli/repl.py

Copilot AI 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.

Pull request overview

Fixes session/tape restoration behavior during checkpoint/compaction flows by restoring tape window state from the latest handoff anchor and synchronizing restored managed-session settings back into the REPL config/context.

Changes:

  • Restore Tape.window_start when rebuilding tapes from serialized entry lists (in addition to JSONL load).
  • Sync restored managed-session config (provider/model/base_url/max_steps/approval mode) into the active REPL config when switching sessions.
  • Add regression tests covering both the tape restore path and REPL session switching behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
coding-agent/src/agentkit/tape/tape.py Adds _scan_window_start() and applies it to Tape.from_list() and Tape.load_jsonl() to restore the tape window consistently.
coding-agent/src/coding_agent/cli/repl.py Adds _sync_config_from_managed_session() and invokes it during session switching to align REPL config with restored session settings.
coding-agent/tests/agentkit/tape/test_tape.py Adds targeted tests for Tape.from_list() window-start restoration behavior.
coding-agent/tests/cli/test_repl.py Extends session-manager integration tests and adds new tests for config sync + rejection paths.
Comments suppressed due to low confidence (2)

coding-agent/tests/cli/test_repl.py:791

  • FakeSessionManager.get_session() here returns a session object without the fields now required by _sync_config_from_managed_session() (e.g., provider_name, base_url, max_steps, approval_policy). With the new REPL behavior, calling session._switch_session() in this test will raise before the runtime-context assertions run. Update the stubbed session to include the required config fields (or explicitly bypass config syncing in this test if that’s the intent).
            def get_session(self, session_id: str):
                assert session_id == "session-b"
                return SimpleNamespace(
                    id="session-b",
                    model_name="gpt-4o-mini",
                    runtime_pipeline=fake_pipeline,
                    runtime_ctx=fake_ctx,
                    runtime_adapter=fake_adapter,
                )

coding-agent/tests/cli/test_repl.py:853

  • Same issue as above: this stubbed managed session is missing required config fields (provider_name, base_url, max_steps, approval_policy). _switch_session() now syncs config and will raise with the current stub, causing this test to fail. Populate the missing fields (or monkeypatch out config syncing for this test).
            def get_session(self, session_id: str):
                assert session_id == "session-b"
                return SimpleNamespace(
                    id="session-b",
                    model_name="gpt-4o-mini",
                    runtime_pipeline=object(),
                    runtime_ctx=fake_ctx,
                    runtime_adapter=adapter,
                )

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 248 to +253
async def _switch_session(self, session_id: str) -> None:
await self._session_manager.ensure_session_runtime(session_id)
managed_session = self._session_manager.get_session(session_id)
self.context["session_id"] = managed_session.id
self._sync_config_from_managed_session(managed_session)
self.context["model"] = self.config.model

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

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

_switch_session() mutates self.context["session_id"] before validating/syncing the restored session’s config. If _sync_config_from_managed_session() raises, the REPL ends up in a partially-switched state (session_id updated but pipeline/config/model not). Consider validating/syncing first and only committing context["session_id"] (and other state) after all checks pass, or snapshot+rollback on error.

Copilot uses AI. Check for mistakes.
Comment on lines +203 to +211
provider_name = getattr(managed_session, "provider_name", None)
allowed_providers = {
"openai",
"anthropic",
"copilot",
"kimi",
"kimi-code",
"kimi-code-anthropic",
}

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

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

allowed_providers duplicates the canonical provider list already defined on Config.provider (see coding_agent/core/config.py:16-23, and also coding_agent/__main__.py:25-27 which derives choices via get_args). To avoid drift when providers change, consider deriving this set from Config.model_fields["provider"].annotation (or another single source of truth) instead of hardcoding it here.

Copilot uses AI. Check for mistakes.
Comment on lines +969 to +977
with pytest.raises(RuntimeError, match="invalid max_steps"):
await session._switch_session("session-b")

assert session.context["model"] == "current-model"
assert session.config.provider == "openai"
assert session.config.model == "current-model"
assert session.config.base_url == "http://current.local"
assert session.config.max_steps == 30
assert session.config.approval_mode == "auto"

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

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

These “without mutating state” tests currently don’t assert that session.context["session_id"] remains unchanged when _switch_session() rejects a restored session. Since _switch_session() updates context["session_id"] early (and that’s a key piece of session state), consider setting an initial session_id and asserting it’s preserved on failure to prevent partial-switch regressions.

Copilot uses AI. Check for mistakes.
Comment on lines +448 to +449
from agentkit.tape.anchor import Anchor as _Anchor

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

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

Unused import: from agentkit.tape.anchor import Anchor as _Anchor is not referenced in this test. It can be removed to keep the test minimal and avoid confusion about intent.

Suggested change
from agentkit.tape.anchor import Anchor as _Anchor

Copilot uses AI. Check for mistakes.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@delete-cloud
delete-cloud merged commit bcac870 into main Apr 20, 2026
1 check was pending
@delete-cloud
delete-cloud deleted the fix/restore-compaction-config branch May 10, 2026 03:56
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