Skip to content

nextest fail-fast discards verdicts: one failure hid 15 unmeasured commands and the shard read as '1 failure', not '1 failure + 15 Unknown' #3587

Closed as not planned

Description

@noahgift

Operator ruling, 2026-09-20: fail-fast is a verdict-discarding default.

What happened

workspace-test-shard (3/3) on #3581 failed one test — falsify_ggml_003_extractor_case_table_passes, from the unguarded git rev-parse --show-toplevel (#3586). Then:

FAIL  [1/16] exit 101: cargo test -p aprender-core --test monorepo_invariants
      fail-fast: the 15 command(s) after it did not run

Fifteen commands never ran, and nothing downstream said so. The shard reports one failure. The truth is one failure and fifteen unmeasured. Among the fifteen was the PR's own new 350-aprender-quant-ggml-traits-fixture.cmd fragment — so the change most in need of proof on that head was the change least exercised, and the receipt could not tell anyone.

Why it is a verdict defect, not a speed setting

Fail-fast saves runner time by converting fourteen unknowns into an absence. An absence reads as "nothing to report", which is indistinguishable from "passed" to every consumer downstream — the same shape as a gate that returns GO because its producer emitted no rows (#3561), and as a status reader taking the oldest line of an append-only file. Skipped is Unknown. It is not green.

What lands

  1. --no-fail-fast on the shard. The cost is runner minutes on an already-red shard; the benefit is that a red run still measures everything else it was going to measure — which is exactly when you most want the data.
  2. The reducer reports ran / skipped / total in the shard receipt. The skipped count is a number, never an absence.
  3. A receipt with skipped > 0 is Unknown, not Fail and not Pass — it names what was not measured, per doctrine 4.

Falsifier: plant a failing test early in a shard's command list and require the receipt to report the exact count of commands that still ran, and the exact count skipped. A receipt that reports only the failure fails the control.

Note for whoever takes it

This interacts with a standing lesson — nextest fail-fast hid every other dark failure: 7 rounds for 4 tests. That was the same mechanism costing round-trips; this is the same mechanism costing coverage nobody knew was missing. One fix addresses both.

Refs #3080, #3581, #3586, #3561

Activity

  1. added this to the 0.69.0 milestone on Sep 20, 2026
  2. added
    bugSomething isn't working
    P1High priority
    on Sep 20, 2026
  3. modified the milestones: 0.69.0, 0.70.0 on Sep 20, 2026
  4. noahgift commented on Sep 20, 2026

    @noahgift
    MemberAuthor

    Four instances measured on 2026-09-20, all on #3581 or #3420. The fourth is the number that decides this.

    # shard the one failure what it hid
    1 #3581 workspace-test-shard (3/3), job 106075655182 falsify_ggml_003 (git refused the bind-mounted tree) fail-fast: the 15 command(s) after it did not run
    2 #3581 workspace-test-shard (1/3), job 106078925945 the_tracked_repo_graph_is_fresh (stale contracts.nt) FAIL [13/17] … the 4 command(s) after it did not run
    3 #3581 workspace-test-shard (3/3), job 106087864812 falsify_ggml_003 (python3: command not found) FAIL [1/16] … the 15 command(s) after it did not run
    4 #3420 workspace-test-shard (1/3), job 105966962481 qdc_dispatch_sites_match_oracle 8576/21113 tests were not run due to test failure

    Instances 1–3 are commands; instance 4 is 8,576 individual tests, 41% of that shard, discarded because one test in it failed. nextest says so itself: "run with --no-fail-fast to run all tests, or run with --max-fail".

    Why the count matters more than the principle. "A fail-fast shard might be hiding something" is a shrug. "One shard hid 8,576 tests on a PR in the 0.69 must-carry set, and nobody can say whether any of them were red" is a measurement, and it is unfalsifiable after the fact — the run is gone and the only honest verdict for those 8,576 is Unknown, never pass.

    The compounding cost, measured on #3581 today. Adding one contract file staled three tracked derivatives — census.json, contracts.nt, and the README CONTRACT_COUNT block. Each was invisible until the one before it was fixed, because the first failure ended the run. Three CI rounds, on a pool at zero idle, to learn three facts that a single --no-fail-fast run would have reported at once. Fail-fast does not only discard verdicts; it serializes discovery at one fact per round.

    So the ran / skipped / total field this issue asks for is not bookkeeping — on instance 4 it is the difference between reading "1 failure" and reading "1 failure + 8,576 Unknown", and those are different states of the world.

    Refs #3581, #3420, #3430

  5. added
    kind:codeWork is a code change (derived rule, #4159)
    on Sep 24, 2026
  6. modified the milestones: 0.70.0, backlog on Sep 24, 2026
  7. noahgift commented on Sep 25, 2026

    @noahgift
    MemberAuthor

    Parked by APR-EPIC-001: over S1 budget (rank 33/163). Reopen = pull. Applied by aprender-d1: claude+agy both said close, apr shadow row recorded (handoff/close-quorum-2026-09-25.json).

  8. added
    parkedClosed over epic budget (APR-EPIC-001 rule 5). Reopen = pull.
    on Sep 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High prioritybugSomething isn't workingkind:codeWork is a code change (derived rule, #4159)parkedClosed over epic budget (APR-EPIC-001 rule 5). Reopen = pull.

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions