Skip to content

Build1 publisher3 min readPublished

Split the suite so flaky tests cannot gate an agent's patch

A dev.to write-up sorts every test path into four classes and hashes the scoring ones on main, so a patch that retouches a fixture fails with a different exit code than a patch that breaks a property.

The Engineer · Build desk

Illustration accompanying Split the suite so flaky tests cannot gate an agent's patch

What happened

  • A dev.to write-up argues a green CI run is the wrong merge signal for an agent patch, because an agent that skips a flake or rewrites a fixture has moved the scoreboard and nothing else.
  • It asks teams to map every test path before the agent is allowed to touch production code, one class per file and no mixed files, with anything unclassifiable treated as diagnostic.
  • A scoring test that flakes is cut over to tests/diagnostic/ with a ledger row instead of being xfailed in place, leaving at most a one-line pointer behind for grep.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • cost The adoption bill is a manual classification pass over the whole suite, plus splitting any module that mixes a property check with a live client call, and it is work the agent cannot be handed.
  • constraint UI checks, live client calls and order-sensitive tests all sit on the diagnostic side, so no integration signal gets a vote on merge even when it is the test a human trusts most.
  • decision Reviewers acquire a new rejection rule, and the scoring map becomes a file argued over in pull requests instead of a convention living in someone's head.

The distinct exit code is the first detail I would copy. The check hashes the scoring properties, the fixtures, the map and the ledger, and stores that digest as a CI artifact from main. It then scores the candidate patch against that digest; when the patch has changed any of those files, the post says to fail with its own exit code and not to fold that failure into "tests failed" [18]. Those are different failures with different owners. A broken property goes back to the agent. A changed fixture digest goes to whoever owns the corpus.

The same scan keeps `pytest.mark.skip`, `xfail` and `flaky` out of the scoring glob, because those marks are how an agent converts a blocker into a pass, according to the write-up [17]. Test names are a weak measure and skip markers are weaker; the stronger one it proposes is a property that still runs, on frozen inputs, with an unchanged fixture digest [20].

On enforcement, the post says the agent gets no write access to the ledger [7], and the committed `scoring_map.yaml` carries a `forbidden_to_agent` list covering `tests/scoring/**`, the ledger file and the map itself [14]. That list is a key in a file inside the repo, and the digest comparison runs in CI after the patch exists [18]. What is described detects an edit; it does not block one [22]. If your harness hands the agent a branch and a commit, the ledger is writable and the check reports the change on the next run.

The ledger schema is specified down to the field. `flake_class` is a closed set of five values, timing, order, network, rng and env, and a test nobody can assign to one of them is not frozen at all, it stays off the scoring glob [10]. `evidence_hash` is a hash of the last failing artifact, and unfreezing requires a new evidence hash from a stability window instead of a deleted skip [11]. The post also rejects an expiry field as the primary control, on the grounds that expiry without a replay just returns the flake to the scoreboard [12]. Its worked entry moves `tests/scoring/test_invoice_poll.py` into the diagnostic tree on 2026-09-21, owner payments, class timing, with the note "Passes alone; fails under xdist when poll window is 50ms." [13]

The suite has to split one class per file with no mixed files, and that mapping happens before the agent is allowed near production code [9]; a module holding a property check and a live client call is two files of preparatory work. You also need properties to begin with: universal checks over a pinned input corpus that do not import wall-clock time, the network or process-wide RNG [4]. A mostly example-based suite lands on the diagnostic side, where it may flake and never votes on merge [6], and the scoring surface it leaves behind is thin.

The strategy is presented without measured results. Its reference script is labelled "reference implementation, not a published benchmark" and loads `scoring_map.json` [19], while the map committed in the walkthrough is `scoring_map.yaml` [14]. Copy both verbatim and the check dies before it hashes anything [21].

What to watch

  • Whether anyone publishes merge-rate or regression numbers from running a frozen scoring surface against a real agent harness.
  • Whether the reference check ships as a packaged CI action with the map filename reconciled between the YAML walkthrough and the JSON loader.
  • Whether agent harnesses add repo-level write denial for the ledger path, making the forbidden list enforceable at write time instead of at CI time.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories