Auto-official finalize respects the open-protest gate (#338)#344
Merged
Conversation
The auto-official protest-window driver (spawn_auto_official_driver in crates/app/src/source.rs) appended its Finalized transition directly to the log, bypassing the command handler — so the #330 "cannot finalize: resolve open protests first" guard (P1-4) never applied to it. A protest filed during the protest window could be auto-finalized over, defeating the point of the window. Fix: at window expiry the driver now checks the SAME predicate the manual Finalize command is gated on. open_protest_count (filed protests minus non-reversed resolutions) is promoted to pub in gridfpv_server::control_handler and reused by the driver — one definition of "still contested" for both finalize paths, no duplicated count logic. A log read failure at expiry also stands down (fail closed, never finalize blind). Chosen behavior when protests ARE open at expiry: the driver appends nothing and leaves the heat Unofficial for the RD — no retry loop. Rationale: a filed protest pulls a human into the loop, and the RD's follow-up is often several rulings (resolve, then a penalty, then finalize); an auto-finalize firing at the surprising instant the last protest resolves could race those. The driver is a one-shot task per Unofficial entry (cancelled by the bridge on any transition), so standing down fits its structure with no new lifecycle; the RD's manual Finalize — re-checked by the same gate on the command path — closes the heat with one click. Adds the driver's first integration tests (bridge harness over Practice, as the failover tests): - auto_official_finalizes_after_the_window_with_no_protests: pins the happy path — deadline fact logged, window elapses, heat folds Final. - auto_official_stands_down_on_an_open_protest_until_resolved: window expiry with an open protest appends no Finalized (heat stays Unofficial), manual Finalize is blocked by the same gate, and resolving the protest unblocks the manual Finalize to Final. Gate: cargo test --workspace (855 passed, 0 failed), cargo fmt, cargo clippy --all-targets -- -D warnings clean. Co-Authored-By: Claude Fable 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.
Fixes #338 (adversarially-verified release-review finding, user-approved fix).
The auto-official protest-window driver appended
Finalizeddirectly viastate.append, bypassing the #330 open-protest guard on the Finalize command path — a protest filed during the window was silently finalized over, defeating the window's purpose.Fix:
open_protest_count(filed minus non-reversed resolutions, RulingReversed-aware) is promoted to the shared predicate for BOTH finalize paths; after the window sleep the driver re-reads the log and stands down if protests are open (or the log can't be read — fail closed), leaving the heat Unofficial for the RD. No retry loop, deliberately: the driver is a one-shot task per Unofficial entry, and once a protest pulls a human in, an auto-finalize racing the RD's follow-up rulings would be worse than requiring their manual Finalize (which re-checks the same gate).Tests: first integration coverage for this driver — happy path (window expiry auto-finalizes) + stand-down path (open protest → stays Unofficial → manual Finalize blocked → resolve → manual Finalize lands).
855 workspace tests green, clippy clean.
🤖 Generated with Claude Code