Public: anyone can read this; the crew can change it.

URGENT flaky gate: "the fork flow > a mirror whose git is not ready yet is fetched again, and lands" fails on most checks #546

Merged opened this in answer to

Check · Review · Land · Live done — Landed and live in production.

Merged

This fixes the flaky test "the fork flow > a mirror whose git is not ready yet is fetched again, and lands" (#541) by taking the clock out of it. The test now controls the order directly instead of racing the mirror's retry. Only the test and the injection point changed; how a mirror imports is the same.

shipped production is serving it deploy 119f4d2

opened this

This fixes the flaky test "the fork flow > a mirror whose git is not ready yet is fetched again, and lands" (#541) by taking the clock out of it. The test now controls the order directly instead of racing the mirror's retry. Only the test and the injection point changed; how a mirror imports is the same.

The race. The test needs the first fetch of octo/late to find nothing and the retry to find the whole repository. Making that repository takes two requests: create it, then commit a.txt.

  • Older version (the one the failing checks ran): it seeded on a 300 ms timer. A fetch could land between the two requests and succeed against an empty repository, so there was no retry. The job ended done with no branches, which matches every failing log (Received: [], about 3.5 s).
  • Current version (from #497): it checks the job's stage every 25 ms, waiting for it to get past the first fetch. Against the fake origin the stages after the fetch last only milliseconds, so the check can miss them and next see the retry's fetching stage. The test then waits out the only retry, which fails because nothing is seeded yet, and seeds too late, so the job ends failed.

The fix.

  • src/origins/mirror.ts: firstFetchRetry gains a wait(ms) function. The forge's default is still a plain timer, so production behaviour does not change.
  • test/fork-flow.test.ts: the test replaces that wait with the seeding itself. The first fetch runs before anything exists upstream, and the retry runs only after both seed requests have answered. A new check, expect(seeded).toBe(1), fails if the retry path is never taken. The polling loop and the 8 s delay are gone, and the global is now put back in a finally, so a failing assertion no longer leaks it into the next file. No retry or skip wrapper was added.

What I chose not to do. I did not make "the fetch worked but brought no branches" count as not ready yet. That empty state only happened because the test seeds in two requests. Forgejo reports pushedAt from updated_at, which an empty repository also has, so the API can't reliably say "this repository has commits".

Proof.

  • Full gate: the forge.json check (tsc plus all 45 files over 3 shards) passed once on this branch: 668 tests in 376 s.
  • 20 runs, all green: bun scripts/test-shards.ts --timeout 60000 -- fork-flow forks cross-repo-pull pulls checks crew passed 20 times in a row (72 tests each, 57–90 s per run).
  • Limits of that proof: the 20 runs were on a 4-core sandbox, not the 3-core forge pod, and over those six heavy files rather than the whole suite. Twenty full gates would take about two hours.

To try it, run bun test --timeout 60000 test/fork-flow.test.ts, or the command above in a loop.

🤖 Generated with Claude Code

Closes #541

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs