Repository navigation
fix(sessions): make host ingest and hook redrive converge instead of re-driving permanent failures - #4567
Conversation
Deterministic per-item failures were re-driven forever: the in-process source ledger forgot every converged source at each daemon restart, the cover-past cursor advance was the only convergence mechanism for refused records (and a non-retryable advance failure re-failed the whole file every pass), and owed hook replay work was redriven every sweep with no backoff or cap. - pi: durable per-path completion and header-quarantine memos keyed by (len, sub-second mtime); an in-process discovery skip set for malformed headers; unchanged converged files never re-enter admission - shared JSONL admission (pi, codex, kimi, vibe, cursor): a durable, generation-scoped refusal high-water memo acts as a never-regressing secondary frontier; a non-retryable cover-past advance failure now advances in-memory with one warning instead of failing the file; repeats below the memo frontier log at debug - hook v2 replay: per-envelope exponential backoff (30 s to 1 h) and an in-process terminal cap after 16 attempts, with one terminal warning naming the envelope; the durable ledger is untouched Fixes the non-converging loop family behind ScriptedAlchemy#4481 and ScriptedAlchemy#4564.
|
- refusal memo floor only applies with a resume fingerprint, so the scanner validates the skipped prefix; a rewritten file diverges into committed-checkpoint recovery instead of skipping changed bytes - the floor re-baselines the in-memory cursor so later frames advance contiguously; the cover-past fallback warns once per pass - path memos bind the unix inode, closing the equal-length timestamp-preserving replacement case - header quarantine (in-process and durable) records only deterministic InvalidSessionHeader failures; SessionHeaderUnavailable stays retryable
|
Review findings addressed in 5797d25:
Full sessions suite: 635 passed, 0 failed. Re: the changeset bot — this repo releases via release-please from conventional commit titles (no .changeset directory exists), and both commits follow the convention. |
The parse-offset writer is monotonic on mtime, and a prefix fingerprint is unordered: a later refusal with a smaller fingerprint value wrote as a no-op and stranded the durable floor behind the in-memory one. The memo now persists via the exact compare-and-swap replace, which orders on nothing but the (generation, offset, fingerprint) pair it carries. The coverage-fallback once-per-pass flag was inverted: mem::take reset it to false on every frame, warning per frame after all. mem::replace sets it, keeping the warn to the first fallback of the pass.
|
Both new findings addressed in 0380c4d:
|
A get_parse_offset read failure is not an absent row: fabricating a zero expected value CAS-fails against a real row and buries the read failure inside a replace conflict. The read failure now keeps the in-memory mark, surfaces its outcome, and defers the persist.
|
Addressed in the follow-up commit: |
9ea42c7 to
39a49de
Compare
cbaedba
into
ScriptedAlchemy:master
Apply the pinned repository formatter to two files introduced by the concurrent #4567 merge so the standalone LSP repair can pass the existing repository-wide formatting gate. No behavior changes.
Apply the pinned repository formatter to the Pi and hook replay files introduced by the concurrent #4567 merge. The corruption assertions and all other source changes remain unchanged.
* test(graph-db): assert corruption event severity and context * style(sessions): format newly merged ingest and replay code Apply the pinned repository formatter to the Pi and hook replay files introduced by the concurrent #4567 merge. The corruption assertions and all other source changes remain unchanged.
* fix(lsp): reject expired analyzer writes before admission Refuse expired semantic work before claiming or starting a retained client. Check the deadline at every frame queue admission poll, including recovery of write capacity, while preserving completion semantics for queued work. Correlate expired and live workspace-symbol requests in the multi-language regression without weakening EOF or cancellation assertions. Cover expired operations and queued frame expiry through the actual authority and writer. * style(sessions): format newly merged ingest and replay code Apply the pinned repository formatter to two files introduced by the concurrent #4567 merge so the standalone LSP repair can pass the existing repository-wide formatting gate. No behavior changes. * test(lsp): name the ready poll in the occupied-writer match Clippy's match_wildcard_for_single_variants rejects the bare wildcard that can only be Poll::Ready. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
fix(sessions): make host ingest and hook redrive converge instead of re-driving permanent failures
Fixes the non-converging retry family behind #4481 and #4564. On the reporting machine the daemon pinned ~180% CPU indefinitely: 68,402 admission-refusal warnings (100%
observation_retrieval_anchor_alias_collision) across ~640 Pi sessions over two days, 176 discovery header failures per pass, and one hook redrive warning every 30 s — all from work that re-runs forever because per-item permanent failures are never memoized durably.Root pattern
Several sweeps re-drive items whose failure is deterministic:
SourceLedgerforgets every converged source at each daemon restart, so the whole corpus (observed: 1,029 files / 713 MB) is re-admitted from scratch.Changes
1. Durable per-path convergence memos for Pi (
hosts/pi.rs)(len, mtime)durably; a later sweep (including after a daemon restart, where the in-process ledger is empty) skips the unchanged file before opening it.(len, mtime)changes, instead of being re-opened and re-failed every pass.2. Refusal high-water memo for the shared JSONL admission (
jsonl_observation_admission.rs)refused_throughmark under the file generation, via the existing parse-offset authority.3. Hook pending-work redrive backoff and cap (
hook_v2_replay_consumer.rs)Deliberately out of scope (follow-ups)
codex_sibling_over_a_retention_released_owner_is_a_typed_alias_collisiondocuments the current semantics.state.dbrow admission has its own cover-past with the same intent; it does not share the JSONL seam and keeps its current behavior.registry.rshot spawn sites): the supervisor already backs off panics and exits on completion; the load there was driven largely by the ingest storm this PR removes.Tests
New:
converged_session_file_is_not_re_admitted_by_a_fresh_sweep— a freshSourceSweepmodels a daemon restart; the durable completion memo must prevent re-admission.malformed_session_header_is_not_re_failed_by_a_fresh_sweep— unchanged malformed files are quarantined after their first failure.refusal_memo_floor_applies_only_within_its_generation— the memo floor is generation-scoped and never regresses.pending_work_redrive_backoff_doubles_then_caps— backoff math.Updated (they asserted the old re-fail-forever semantics):
discovery_summary_reports_changes_and_recovery_without_repeating_unchanged_failures— quarantined files stop counting; a rewrite (sub-second stamp change) retries them.repeated_discovery_summary_keeps_malformed_sources_and_coverage_refused— a later pass reports zero new failures while retaining coverage.Full
tracedecay-sessionslib suite: 635 passed, 0 failed (run with--features tracedecay-rusqlite-runtime/test-runtime; without the feature the cargo harness runs async tests with no process runtime and they fail on both this branch andmaster— CI exercises the Bazel targets instead).Observed impact expected
On the reporting machine, after one full pass post-upgrade: no re-admission of the unchanged 1,029-file corpus on restart, the anchor-collision storm reduced to at most one refusal per record per generation, and the 30-second hook redrive warning gone after bounded retries.