Skip to content

fix(google-antigravity): persist thought-signature replay across restarts - #1434

Closed
Yuxin-Qiao wants to merge 10 commits into
lidge-jun:devfrom
Yuxin-Qiao:fix/1429-antigravity-replay-persistence
Closed

fix(google-antigravity): persist thought-signature replay across restarts#1434
Yuxin-Qiao wants to merge 10 commits into
lidge-jun:devfrom
Yuxin-Qiao:fix/1429-antigravity-replay-persistence

Conversation

@Yuxin-Qiao

@Yuxin-Qiao Yuxin-Qiao commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Persist the Google Antigravity (CCA / Vertex) thought-signature replay cache to disk so Gemini tool-call continuations survive a proxy restart. Closes [Bug]: google-antigravity tool continuation 400s once the in-process thought-signature replay cache misses #1429.
  • Root cause: src/adapters/google-antigravity-replay.ts was process-local (1h TTL, 10,240 sessions, batch eviction). After ocx service stop && ocx service start (or TTL/eviction pressure), a /v1/chat/completions continuation 400s with Function call is missing a thought_signature, and the OpenAI-format surface has no field for the caller to echo the signature back — the conversation was stuck. The /v1/responses path was unaffected because the signature travels in the payload there.
  • Fix: debounced atomic snapshot of the cache to antigravity-replay.json in the config directory (same durability pattern as responses-state.json), lazy bounded load on first cache access with expired sessions dropped at load, and a shutdown flush wired into drainAndShutdown. The CCA session id is deterministic (SHA-256 of the first user message text), so a restarted proxy re-derives the same key and re-injects the cached signatures.
  • Claude-on-Antigravity is untouched: it uses inline signature sanitization, not the replay cache (antigravityUsesReplayCache is Gemini-only).
  • Snapshot contents are bounded (24 MiB write cap, 32 MiB refuse-to-parse ceiling) and contain only 64-hex hashed keys (model+session, function identity) plus opaque upstream signature tokens — no raw request content, credentials, or model names. Writes go through the guarded atomicWriteFileAsync (0600 file, 0700 dir, real-home write guard under test).

Verification

  • bun test tests/google-antigravity-replay.test.ts — 53 pass, including restart simulation (observe -> flush -> reset -> apply re-injects), flush before debounce, load-time expiry and cap cleanup, corrupt/unknown-version refusal, oversized-file refusal, clear-on-invalid, duplicate-key collapse, UTF-8 byte accounting, and the review regressions below.
  • Review regressions: load recomputes call sizes from the signature and ignores serialized sizeBytes (including multi-byte signatures whose UTF-8 size differs from JavaScript string length), snapshot never serializes sizeBytes, flush waits out a blocked writer and persists mutations that land during it (deterministic afterTempWrite seam), flush surfaces snapshot write failures for shutdown diagnostics, background snapshot failures log a redacted warning and keep the service running.
  • bun test tests/responses-state.test.ts tests/server-background-lifecycle.test.ts tests/shutdown-drain.test.ts tests/google-antigravity-wire.test.ts tests/google-antigravity-oauth.test.ts tests/state-store-sweeper.test.ts tests/app-owned-memory.test.ts tests/storage-mutation-race.test.ts — 218 pass.
  • bun run typecheck — clean.
  • bun run privacy:scan — passed.
  • cd docs-site && bun install --frozen-lockfile && bun run build — 221 pages built (English + ja/ko/ru/zh-cn updated).
  • Full bun run test: 10,926 pass, 8 skipped, 20 baseline failures. Failures are confined to unrelated catalog/live-update/provider-management, crash-guard, and retained-root seam tests; none overlap with this change. The replay suite remains 53/53.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.

  • I pushed my PR to the latest dev commit.

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

Summary by CodeRabbit

  • New Features

    • Google Vertex and Antigravity reasoning continuity now survives proxy restarts through durable replay-state snapshots.
    • Expired, invalid, duplicate, or oversized cached data is safely ignored.
  • Bug Fixes

    • Shutdown now flushes pending replay state, reducing the risk of losing continuation data.
    • Cache updates remain resilient when background persistence encounters errors.
  • Documentation

    • Updated adapter documentation in English, Japanese, Korean, Russian, and Simplified Chinese to describe persisted thoughtSignature snapshots.

@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Aug 10, 2026
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The Antigravity replay cache now persists validated thoughtSignature snapshots in the configuration directory. It restores valid entries after restart, enforces expiration and size limits, flushes during shutdown, and includes persistence tests and localized documentation.

Changes

Antigravity replay persistence

Layer / File(s) Summary
Snapshot cache lifecycle
src/adapters/google-antigravity-replay.ts
Adds versioned loading, validation, eviction, debounced serialized atomic writes, size limits, explicit flushing, cache integration, test seams, and reset handling.
Persistence and snapshot validation tests
tests/google-antigravity-replay.test.ts
Tests persistence, reloads, flushing, expiration, limits, invalid snapshots, clearing, oversized files, concurrent writes, and write failures.
Shutdown flushing and adapter documentation
src/server/lifecycle.ts, docs-site/src/content/docs/*/reference/adapters.md
Shutdown flushes replay state and logs separate warnings for flush failures. Adapter documentation describes persistence across proxy restarts.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AntigravityReplayCache
  participant ConfigDirectory
  participant ServerLifecycle

  Client->>AntigravityReplayCache: Observe tool-call signature
  AntigravityReplayCache->>ConfigDirectory: Load validated snapshot
  ConfigDirectory-->>AntigravityReplayCache: Return retained signature
  AntigravityReplayCache-->>Client: Apply signature to continuation
  ServerLifecycle->>AntigravityReplayCache: Flush pending replay writes
  AntigravityReplayCache->>ConfigDirectory: Atomically persist snapshot
Loading

Possibly related PRs

Suggested reviewers: lidge-jun

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.75% 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
Linked Issues check ✅ Passed The implementation addresses issue [#1429] by persisting, reloading, validating, and flushing the Antigravity replay cache across restarts, expiry, and eviction.
Out of Scope Changes check ✅ Passed The code, lifecycle integration, tests, and localized documentation directly support the linked issue and stated persistence objectives.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: persisting Google Antigravity thought-signature replay across proxy restarts.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

⏳ DRAFT

  • review readiness checklist open (0/4 boxes ticked).

What to do

  • Tick all four boxes in the PR description once you're done (currently 0/4).
  • New commits were pushed after the checklist was completed on 89a96ce; the current head is 3dad8a8.
  • The checklist has been reset: re-test against the latest code and tick all four boxes again.

Review readiness checklist

  • ⬜ All CI tests are green on my local testing.
  • ⬜ I pushed my PR to the latest dev commit.
  • ⬜ I resolved all correct Codex and CodeRabbit findings.
  • ⬜ My PR is ready for review.

0/4 boxes ticked.

New commits were pushed after the checklist was completed on 89a96ce; the current head is 3dad8a8.
The checklist has been reset: re-test against the latest code and tick all four boxes again.
This PR stays in draft until every box above is ticked.

@github-actions
github-actions Bot marked this pull request as draft August 10, 2026 18:36

@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: 5

🤖 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 `@docs-site/src/content/docs/reference/adapters.md`:
- Around line 93-96: Update the documentation text describing Vertex and
Antigravity so it explicitly calls preserved and replayed thoughtSignature
values opaque. Keep the wording clear that adapters preserve and replay these
values, without implying clients provide or reconstruct them.

In `@src/adapters/google-antigravity-replay.ts`:
- Around line 91-102: In the replay-entry admission logic around the call
validation and byCall.set, calculate the UTF-8 byte size from call.signature and
pair[0], reject entries whose persisted call.sizeBytes differs from that
calculated value, and use the calculated size for ReplayCall.sizeBytes and
entry.bytes accounting. Add a regression test covering an underreported
sizeBytes value.
- Around line 199-200: Update flushAntigravityReplay() so that after awaiting
replaySnapshotPersistGate when no timer is initially present, it rechecks
replaySnapshotPersistTimer and calls persistReplaySnapshotNow() once if a timer
was scheduled during the in-flight write, ensuring the final mutation is
persisted before shutdown completes.

In `@tests/google-antigravity-replay.test.ts`:
- Line 563: Remove the duplicate const raw declaration in the test’s
snapshot-loading scope, keeping a single declaration with the existing version
and sessions typing so the file parses successfully.
- Around line 33-42: Inside describe("durable antigravity replay snapshot", ...)
add a nested beforeEach hook that calls setAntigravityReplayLimitsForTests(),
ensuring replaySnapshotLoaded and replayCache are reset before each test while
preserving the existing environment setup and cleanup hooks.
🪄 Autofix

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: ASSERTIVE

Plan: Pro Plus

Run ID: e40946e5-1fd7-493d-83b4-49162bbc7829

📥 Commits

Reviewing files that changed from the base of the PR and between 08e7e0f and 4a8b045.

📒 Files selected for processing (8)
  • docs-site/src/content/docs/ja/reference/adapters.md
  • docs-site/src/content/docs/ko/reference/adapters.md
  • docs-site/src/content/docs/reference/adapters.md
  • docs-site/src/content/docs/ru/reference/adapters.md
  • docs-site/src/content/docs/zh-cn/reference/adapters.md
  • src/adapters/google-antigravity-replay.ts
  • src/server/lifecycle.ts
  • tests/google-antigravity-replay.test.ts

Comment thread docs-site/src/content/docs/reference/adapters.md
Comment thread src/adapters/google-antigravity-replay.ts Outdated
Comment thread src/adapters/google-antigravity-replay.ts Outdated
Comment thread tests/google-antigravity-replay.test.ts
Comment thread tests/google-antigravity-replay.test.ts

@Wibias Wibias 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.

Request changes based on a full bugs/edge-cases/security review of the current head.

Merge blockers:

  1. Snapshot loading trusts serialized sizeBytes instead of deriving it from the persisted signature. A corrupt/tampered snapshot can store a very large signature with sizeBytes: 0/1, bypassing maxSignatureBytes, per-session byte limits, replay byte accounting, and app-owned memory accounting. Recompute the authoritative UTF-8 byte length from signature during load, enforce the limits against that value, and use it for bookkeeping. Prefer not serializing derived sizeBytes at all. Add a regression with a large signature plus falsified small sizeBytes.

  2. flushAntigravityReplay() is not guaranteed to include the final mutation during shutdown. If a writer is already in flight and no debounce timer exists when flush starts, flush only awaits the current gate. A mutation during that write can schedule a new debounce timer; the first writer completes, flush returns, and shutdown can exit before the newly scheduled write runs. Use a dirty/mutation generation or loop until the persisted generation catches the current mutation generation and no writer/timer remains. Add a deterministic blocked-writer test with a second mutation during the first write.

Required hardening:

  1. Snapshot persistence errors are swallowed inside persistReplaySnapshotNow(). This also makes the lifecycle's Promise.allSettled(...flushAntigravityReplay()) rejection handling ineffective for real disk/ACL/rename failures. The cache need not crash the service, but durability loss should at least produce a sanitized warning/metric or otherwise reach the shutdown diagnostic path.

Security-positive note: using guarded atomic writes and hardened local permissions is the right direction, and I did not find raw credentials/account ids in the snapshot.

I explicitly did NOT adopt the stale bot claims about a duplicate const raw declaration or cross-test snapshot leakage; those do not reproduce on this head.

Please fix the two durability/integrity blockers, then refresh onto current dev and obtain green exact-head CI.

@Yuxin-Qiao
Yuxin-Qiao force-pushed the fix/1429-antigravity-replay-persistence branch from 4a8b045 to 5796144 Compare August 11, 2026 03:16
@Yuxin-Qiao

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review. All three points are fixed on the refreshed head (rebased onto current dev), and the head gates are green on the exact head.

1. Snapshot loading no longer trusts serialized sizeBytes

loadReplaySnapshotEntry now derives the authoritative UTF-8 byte length from signature itself (utf8.encode(call.signature).byteLength), enforces maxSignatureBytes and the per-call/session limits against that value, and uses it for ReplayCall.sizeBytes and all byte accounting. The serialized sizeBytes field is ignored entirely, so a forged snapshot cannot shrink its footprint below the true signature size. The write path no longer serializes sizeBytes at all; old snapshots that still contain it load fine because the field is ignored.

Regression coverage in tests/google-antigravity-replay.test.ts: "load recomputes call sizes from the signature and ignores serialized sizeBytes" (70 KB signature with forged sizeBytes: 1 is dropped; a valid signature with forged sizeBytes: 1 is accounted at its true size), plus "snapshot never serializes sizeBytes" which locks the on-disk format.

2. flushAntigravityReplay() now guarantees the final mutation is persisted

I added a mutation generation (replayMutationGeneration, bumped by markReplayDirty() on every cache change) and a written generation that each successful snapshot write records. flushAntigravityReplay() loops until the written generation equals the current mutation generation and no debounce timer remains, so a mutation that lands while a writer is in flight is persisted before shutdown completes (this is stronger than the one-shot recheck flushResponseState() does, because a mutation can also land during the follow-up write).

Deterministic regression: "flush waits out a blocked writer and persists mutations that land during it" blocks the first snapshot write via the afterTempWrite seam (same pattern as AtomicWriteAsyncTestSeam), mutates the cache while the write is blocked, releases it, and asserts both sessions are present in the final snapshot.

3. Persistence failures are no longer swallowed

The debounced background path now logs a sanitized static warning on failure (no error payload, no paths). persistReplaySnapshotNow() propagates failures, so flushAntigravityReplay() rejects and the existing lifecycle shutdown diagnostic ([antigravity] replay flush during shutdown failed) is actually reachable, without crashing the service.

Coverage: "flush surfaces snapshot write failures for shutdown diagnostics" and "background snapshot failures log a redacted warning and keep the service running".

Verification on the new head: replay suite 51/51, adjacent suites 168/168 across responses-state, server-background-lifecycle, and shutdown-drain, bun run typecheck clean, bun run privacy:scan passed, docs build 221 pages. I also updated the adapters.md wording (all five locales) to describe thoughtSignature values as opaque, addressing the CodeRabbit docs note; the other CodeRabbit threads you explicitly did not adopt were resolved without changes.

@github-actions
github-actions Bot marked this pull request as ready for review August 11, 2026 03:25
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@github-actions[bot] Acknowledged. Pull request #1434 is marked Ready for Review at bf257aa39581dbf62c8179df52fb4cbb90b40273.

You are interacting with an AI system.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

@github-actions The readiness checks are complete for PR #1434. I will review the current head.

⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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

🤖 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 `@src/adapters/google-antigravity-replay.ts`:
- Around line 214-226: Bound the retry loop in flushAntigravityReplay so it
cannot wait indefinitely while replay mutations continue arriving. Add a small
iteration limit around the existing persistReplaySnapshotNow and
replaySnapshotPersistGate flow, while preserving the current behavior of
persisting pending mutations and awaiting in-flight writes; after the bound is
reached, return with the latest observed state persisted.

In `@tests/google-antigravity-replay.test.ts`:
- Around line 727-739: Update the background snapshot failure test around the
console.warn spy to restore the captured spy after assertions complete. Ensure
cleanup runs even when the test fails, using the test’s existing setup or
teardown mechanism rather than leaving console.warn mocked for subsequent tests.
🪄 Autofix

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: ASSERTIVE

Plan: Pro Plus

Run ID: b3479356-ac60-4218-bb80-f2c61ea8b142

📥 Commits

Reviewing files that changed from the base of the PR and between 4a8b045 and bf257aa.

📒 Files selected for processing (7)
  • docs-site/src/content/docs/ja/reference/adapters.md
  • docs-site/src/content/docs/ko/reference/adapters.md
  • docs-site/src/content/docs/reference/adapters.md
  • docs-site/src/content/docs/ru/reference/adapters.md
  • docs-site/src/content/docs/zh-cn/reference/adapters.md
  • src/adapters/google-antigravity-replay.ts
  • tests/google-antigravity-replay.test.ts

Comment thread src/adapters/google-antigravity-replay.ts
Comment thread tests/google-antigravity-replay.test.ts
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@github-actions[bot] Acknowledged. PR #1434 is Ready for Review at bf257aa39581dbf62c8179df52fb4cbb90b40273.

You are interacting with an AI system.

@github-actions
github-actions Bot marked this pull request as draft August 11, 2026 03:37
@github-actions
github-actions Bot marked this pull request as ready for review August 11, 2026 03:39
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

@github-actions[bot] The readiness gate completed for PR #1434 at ea942d4446a6ab9dd72bd9613911f632270b7ac8. I will review the current head.

⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 1

Caution

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

⚠️ Outside diff range comments (2)
src/adapters/google-antigravity-replay.ts (2)

73-130: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Persist load-time removals.

The loader drops expired or over-limit entries from memory, but the load path does not mark the snapshot dirty. If the first access only reads metrics or performs a cache miss, antigravity-replay.json remains unchanged. Expired signatures then remain on disk and are parsed again after every restart.

Track whether loading removed entries and schedule one rewrite after loading. Extend the expiry test to flush and verify that the stale session key is absent from the file.

Also applies to: 132-160

🤖 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 `@src/adapters/google-antigravity-replay.ts` around lines 73 - 130, Track
whether loadReplaySnapshotEntry or load-time trimming removes any snapshot data,
mark the replay snapshot dirty, and schedule a rewrite once snapshot loading
completes so stale entries are removed from disk. Preserve normal loading
behavior when nothing is discarded, and extend the expiry test to flush
persistence and verify the expired session key is absent from the snapshot file.

162-214: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Order replay sessions by activity before applying the snapshot cap.

At src/adapters/google-antigravity-replay.ts:190-197, reverse Map order does not represent session activity. observeAntigravityReplay refreshes expiresAtMs, but Map.set preserves the key’s original position. A recently updated older session can therefore be excluded when a newer inactive session consumes the 24 MiB cap. applyAntigravityReplay also updates only touchedAtMs at lines 635-642, so expiresAtMs does not represent all session activity.

Track a session-level activity timestamp on both paths, sort candidates by that timestamp before enforcing the cap, and add a regression test in tests/google-antigravity-replay.test.ts for an older session updated after a newer session.

🤖 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 `@src/adapters/google-antigravity-replay.ts` around lines 162 - 214, Track a
session-level activity timestamp in both observeAntigravityReplay and
applyAntigravityReplay, updating it whenever either path refreshes a session. In
persistReplaySnapshotNow, order session candidates by this activity timestamp,
newest first, before applying REPLAY_SNAPSHOT_MAX_BYTES instead of relying on
reversed Map order; persist and restore the timestamp as needed. Add a
regression test covering an older session updated after a newer session so the
recently active session is retained under the cap.
🤖 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 `@tests/google-antigravity-replay.test.ts`:
- Around line 734-736: Replace the fixed Bun.sleep in the debounce test with the
existing write seam, signaling when the failing background write is reached and
waiting for that signal with a bounded timeout before checking warn.mock.
Preserve the warning assertion and existing finally cleanup.

---

Outside diff comments:
In `@src/adapters/google-antigravity-replay.ts`:
- Around line 73-130: Track whether loadReplaySnapshotEntry or load-time
trimming removes any snapshot data, mark the replay snapshot dirty, and schedule
a rewrite once snapshot loading completes so stale entries are removed from
disk. Preserve normal loading behavior when nothing is discarded, and extend the
expiry test to flush persistence and verify the expired session key is absent
from the snapshot file.
- Around line 162-214: Track a session-level activity timestamp in both
observeAntigravityReplay and applyAntigravityReplay, updating it whenever either
path refreshes a session. In persistReplaySnapshotNow, order session candidates
by this activity timestamp, newest first, before applying
REPLAY_SNAPSHOT_MAX_BYTES instead of relying on reversed Map order; persist and
restore the timestamp as needed. Add a regression test covering an older session
updated after a newer session so the recently active session is retained under
the cap.
🪄 Autofix

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: ASSERTIVE

Plan: Pro Plus

Run ID: 43f1b4d3-ad6b-434f-910b-fb3b4f2cb761

📥 Commits

Reviewing files that changed from the base of the PR and between bf257aa and ea942d4.

📒 Files selected for processing (2)
  • src/adapters/google-antigravity-replay.ts
  • tests/google-antigravity-replay.test.ts

Comment thread tests/google-antigravity-replay.test.ts Outdated
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

@github-actions[bot] PR #1434 is marked Ready for Review at ea942d4446a6ab9dd72bd9613911f632270b7ac8. I will review the current head.

⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions
github-actions Bot marked this pull request as draft August 11, 2026 03:52

@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: 1

Caution

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

⚠️ Outside diff range comments (1)
src/adapters/google-antigravity-replay.ts (1)

117-122: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject duplicate call keys during snapshot loading.

A duplicate pair[0] replaces the prior value in byCall, but bytes still includes both call sizes. This can overstate replayBytes, trigger eviction of valid sessions, or drop the only retained call when per-session trimming runs.

Reject duplicate keys and mark the snapshot for cleanup. Alternatively, subtract the replaced ReplayCall.sizeBytes before adding the replacement size. Add a regression test with two serialized pairs that use the same call key.

Proposed fix
     const callBytes = utf8.encode(pair[0]).byteLength + signatureBytes;
     if (callBytes > replayLimits.maxBytesPerSession) continue;
+    if (byCall.has(pair[0])) {
+      replaySnapshotLoadDiscarded = true;
+      continue;
+    }
     byCall.set(pair[0], {
       signature: call.signature,
       sizeBytes: callBytes,
       touchedAtMs: call.touchedAtMs,
     });
🤖 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 `@src/adapters/google-antigravity-replay.ts` around lines 117 - 122, Update
snapshot loading around the byCall map insertion to detect an existing pair[0]
before replacing it, reject duplicate call keys, and mark the snapshot for
cleanup. Ensure duplicate entries cannot inflate bytes or interfere with
per-session trimming, and add a regression test covering two serialized pairs
with the same call key.
🤖 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 `@src/adapters/google-antigravity-replay.ts`:
- Around line 222-225: Update the snapshot size accounting around persistEntry
and replaySnapshotMaxBytes to include the exact UTF-8 byte length of the
complete {"version":...,"sessions":[...]} framing, including commas between
entries, before accepting each session. Preserve the cap when writing the final
snapshot, and add a boundary test that verifies the written file byte length
never exceeds the configured limit.

---

Outside diff comments:
In `@src/adapters/google-antigravity-replay.ts`:
- Around line 117-122: Update snapshot loading around the byCall map insertion
to detect an existing pair[0] before replacing it, reject duplicate call keys,
and mark the snapshot for cleanup. Ensure duplicate entries cannot inflate bytes
or interfere with per-session trimming, and add a regression test covering two
serialized pairs with the same call key.
🪄 Autofix

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: ASSERTIVE

Plan: Pro Plus

Run ID: d9cc0732-3fe8-420f-b76e-3083d9cb9c0a

📥 Commits

Reviewing files that changed from the base of the PR and between ea942d4 and 66e268e.

📒 Files selected for processing (2)
  • src/adapters/google-antigravity-replay.ts
  • tests/google-antigravity-replay.test.ts

Comment thread src/adapters/google-antigravity-replay.ts
@github-actions
github-actions Bot marked this pull request as ready for review August 11, 2026 04:01

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The persistence direction is valid, and the earlier size-accounting, in-flight mutation, and error-propagation blockers have been substantially addressed. I am still requesting changes on current head 244ade8393e4b201077ad5d02b3ba536b8881231 because two durability contracts remain incomplete:

  1. persistReplaySnapshotNow() enforces replaySnapshotMaxBytes against only the sum of JSON.stringify(persistEntry) values. The written document also contains the version/sessions wrapper, brackets, and inter-entry commas, so the final UTF-8 file can exceed the declared hard cap. Account for the exact complete document size before admitting each session (or construct an exactly bounded document), and add a boundary regression that asserts Buffer.byteLength(the entire written snapshot) <= configured cap with multiple small sessions.
  2. flushAntigravityReplay() now bounds retries, but after eight non-converging attempts it returns successfully even when replayMutationGeneration !== replayWrittenGeneration or a timer is still pending. drainAndShutdown() therefore sees a fulfilled flush and emits no failure diagnostic although the latest signature was not made durable. Exhausting the retry budget must return an explicit failure (preferably a fixed, non-sensitive error) so the existing shutdown warning is reachable. Add a deterministic test that mutates during every attempted write and proves the flush fails rather than silently claiming success.

The branch is also two commits behind current dev, and this fork head currently has only the lightweight PR gates rather than exact-head platform/full-suite CI. After the two contracts are fixed, please refresh onto current dev, rerun the focused persistence/lifecycle suites plus typecheck/privacy, and obtain the full exact-head CI before merge.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

@github-actions[bot] The readiness gate is complete. I will review the current PR head, including the durable replay-cache behavior and the previously reported findings.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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: 1

🤖 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 `@tests/google-antigravity-replay.test.ts`:
- Around line 658-687: Update the test around antigravity replay loading to use
a multi-byte UTF-8 signature for the admitted call and assert totalBytes with
TextEncoder byte length rather than character length. Add an oversized
multi-byte signature whose character count is below the signature limit but
whose encoded byte length exceeds it, and verify that session is dropped. Keep
the regression coverage focused near the existing load recomputation test.
🪄 Autofix

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: ASSERTIVE

Plan: Pro Plus

Run ID: a30ea0e3-c64a-4a1a-ae8f-9cbeac73b2fc

📥 Commits

Reviewing files that changed from the base of the PR and between 460d67e and edf6ef0.

📒 Files selected for processing (1)
  • tests/google-antigravity-replay.test.ts

Comment thread tests/google-antigravity-replay.test.ts
@Yuxin-Qiao
Yuxin-Qiao force-pushed the fix/1429-antigravity-replay-persistence branch from edf6ef0 to 89a96ce Compare August 11, 2026 06:29
@github-actions
github-actions Bot marked this pull request as ready for review August 11, 2026 06:32
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

head_sha='89a96ce0ebf6707278bf83a3077ff9b845eb8b0e'

echo '=== Checkout and reported head ==='
git rev-parse HEAD
git cat-file -e "${head_sha}^{commit}"
git show -s --format='commit=%H%nparents=%P%nsubject=%s' "$head_sha"

echo '=== Changed files relative to dev merge base ==='
base_sha="$(git merge-base "$head_sha" origin/dev)"
git diff --name-status "$base_sha" "$head_sha"

echo '=== Replay module outline ==='
ast-grep outline src/adapters/google-antigravity-replay.ts --items all --view expanded

echo '=== Snapshot load and persistence at reported head ==='
git show "${head_sha}:src/adapters/google-antigravity-replay.ts" \
  | nl -ba \
  | sed -n '80,300p'

echo '=== Cache expiry, eviction, mutation, and flush paths at reported head ==='
git show "${head_sha}:src/adapters/google-antigravity-replay.ts" \
  | nl -ba \
  | sed -n '430,650p'

echo '=== Lifecycle shutdown integration at reported head ==='
git show "${head_sha}:src/server/lifecycle.ts" \
  | nl -ba \
  | sed -n '1,260p'

echo '=== Durable replay regression tests at reported head ==='
git show "${head_sha}:tests/google-antigravity-replay.test.ts" \
  | rg -n -C 5 \
    '(duplicate.*session|duplicate.*key|expired.*snapshot|snapshot.*expired|sweepExpiredAntigravityReplay|flush.*blocked|flush.*failure|retry|activity|sizeBytes|permission|replay)' \
  || true

echo '=== Full PR diff for replay module and lifecycle ==='
git diff --unified=50 "$base_sha" "$head_sha" -- \
  src/adapters/google-antigravity-replay.ts \
  src/server/lifecycle.ts \
  tests/google-antigravity-replay.test.ts

Length of output: 2235


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head_sha='89a96ce0ebf6707278bf83a3077ff9b845eb8b0e'
parent_sha="$(git rev-parse "${head_sha}^")"

echo '=== Commit range ==='
git show -s --format='head=%H%nparent=%P%nsubject=%s' "$head_sha"

echo '=== Changed files in head commit ==='
git diff --name-status "$parent_sha" "$head_sha"

echo '=== Replay module structure ==='
ast-grep outline src/adapters/google-antigravity-replay.ts --items all --view expanded

echo '=== Snapshot loading and write scheduling ==='
nl -ba src/adapters/google-antigravity-replay.ts | sed -n '1,330p'

echo '=== Cache mutation, expiration, eviction, and flush behavior ==='
nl -ba src/adapters/google-antigravity-replay.ts | sed -n '400,700p'

echo '=== Shutdown integration ==='
ast-grep outline src/server/lifecycle.ts --items all --view expanded
nl -ba src/server/lifecycle.ts | sed -n '1,280p'

echo '=== Relevant durable-cache tests ==='
rg -n -C 7 --type ts \
  '(duplicate.*(session|key)|expired.*snapshot|snapshot.*expired|sweepExpiredAntigravityReplay|flush.*(blocked|failure|mutation|retry)|activity|sizeBytes|UTF-8|utf8|write.*warning)' \
  tests/google-antigravity-replay.test.ts

echo '=== Head commit diff ==='
git diff --unified=45 "$parent_sha" "$head_sha" -- \
  src/adapters/google-antigravity-replay.ts \
  tests/google-antigravity-replay.test.ts \
  src/server/lifecycle.ts

Length of output: 7542


🏁 Script executed:

#!/bin/bash
set -euo pipefail

numbered_range() {
  local file="$1"
  local first="$2"
  local last="$3"
  awk -v first="$first" -v last="$last" \
    'NR >= first && NR <= last { printf "%6d  %s\n", NR, $0 }' "$file"
}

echo '=== Snapshot load, mutation tracking, persistence, and flush ==='
numbered_range src/adapters/google-antigravity-replay.ts 80 275

echo '=== Expiration and eviction ==='
numbered_range src/adapters/google-antigravity-replay.ts 460 565

echo '=== Observe, apply, clear, metrics, and test reset ==='
numbered_range src/adapters/google-antigravity-replay.ts 565 790

echo '=== Shutdown flushing ==='
numbered_range src/server/lifecycle.ts 1 280

echo '=== All durable replay test names ==='
rg -n --type ts '^\s*(test|it)\(' tests/google-antigravity-replay.test.ts

echo '=== Test blocks for prior findings and later hardening ==='
rg -n -C 18 --type ts \
  '(duplicate top-level|duplicate.*session|expired.*snapshot|snapshot.*expired|flush waits|flush.*failure|background.*failure|bounded.*flush|activity.*timestamp|UTF-8|utf8|sizeBytes)' \
  tests/google-antigravity-replay.test.ts

echo '=== Test-only head commit ==='
git diff --unified=60 c6a3d709f84c7299cb80a68c7594384fda30f7a7 \
  89a96ce0ebf6707278bf83a3077ff9b845eb8b0e -- tests/google-antigravity-replay.test.ts

Length of output: 69299


@github-actions[bot] I reviewed ready head 89a96ce0ebf6707278bf83a3077ff9b845eb8b0e. Two durable replay-cache defects remain.

  1. Duplicate top-level snapshot session keys corrupt byte accounting.
    In src/adapters/google-antigravity-replay.ts, Line 141 executes replayCache.set(key, entry). Line 142 then adds entry.bytes to replayBytes. If the snapshot has two sessions entries with the same key, the second Map insertion replaces the first entry but does not subtract the first entry bytes.

    This makes antigravityReplayMetrics().totalBytes larger than the bytes retained in replayCache. It can also cause evictIfNeeded() at Lines 554-559 to remove valid sessions unnecessarily.

    Before Line 141, reject a session when replayCache.has(key) is true. Set replaySnapshotLoadDiscarded = true before the continue path. Add a test with two valid top-level sessions records that use the same replay key. Verify that the cache has one session, totalBytes matches that session only, and the rewritten snapshot has one record.

  2. Runtime expiry cleanup is not persisted.
    In src/adapters/google-antigravity-replay.ts, deleteExpiredReplaySessions() at Lines 501-503 removes entries from memory only. Both callers at Lines 515-524 omit markReplayDirty().

    Therefore, after sweepExpiredAntigravityReplay() removes an expired session, flushAntigravityReplay() has no mutation generation to write. The existing antigravity-replay.json can retain the expired signature until an unrelated cache mutation occurs.

    Make deleteExpiredReplaySessions() return its removal count. When the count is nonzero, call markReplayDirty() from the throttled cleanup path and sweepExpiredAntigravityReplay(). Add a regression test that persists a session, expires it through sweepExpiredAntigravityReplay(), flushes, and verifies that the snapshot no longer contains that session.

You are interacting with an AI system.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@github-actions[bot] Acknowledged. The readiness gate confirms PR #1434 is ready for review at 89a96ce0ebf6707278bf83a3077ff9b845eb8b0e. This is the same commit I already reviewed. I will not re-review it.

Two findings from that review remain open at this head:

  1. Duplicate top-level session keys in the snapshot corrupt replayBytes. File: src/adapters/google-antigravity-replay.ts, Line 141 and Line 142. Fix: reject a session when replayCache.has(key) is true before Line 141, and set replaySnapshotLoadDiscarded = true.
  2. deleteExpiredReplaySessions() does not call markReplayDirty(). File: src/adapters/google-antigravity-replay.ts, Lines 501-503 and callers at Lines 515-524. Fix: return the removal count from deleteExpiredReplaySessions() and call markReplayDirty() when the count is nonzero.

If a new commit addresses these two points, push it and I will review the new head.

You are interacting with an AI system.

@Wibias Wibias 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.

Request changes on current head 89a96ce0ebf6707278bf83a3077ff9b845eb8b0e.

Two durability contracts are still incomplete:

  1. flushAntigravityReplay() bounds its convergence loop, but exhausting REPLAY_FLUSH_MAX_ATTEMPTS currently falls through and resolves successfully even when replayMutationGeneration !== replayWrittenGeneration or a debounce timer remains pending. In that state drainAndShutdown() observes a successful flush even though the latest thought signature may not be durable. Keep the retry bound, but make budget exhaustion reject with a fixed, non-sensitive error so shutdown diagnostics remain truthful. Add a deterministic regression that forces a mutation during every attempted write and proves the flush rejects rather than silently succeeding.

  2. Snapshot write-cap accounting only sums Buffer.byteLength(JSON.stringify(persistEntry), "utf8"). The actual persisted document also contains the { "version": ..., "sessions": [...] } framing, array brackets, and commas between entries, so the final UTF-8 file can exceed the advertised replaySnapshotMaxBytes hard cap. Account for the exact serialized document size before admitting each session, and add a boundary regression that asserts Buffer.byteLength(the complete written snapshot, "utf8") <= configured cap with multiple entries.

The earlier integrity, in-flight mutation, error-propagation, load-cleanup, duplicate-key, UTF-8 accounting, and redacted-warning fixes look good. After these two remaining contracts are fixed, refresh onto current dev and rerun exact-head CI.

@Wibias
Wibias marked this pull request as draft August 11, 2026 08:31
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo='lidge-jun/opencodex'
pr='1434'

echo '=== Pull request head ==='
gh pr view "$pr" --repo "$repo" --json number,headRefOid,baseRefOid,state,isDraft \
  --jq '"number=\(.number)\nhead=\(.headRefOid)\nbase=\(.baseRefOid)\nstate=\(.state)\ndraft=\(.isDraft)"'

echo '=== Checked-out commit ==='
git rev-parse HEAD

echo '=== Replay module structure ==='
ast-grep outline src/adapters/google-antigravity-replay.ts --items all --view expanded

echo '=== Snapshot loading ==='
nl -ba src/adapters/google-antigravity-replay.ts | sed -n '90,180p'

echo '=== Expiry cleanup and mutation scheduling ==='
nl -ba src/adapters/google-antigravity-replay.ts | sed -n '470,545p'

echo '=== Relevant regression tests ==='
rg -n -C 12 --type ts \
  '(duplicate top-level|duplicate.*session|replayCache\.has\(key\)|expired.*snapshot|snapshot.*expired|sweepExpiredAntigravityReplay|deleteExpiredReplaySessions)' \
  tests/google-antigravity-replay.test.ts || true

Length of output: 7104


🏁 Script executed:

#!/bin/bash
set -euo pipefail

numbered_range() {
  local file="$1"
  local first="$2"
  local last="$3"
  awk -v first="$first" -v last="$last" \
    'NR >= first && NR <= last { printf "%6d  %s\n", NR, $0 }' "$file"
}

echo '=== Snapshot loading ==='
numbered_range src/adapters/google-antigravity-replay.ts 90 160

echo '=== Mutation scheduling and flushing ==='
numbered_range src/adapters/google-antigravity-replay.ts 180 275

echo '=== Runtime expiry cleanup ==='
numbered_range src/adapters/google-antigravity-replay.ts 465 530

echo '=== Regression-test matches ==='
rg -n -C 15 --type ts \
  '(duplicate top-level|duplicate.*session|replayCache\.has\(key\)|expired.*snapshot|snapshot.*expired|sweepExpiredAntigravityReplay|deleteExpiredReplaySessions)' \
  tests/google-antigravity-replay.test.ts || true

Length of output: 17652


@github-actions[bot] The readiness notification does not match the current PR state.

GitHub reports PR #1434 at 89a96ce0ebf6707278bf83a3077ff9b845eb8b0e. The checkout matches this commit. GitHub also reports draft=true.

Two previously reported replay-cache defects remain at this head.

  1. Duplicate top-level snapshot session keys corrupt replayBytes.
    In src/adapters/google-antigravity-replay.ts, Line 141 inserts each loaded session with replayCache.set(key, entry). Line 142 then increments replayBytes unconditionally. A later session record with the same key replaces the map entry but does not remove the earlier entry byte count.

    Add a replayCache.has(key) check before Line 141. Set replaySnapshotLoadDiscarded = true and skip the duplicate record. Add a regression test that loads two valid sessions records with one replay key. Verify one retained session, correct totalBytes, and one session in the rewritten snapshot.

  2. Runtime expiry cleanup does not persist snapshot cleanup.
    In src/adapters/google-antigravity-replay.ts, Lines 501-503 remove expired sessions from memory. The callers at Lines 515-524 do not call markReplayDirty(). Therefore, flushAntigravityReplay() has no new mutation generation to persist after an expiry-only sweep.

    Make deleteExpiredReplaySessions() return the number of removed sessions. When the return value is nonzero, call markReplayDirty() from both deleteExpiredReplaySessionsThrottled() and sweepExpiredAntigravityReplay(). Add a regression test that persists a session, expires it through sweepExpiredAntigravityReplay(), flushes, and verifies that antigravity-replay.json no longer contains the session.

The PR should remain unresolved until these two fixes are present on a new head.

You are interacting with an AI system.

…arts

The antigravity replay cache was process-local, so a tool-call continuation
on /v1/chat/completions 400'd with 'missing a thought_signature' once the
cache was lost (proxy restart, TTL expiry, or eviction pressure). The
OpenAI-format surface has no field for a caller to carry the signature
back, so the conversation was stuck.

Snapshot the cache to antigravity-replay.json in the config directory:
debounced atomic writes on mutation, lazy bounded load on first access,
drop expired sessions at load, and a shutdown flush wired into
drainAndShutdown. The CCA session id is deterministic (anchored on the
first user message), so a restarted proxy re-derives the same key and
re-injects the signatures. Claude-on-Antigravity is unaffected (it uses
inline sanitization, not the replay cache).
@Yuxin-Qiao
Yuxin-Qiao force-pushed the fix/1429-antigravity-replay-persistence branch from 89a96ce to 3dad8a8 Compare August 11, 2026 09:08
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@github-actions[bot] The readiness checklist does not resolve the open review findings.

The reported head is 89a96ce0ebf6707278bf83a3077ff9b845eb8b0e. This is the same head with two unresolved defects in src/adapters/google-antigravity-replay.ts:

  1. Duplicate top-level snapshot session keys inflate replayBytes.
  2. Runtime expiry cleanup does not mark the replay snapshot dirty, so flushAntigravityReplay() can retain expired sessions on disk.

The PR must remain unresolved until a new commit fixes both defects and includes the requested regression tests.

You are interacting with an AI system.

@lidge-jun

Copy link
Copy Markdown
Owner

Landed on dev — closing as superseded rather than merged; the change is already there and the branch now conflicts with it.

Your commits are on dev with authorship intact, plus d065fd0ad closing the two remaining durability blockers.

flushAntigravityReplay() bounded its convergence loop but fell out of it and resolved when the attempt budget was exhausted with the durable generation still behind the latest mutation. drainAndShutdown() then saw a fulfilled promise and emitted no warning, so shutdown reported a durability it could not demonstrate. Budget exhaustion now rejects, with fixed text since the shutdown path logs it and it must not carry session or signature detail.

The snapshot cap counted only per-entry JSON and ignored the {"version":N,"sessions":[...]} framing and the separators, so the written file exceeded replaySnapshotMaxBytes by a margin that grew with each session. Measured: a cap of 1122 bytes produced an 1151-byte file. Accounting now seeds with the exact prefix and suffix, charges one byte per separator, and builds the payload from the same fragments.

You reached equivalent fixes independently on your newer head — REPLAY_FLUSH_INCOMPLETE_ERROR and the per-entry-plus-comma accounting — which is why this closes as superseded rather than replaced.

Verified: tests/google-antigravity-replay.test.ts 55 pass / 0 fail and tests/shutdown-drain.test.ts 17 pass / 0 fail, with both regressions red before the fix. Thanks for the persistent iteration on this one.

@lidge-jun lidge-jun closed this Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants