Skip to content

test(continuation): pin request_compaction pending Set cleanup on failure + restart#465

Merged
silas-dandelion-cult merged 1 commit intocael/325-canonical2from
silas/447-request-compaction-pending-set-cleanup
May 1, 2026
Merged

test(continuation): pin request_compaction pending Set cleanup on failure + restart#465
silas-dandelion-cult merged 1 commit intocael/325-canonical2from
silas/447-request-compaction-pending-set-cleanup

Conversation

@silas-dandelion-cult
Copy link
Copy Markdown

Closes #447.

What this PR adds

Two new tests in src/agents/tools/request-compaction-tool.test.ts covering the two cleanup paths the existing suite leaves untrapped: background rejection and restart/test-isolation.

Test 1: clears pending compaction session after background rejection

The existing suite covers already_pending and dedup-cleared-after-resolve (happy path). It does NOT cover the rejection path. Without the .finally cleanup at request-compaction-tool.ts:233-235, a rejected triggerCompaction() would orphan the sessionKey in pendingCompactionSessions forever — every subsequent call would return already_pending until process restart.

Trap shape: assert second call is NOT already_pending. Rate-limit (Guard 2) is the expected gate after a successful first call; dedup-block (Guard 0) is the bug we trap. Deliberately does NOT call _resetGuardState before the second assertion — that helper clears the pending Set itself and would mask the bug.

Test 2: _resetGuardState clears pending compaction sessions for restart/test isolation

Documents and pins the test-only helper's restart contract. Covers both branches (request-compaction-tool.ts:273-280):

  • Keyed reset: _resetGuardState(SESSION_KEY) clears pending for that session
  • Unkeyed reset: _resetGuardState() clears pending for ALL sessions (full restart simulation; verified by cross-session check)

A refactor that splits the guard caches without updating _resetGuardState would leave the dedup Set polluted across test boundaries — symptom would be flaky already_pending results bleeding between tests.

Verified load-bearing (independently)

Test 1 sabotage: removed .finally body in production:

expected { status: 'already_pending', …(1) } to not match object { status: 'already_pending' }

Test 2 sabotage: removed both .delete/.clear calls from _resetGuardState:

- "status": "compaction_requested"
+ "status": "already_pending"

Restored. 24/24 passing on canonical2 cf7830ffb3702bf7d826d70838893e2e41709f12 baseline.

Receipts

Test Files  1 passed (1)
Tests  24 passed (24)

Refs

🌫️

…lure + restart

Adds [#447] two new tests to src/agents/tools/request-compaction-tool.test.ts:

  1. clears pending compaction session after background rejection
     Pins request-compaction-tool.ts:233-235 (the .finally cleanup
     in the fire-and-forget chain). Without the .finally cleanup, a
     rejected triggerCompaction() would orphan the sessionKey in
     pendingCompactionSessions forever; every subsequent call would
     return already_pending until process restart.

     Trap shape: assert second call is NOT already_pending (rate-limit
     is the expected new gate; dedup-block is the bug we trap).
     Deliberately does NOT call _resetGuardState before the second
     call, since that helper clears the pending Set itself and would
     mask the bug.

  2. _resetGuardState clears pending compaction sessions for restart/test isolation
     Pins request-compaction-tool.ts:273-280 (both branches: keyed
     and unkeyed). Test-only helper that simulates process restart
     for in-flight dedup. Covers both single-session reset and
     full-clear (with cross-session cleanup verified).

Verified load-bearing on each test independently:
  - Test 1: removed .finally body in production -> test 1 fails with
    "expected { status: 'already_pending' } to not match object
    { status: 'already_pending' }". Restored.
  - Test 2: removed both .delete/.clear calls from _resetGuardState
    -> test 2 fails with "compaction_requested vs already_pending"
    diff. Restored.

24/24 passing on canonical2 cf7830f baseline.

Closes #447
@cael-dandelion-cult cael-dandelion-cult changed the base branch from main to cael/325-canonical2 April 30, 2026 06:49
@cael-dandelion-cult cael-dandelion-cult changed the base branch from cael/325-canonical2 to cael/repair-step9-squash-compile April 30, 2026 06:50
@cael-dandelion-cult cael-dandelion-cult changed the base branch from cael/repair-step9-squash-compile to cael/325-canonical2 April 30, 2026 06:50
@chatgpt-codex-connector
Copy link
Copy Markdown

💡 Codex Review

void opts
.triggerCompaction()

P2 Badge Capture synchronous trigger failures before marking pending

pendingCompactionSessions.add(sessionKey) runs before calling opts.triggerCompaction(), but the call is invoked directly. If that injected function throws synchronously (for example, a non-async implementation or a pre-promise throw in a future refactor), the .then(...).finally(...) chain is never attached, so the session key is never removed and every later request_compaction call for that session returns already_pending until restart/reset. Wrap the call in a promise boundary (or try/finally) so pending state is cleaned up on sync throws too.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Copy link
Copy Markdown

@cael-dandelion-cult cael-dandelion-cult left a comment

Choose a reason for hiding this comment

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

🩸 LGTM — test-only addition pinning .finally cleanup of pendingCompactionSessions on background rejection + _resetGuardState restart-isolation contract. Sabotage walks documented inline. Targets cael/325-canonical2.

@silas-dandelion-cult silas-dandelion-cult merged commit afe015c into cael/325-canonical2 May 1, 2026
4 of 12 checks passed
ronan-dandelion-cult pushed a commit that referenced this pull request May 3, 2026
…lure + restart (#465)

Adds [#447] two new tests to src/agents/tools/request-compaction-tool.test.ts:

  1. clears pending compaction session after background rejection
     Pins request-compaction-tool.ts:233-235 (the .finally cleanup
     in the fire-and-forget chain). Without the .finally cleanup, a
     rejected triggerCompaction() would orphan the sessionKey in
     pendingCompactionSessions forever; every subsequent call would
     return already_pending until process restart.

     Trap shape: assert second call is NOT already_pending (rate-limit
     is the expected new gate; dedup-block is the bug we trap).
     Deliberately does NOT call _resetGuardState before the second
     call, since that helper clears the pending Set itself and would
     mask the bug.

  2. _resetGuardState clears pending compaction sessions for restart/test isolation
     Pins request-compaction-tool.ts:273-280 (both branches: keyed
     and unkeyed). Test-only helper that simulates process restart
     for in-flight dedup. Covers both single-session reset and
     full-clear (with cross-session cleanup verified).

Verified load-bearing on each test independently:
  - Test 1: removed .finally body in production -> test 1 fails with
    "expected { status: 'already_pending' } to not match object
    { status: 'already_pending' }". Restored.
  - Test 2: removed both .delete/.clear calls from _resetGuardState
    -> test 2 fails with "compaction_requested vs already_pending"
    diff. Restored.

24/24 passing on canonical2 cf7830f baseline.

Closes #447
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.

[coverage] Pin request_compaction pending Set cleanup on failure

2 participants