Skip to content

fix: make veto-deadline recovery test deterministic#460

Merged
BunsDev merged 1 commit into
mainfrom
fix/455-veto-deadline-flake
Jul 22, 2026
Merged

fix: make veto-deadline recovery test deterministic#460
BunsDev merged 1 commit into
mainfrom
fix/455-veto-deadline-flake

Conversation

@BunsDev

@BunsDev BunsDev commented Jul 22, 2026

Copy link
Copy Markdown
Member

Closes #455.

What

Makes api::tests::threads_scheduler_recovers_veto_claimed_before_deadline deterministic. It has been failing repeatedly on windows-latest (three consecutive failures on PR #459's runs, plus the occurrences tracked in #455) and is currently the top CI flake.

Root cause

The test staged a proposal with a real 2-second veto window and relied on two wall-clock races:

  1. Stage → claim latency. The interrupted POST /reject durably records claimed_at = now_utc() before the ClaimBeforeValidation failpoint fires. Recovery judges veto timeliness by that durable claimed_at against veto_deadline = staged_at + 2s (proposal_decision_semantics reject arm → proposal-veto-window-closed). On a slow Windows runner, staging + one GET /threads/proposals + the POST can take longer than 2 s, so the claim lands after the deadline and recovery legitimately vetoes nothing — recovered == 0, the assertion fails. This is the observed CI failure.
  2. Fixed 2.1 s sleep to get past the deadline before replaying recovery.

The issue suggested widening the sleep, but the sleep isn't the racing part — the stage-to-claim latency is, and no fixed window is safe against arbitrary runner stalls.

Fix

Drive both timing conditions through durable state instead of the wall clock, following the idioms the sibling scheduler tests already use (300 s window, backdated staged_at):

  • Stage with staged_at = now - 10 min and a 300 s window, so the veto deadline has already elapsed when recovery runs — the 2.1 s sleep is deleted.
  • After the interrupted reject, pin the durable decisionRequest.claimed_at to staged_at + 1s (round-tripping the module's own ProposalDecisionRequest type), modelling a claim that landed inside the window. Recovery reads claimed_at verbatim from the durable request, so this exercises exactly the recovery-semantics path the test exists to cover: timely claim, post-deadline replay.

No production code changes — test-only diff.

Verification

  • cargo test -p coven-cli threads_scheduler — 5/5 pass; the test now runs in ~60 ms (was ~2.1 s+).
  • Ran the fixed test 5× consecutively — stable.
  • cargo fmt --check, cargo clippy --workspace --all-targets -- -D warnings, cargo test --workspace --locked, python scripts/check-secrets.py — all green.

threads_scheduler_recovers_veto_claimed_before_deadline staged a
proposal with a real 2-second veto window and raced the wall clock
twice: the interrupted reject had to durably record claimed_at within
2s of staging (slow Windows runners exceed this, so recovery correctly
reported the veto window closed and the test failed), and a fixed 2.1s
sleep had to outlast the deadline.

Drive both conditions through durable state instead, following the
sibling scheduler tests' idioms: stage with a 300s window backdated 10
minutes so the deadline has already elapsed (deleting the sleep), and
pin the durable decisionRequest.claimed_at inside the window to model
a timely claim. Recovery judges veto timeliness by the durable
claimed_at alone, so the covered scenario — timely claim, post-deadline
replay — is unchanged; it just no longer races the scheduler's clock.

Test-only change; the test now runs in ~60ms instead of 2.1s+.

Closes #455

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Val Alexander <68980965+BunsDev@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 22, 2026 02:53

Copilot AI 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.

Pull request overview

This PR fixes a flaky Windows CI test by making api::tests::threads_scheduler_recovers_veto_claimed_before_deadline deterministic, shifting the scenario from wall-clock races to durable, backdated scheduler state.

Changes:

  • Expands the test veto window (2s → 300s) and backdates staged_at so the veto deadline is deterministically elapsed without sleeping.
  • Replaces the fixed sleep with direct mutation of the durable decisionRequest.claimedAt timestamp to model a timely claim during recovery.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +9654 to +9666
let raw = std::fs::read_to_string(&pending_path)?;
let mut value: Value = serde_json::from_str(&raw)?;
let mut request = proposal_decision_request(&value)?
.context("interrupted reject left a durable decision request")?;
request.claimed_at = staged_at + time::Duration::seconds(1);
value
.as_object_mut()
.context("pending proposal is a JSON object")?
.insert(
"decisionRequest".to_string(),
serde_json::to_value(&request)?,
);
std::fs::write(&pending_path, serde_json::to_vec_pretty(&value)?)?;
Comment on lines +9629 to 9633
let (pending_path, proposal_id) = stage_scheduled_reviewed_edit(
home,
coven_threads_core::ApprovalPath::FamiliarCoherence { veto },
time::OffsetDateTime::now_utc(),
staged_at,
)?;
@BunsDev
BunsDev merged commit 14801a7 into main Jul 22, 2026
18 checks passed
@BunsDev
BunsDev deleted the fix/455-veto-deadline-flake branch July 22, 2026 03:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

flaky: threads_scheduler_recovers_veto_claimed_before_deadline times out its veto-deadline margin on windows-latest

2 participants