Repository navigation
Conversation
…unded by its own timeout Phase 3 of a shadowed cut (base check, then seal) now runs under context.WithoutCancel with a 15 s bound, so a client that hangs up after a matched comparison no longer discards it. The witness append and TSA anchor are detached too. A WARNING line is logged when the signing step fails after a shadow check. Docs state the full latency bound and the two-writer and backoff wording more precisely. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d three mutants The statement gains the snapshot base check and the signing step that is not cancelled by the caller. Relocked covers: shadowOffLock, compareShadow, cutCheckpoint (the statement still holds for each: same bytes signed, same base check, same classification, ALERTs and counters). New mutants kill by the new offlock tests; the cannot-run-refuses mutant is re-cut for the new compareShadow return shape. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Re-cut the shadow-under-write-lock mutant for the three-value compareShadow so it is killed by behaviour, not a compile break. Add a test for the phase-2 snapshot-commit failure after a could-not-run, and one that a mismatch logs no not-signed WARNING. Reword the not-signed log line to be true for both paths. Make the docs and claim texts precise about the unknown commit outcome, the refusal and raced outcomes, the 10 s commit bound and the PostgreSQL-only 45 s bound. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-up while waiting for the write lock The witness append is given a 10 s deadline that the file witness checks only before it starts writing. The WARNING for a failure at the read snapshot's commit is logged only when the shadow could not run. CONFIGURATION now includes the signing step's database commit. The log line no longer says nothing was signed (the commit outcome can be unknown). New Mem test: a client gone while phase 1 waits for the write lock stores nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Wave 2: held for the lead; do not merge before the v1.0.1 decision.
Closes the averin#59 low findings tracked in FeirAI/feir-ops#27.
Problem
After averin#59 the shadow comparison runs off the write lock, but phase 3 of a shadowed cut (base check, then seal) still ran on the caller's request context, while the shadow can take budget + 5 s. A client that hangs up after a matched comparison signed nothing, and on a latched project every cut is shadowed, so such a client never got a checkpoint stored and each attempt cost a full O(history) read with no backoff. When the could-not-run ALERT had been logged, a later failure left that ALERT claiming a signature that never happened. API.md also understated the latency bound, and four docs/comment statements were broader than the code.
Design
context.WithoutCancel(ctx)with its own 15 s bound (checkpointSignTimeout, above the 10 s lock wait limit), in the same goroutine. It still runs the unchanged O(1) base check first and signs only the compared bytes. The gate wait and phase 1 stay cancellable, so a client that is gone before the decision costs nothing.createCheckpointare detached too (their existing 10 s bounds stay). Without that, a hung-up cut would be stored but neither witnessed nor anchored.compareShadowreports whether the full computation could not run (carried inshadowResult.unchecked). NewlogCutNotSignedwrites one line startingWARNING(notALERT) on any failure at the signing step, and after a failed read-snapshot commit that follows a could-not-run ALERT. ALERT texts, counters, latch,verifiedand backoff are unchanged and are not undone if signing then fails.createCheckpointcomment replaced by a correct one.Invariants
createCheckpointTxis byte-identical). A mismatch or a full-computation refusal still returns in phase 2 before any lock or seal.Tests
TestCheckpointCutShadowOffLockClientCancel(Mem) and...ClientCancelPostgresrewritten: after a client hang-up the cut returns no error, seq 1 is stored, the body equals the compared body, checks 1, mismatch 0, failed 0, gate count 0....ClientCancelWitnessedAndAnchored: witness holds exactly the new checkpoint and seq 1 is anchored. Confirmed locally that it fails withcontext canceledwarnings when the witness/TSA detach is reverted....SignStepIsBounded: with the could-not-run path and the project write lock held elsewhere, phase 3 returnscontext.DeadlineExceededwithin its bound, nothing is stored, the failed counter stays 1, the log holds the ALERT then the WARNING with the could-not-run note (and no second ALERT), the next cut inside the backoff signs seq 1 unshadowed; also pins the default bound at 15 s (docs drift guard)....RacedBeforeSnapshot: closes the untested phase-2 base-check branch ofshadowOffLock(raced 1, checks 0, mismatch 0, failed 0, next cut shadowed).Formal
Claim
checkpoint-cut-incremental-equals-full: statement extended (snapshot base check; signing step not cancelled by the caller, own 15 s bound; response-time note in does_not_establish). Relocked covers:shadowOffLock,compareShadow,cutCheckpoint; the statement still holds for each (same bytes signed, same base check, same classification, ALERTs and counters; only the context of phase 3, a carried flag and a log line changed). No other cover changed. New mutants:checkpoint-cut-shadow-sign-on-caller-ctx,checkpoint-cut-shadow-sign-unbounded,checkpoint-cut-shadow-skips-snapshot-base-check;checkpoint-cut-shadow-cannot-run-refusesre-cut for the new return shape. All 26 mutants of the claim: 26 killed, 0 survived. FORMAL.md regenerated; legacy claims.json statement and proofs updated.Gate output lines
Host note: the default python3 here is 3.8, where
make check-claimsfails onPath.is_relative_to(also on base, unrelated); the claim checks above ran under python3.12.Dependencies
No dependency change and no Rust change, so
sfw make supply-chainis not needed and feir-osdeploy/eve-isolated/native-verifier/Cargo.lockneeds no relock.Risks
🤖 Generated with Claude Code