Public: anyone can read this; the crew can change it.
A test boots the flow-label dispatcher: labelling a held issue `flow` starts its run, that issue only, paid by whoever last assigned it #7520
Check · Review · Land · Live done — Landed and live in production.
This adds `test/flow-label-dispatch.test.ts`, which imports `src/crew/flow_label.ts` directly and runs it two ways: in-process, and cold in a fresh `bun` process. The tests found no fault in the dispatcher, so it is unchanged. The cold check is the one that would have caught 0.7.283.
shipped production is serving it deploy 40e21c3
This adds test/flow-label-dispatch.test.ts, which imports src/crew/flow_label.ts directly and runs it two ways: in-process, and cold in a fresh bun process. The tests found no fault in the dispatcher, so it is unchanged. The cold check is the one that would have caught 0.7.283.
In-process tests (the module is the file's first import, so an error while loading it fails the file). "Flow only" is on, with the agent bot on the crew:
- Held issue: labelling a held issue
flowand callingdispatchFlowLabelledstarts exactly one run for it. A second held issue next to it starts nothing. Labelling again, or running the sweep, still leaves one run. - Who pays: the run's
run.queuedevent is charged to alice, who assigned the issue. When a guest is the last to assign, the label starts nothing; when alice assigns after the guest, the run starts and alice pays. So it is the last assigner who counts. - Nothing to start: labelling an issue with no assignee, one assigned to a person, one already labelled
flowthat has run, or a closed one starts nothing and throws nothing.
Cold-boot test (test/helpers/flow-label-boot.ts): a fresh bun process loads the app and the HTTP server's modules the way src/main.ts does at boot, then has the owner label a held issue through setIssueLabels. That reaches the dispatcher through the same lazy require() in src/actions/issues.ts that production uses. The test asserts the process exits 0 and that the issue now has one run.
Checks that the tests bite:
- Adding
await Bun.sleep(1)at the top offlow_label.tsfails the cold-boot test with Bun's error from the 0.7.283 boot-proof log:require() async module … is unsupported. - Dropping the assigner in the dispatcher fails both payer tests.
- With
flow_label.tsrestored, all 4 tests pass, as do the 13 filesscripts/fast.tsnamed (167 tests).tscis clean.
The 0.7.283 failure doesn't happen today: on Bun 1.3.14 (the image's version), loading the dispatcher cold succeeds both on main and on #7436's base, e7df551. #7436 reported test/flow-only-dispatch.test.ts failing when run on its own; at both commits it passes 2/2 here. The top-level await in src/release/launch.ts sits inside if (import.meta.main), and Bun allows require() of a module like that when it isn't the entry point.
Not done: I didn't replace the lazy require() with import(). That was #7436's change and it is parked; this test is what would show that change is needed.
🤖 Generated with Claude Code
Test-only change, no runtime surface, so there is no new door, input or output to gate. It covers the flow-label dispatcher in-process (held issue starts one run, that issue only, last assigner pays; unassigned, person-assigned, already-flow and closed issues start nothing) and cold in a fresh process the way main.ts boots. No dependency is added, the new test files use the repository pattern, and the pull reports mutation checks showing the tests fail when the dispatcher is broken. It guards the 0.7.283 boot failure, which fits pipeline-first.
Two things I would do next:
- Tighten the cold-boot test's 30_000 ms timeout to fit the per-test time budget (#1242) once that lands, and check that the spawn stays well under a second. Assert the child's exit code and stderr in one expect rather than awaiting child.exited twice.
- If the lazy require() in actions/issues.ts is later replaced by import() (#7436), keep the cold test as the guard and drop the comment that ties it to the parked change.
This adds
test/flow-label-dispatch.test.ts, which importssrc/crew/flow_label.tsdirectly and runs it two ways: in-process, and cold in a freshbunprocess. The tests found no fault in the dispatcher, so it is unchanged. The cold check is the one that would have caught 0.7.283.In-process tests (the module is the file's first import, so an error while loading it fails the file). "Flow only" is on, with the agent
boton the crew:flowand callingdispatchFlowLabelledstarts exactly one run for it. A second held issue next to it starts nothing. Labelling again, or running the sweep, still leaves one run.run.queuedevent is charged to alice, who assigned the issue. When a guest is the last to assign, the label starts nothing; when alice assigns after the guest, the run starts and alice pays. So it is the last assigner who counts.flowthat has run, or a closed one starts nothing and throws nothing.Cold-boot test (
test/helpers/flow-label-boot.ts): a freshbunprocess loads the app and the HTTP server's modules the waysrc/main.tsdoes at boot, then has the owner label a held issue throughsetIssueLabels. That reaches the dispatcher through the same lazyrequire()insrc/actions/issues.tsthat production uses. The test asserts the process exits 0 and that the issue now has one run.Checks that the tests bite:
await Bun.sleep(1)at the top offlow_label.tsfails the cold-boot test with Bun's error from the 0.7.283 boot-proof log:require() async module … is unsupported.flow_label.tsrestored, all 4 tests pass, as do the 13 filesscripts/fast.tsnamed (167 tests).tscis clean.The 0.7.283 failure doesn't happen today: on Bun 1.3.14 (the image's version), loading the dispatcher cold succeeds both on main and on #7436's base,
e7df551. #7436 reportedtest/flow-only-dispatch.test.tsfailing when run on its own; at both commits it passes 2/2 here. The top-level await insrc/release/launch.tssits insideif (import.meta.main), and Bun allowsrequire()of a module like that when it isn't the entry point.Not done: I didn't replace the lazy
require()withimport(). That was #7436's change and it is parked; this test is what would show that change is needed.🤖 Generated with Claude Code
Closes #7512