Harden offline repair for oversized session replays - #7039
ruizanthony wants to merge 3 commits into
Conversation
|
|
CI follow-up on
The commit is test-only; production remains the reviewed |
be1e0db to
dd74c5d
Compare
|
Rebased without conflicts onto current |
dd74c5d to
ff7ad21
Compare
ff7ad21 to
cb8c0b3
Compare
|
Remediated the three recovery blockers on exact head |
cb8c0b3 to
58d9009
Compare
|
Final rebase-only refresh: exact head is now |
|
CI follow-up on exact head |
cb6e187 to
5283e1f
Compare
|
Final rebase-only refresh: exact head is now |
|
Exact final candidate: |
|
Final exact candidate after global-suite remediation: Evidence: 128 modified-portfolio tests passed; Ruff diff, |
|
HOLD MERGE on current head |
|
Replacement candidate published: |
|
HOLD on current head |
|
Final independent exact-head verdict for |
|
Final independent exact-SHA review of Evidence on this exact head:
Non-blocking doc note: the offline compactor preserves mode (0600) but not ownership when run as root on foreign-owned sidecars (recoverable via chown; no data loss). This supersedes all prior HOLDs on |
f6b6727 to
d3420c6
Compare
🔬 Gate certification — RED ⛔Certified head: This cert is valid only while the head stays at What I ran (own isolated worktrees, rebase-first)
Findings (blockers)1. [CORE / liveness] 2. [SILENT / data-loss] Hidden-cleanup churn evicts live deletion fences → deleted transcripts resurrect. Maintainer notes (non-blocking, from Opus)
Recommendation to the next agentRED — do not merge. Fix findings #1 (lock-ordering rework) and #2 (fence/cap redesign), then re-warm/re-gate. Both need @ruizanthony's design intent (lock contract + delete-vs-tombstone policy for hidden sessions), so this is a contributor bounce, not a mechanical maintainer fix. The concept (bounded offline session-repair + hidden- Gate-certifier layer (warm-up → gate → release). I do not merge/tag/deploy/close — that's the release agent's call. This cert is valid only while the head stays at |
d3420c6 to
5d0b393
Compare
5d0b393 to
2752e30
Compare
|
Rebased onto current Greptile P1 (cleanup failure reported as success) — fixed. A Verification on 2752e30: targeted suite (PR tests + background/session adjacent files) 926 passed / 1 skipped / 1 xfailed; CI note — |
…eanup Finding 1 (deadlock /btw): _cleanup_ephemeral_session_sidecar_locked no longer acquires _get_session_agent_lock itself — it now documents the lock-caller contract, and the completion path in _run_agent_streaming acquires the non-reentrant per-session agent lock explicitly before cleanup, matching every other session writer. Previously the cancel path already held the lock and the helper self-deadlocked, leaving the private ephemeral sidecar persisted on disk and the worker thread hung forever. Finding 2 (tombstone eviction): hidden /btw/background cleanups record their anti-resurrection fence in a SEPARATE bounded log (_hidden_cleanup_sessions.json, WEBUI_HIDDEN_CLEANUP_TOMBSTONE_CAP=1000) with cross-process authority, instead of sharing the user delete log. Previously >1000 hidden cleanups silently evicted a user delete fence, and repair-safe (recover_missing_sidecars_from_state_db) could re-materialize a deleted transcript. Callers pass tombstone_kind='hidden' in _delete_session_sidecar_artifacts_locked; readers (state.db reconcile, CLI projection, discoverability, compactor, backup-restore) OR both logs; clear-paths (Session.save, new_session, import_cli_session) clear both. Tests: realign test_hidden_ephemeral_cleanup_uses_complete_durable_delete_protocol to the hidden-log fence contract; add tests/test_pr7039_gate_red_regressions.py (9 regression tests covering lock contract, fence separation, churn non-eviction, reader union and clear-paths).
…lure `_delete_hidden_background_session_sidecar()` returns False (without raising) when the hidden sidecar's revision changed under the lock, which leaves the transcript and its recovery artifacts on disk. The background worker ignored that result and published the assistant answer as a success. Route a False result through the same failure branch as an exception: warning log plus an explicit "(background task cleanup failed)" answer, never the success answer. Regression coverage drives `_handle_background` end to end with the cleanup helper patched to return False / raise / return True.
2752e30 to
48fc667
Compare
|
Rebased onto current master |
Thinking Path
The runtime must not parse and copy replay-bloated sidecars inside an HTTP request, but an offline repair is safe only if it stays bounded in memory, coordinates with normal saves, preserves an exact rollback path, and fails closed on malformed or concurrently changed input. Review of the first implementation found three additional recovery hazards: large index rebuilds trusted a 64 KiB key-order prefix, the streaming validator accepted non-RFC JSON whitespace, and source publication was not durably ordered after the manifest rename.
What Changed
messagesandcontext_messagesin separate analysis/write passes;Session.save()and offline compact/restore publication through the same per-SID lock while advancing_sidecar_generation_v1monotonically;/clearis a no-op on an already-empty live session, while retiring pre-clear artifacts after a real truncate; restore malformed live JSON only through a tagged raw-digest CAS that is revalidated before publication;messageswhen no persisted count exists, and reject a missing/invalidsession_idinstead of generating a phantom row;--dry-run --restorerather than performing a destructive restore;Why It Matters
Very large replay-bloated sessions can now be repaired without request-path memory spikes, silent concurrent-write loss, omitted/phantom sidebar rows, malformed JSON republication, or a crash window where the compacted source is durable but its rollback manifest is not.
Operator Contract
Always stop or drain every WebUI process before running this strictly offline tool. The WebUI lifecycle paths hardened by this PR now share the SID authority, generation/epoch checks, and durable deletion fence; older runtimes and direct non-WebUI writers may not participate. The command is POSIX/WSL-only; use WSL rather than native Python on Windows.
RAM is bounded, but the temporary SQLite index needs scratch-disk headroom proportional to unique replay identities. One decoded JSON row is capped at 64 MiB. Strict stale-alias CAS after the maintenance lock is released remains the separate #7036 scope.
Contract Routing
State layers: session sidecar JSON, sidebar
_index.json, per-SID maintenance lock, durable generation, content-addressed backup, and hidden rollback manifest.Relevant public docs:
AGENTS.mddocs/CONTRACTS.mddocs/GUIDELINES.mdARCHITECTURE.mdScope
The cumulative reliability branch changes 15 files across the offline compactor, sidecar/recovery lifecycle, bounded metadata scanner, shared SID authority, and regression coverage. It remains one logical durability boundary: compact, restore, save, recover, delete, cleanup, index, and State DB materialization must agree on the same session lifetime.
This PR supersedes the offline-compaction portion of #6600. Please keep #6600 open as design/review history until the narrower replacements land.
Verification
266 passed— every test file modified againstorigin/master, on exact headf6b67279, with the Hermes Agent runtime provided and zero skips;156 passed— historical recovery/sidecar regression portfolio, zero skips;9/9 PASS— exact-SHA adversarial probes for authority ordering, delete/cleanup fail-closed behavior, epoch ABA, materializers, full-payload backup/restore, partial signatures, complete private cleanup, and bounded scanning;d04170d4failed nine GitHub matrix jobs across all three Python versions, reproducing four historical contracts: no-op clear backup preservation, malformed-live recovery, canonical ephemeral cleanup, and complete delete artifact cleanup; all four are GREEN locally onf6b67279, including a new mutation-between-CAS-reads rejection probe;4e1691c0d15780e37b6a60a35468496a93cedfe5312354540c302051c3e6a070remained stable before/after execution; Ruff diff-scoped,py_compile,compileall, andgit diff --checkpass;f6b67279and are not claimed complete here.Risks / Follow-ups
Model Used
OpenAI Codex /
gpt-5.6-sol, with Hermes read-only delegated adversarial reviews and local deterministic probes.Related