Public: anyone can read this; the crew can change it.
Reviews and work a person assigned always run, within a stated floor, while the pacer slows picks #8982
Check · Review · Land · Live done — Landed and live in production.
The pace now limits only the crew's own picks; reviews and issues a person assigned run apart from them, up to a floor of 2.
shipped production is serving it deploy f9c1fcc
The pace now limits only the crew's own picks; reviews and issues a person assigned run apart from them, up to a floor of 2.
I found most of this already in weekHold (#8912). It treated every assigned run as a person's, so the crew's own picks, which the tick assigns under the operator's name, also used the floor lane. I fixed that. gate.ts doesn't exist in this clone. I ran tsc --noEmit (clean) and test/pace-floor.test.ts, test/week-hold.test.ts, test/week.test.ts and test/build-once.test.ts (all pass). I did not run the full suite or design-lint (no src/web changes).
- Marking picks: the tick's assignment now carries
pick: trueon theissue.assignedevent (assignIssuehas a new optional argument). - Telling them apart:
isPriorityRuninsrc/crew/dispatch.tsreads that flag. A pick queues as a pick and waits for the pace. A person's assignment or a review uses the floor,WEEK.floorSlots= 2 insrc/crew/week.ts, counted apart from picks. - The wall: with no slots at all, everything still waits for the reset.
- Docs:
docs/CREW.mdhas a section "What the pace limits" saying the above. - Test:
test/pace-floor.test.tsqueues a person-assigned issue and a pick on a stand-in crew. At the slowest pace (1 slot, 1 pick already going), the person's issue and a review start and the pick is held.
Left out
- Feedback, check and conflict runs are still counted with the picks.
- Picks queued before this deploy have no flag, so they count as a person's assignment until they run.
- The floor is a constant, not a config setting.
To try it: push an assignment as a person while the home page says "slowing to 1 slot". It starts. A crew pick queues with the pace sentence in its run log.
The pace now limits only the crew's own picks. Reviews and issues a person assigned run apart from the picks, up to a floor of 2.
I haven't seen the forge's check re-run on this push. The last failure was the gate rule that wants a "Try:" line in a commit message. I pushed an empty commit carrying that line. I didn't rerun the tests after it, since no code changed. Earlier, tsc --noEmit was clean and test/pace-floor.test.ts, test/week-hold.test.ts, test/week.test.ts and test/build-once.test.ts passed. scripts/gate.ts wasn't in my clone, so I couldn't run it myself.
What changed
- Dispatch (
src/crew/dispatch.ts):weekHoldalready had a floor lane (WEEK.floorSlots, 2) forassignedandreviewruns. It counted the crew's own picks as person-assigned too, because the tick assigns under the operator's name. The tick's assignment now carriespick: trueon theissue.assignedevent (new optional argument onassignIssue).isPriorityRunreads that flag, so a pick waits for the pace and a person's assignment or a review uses the floor. - At the wall: with no slots at all, everything still waits for the reset.
- Docs:
docs/CREW.mdhas a section "What the pace limits". - Test:
test/pace-floor.test.tscovers the slowest pace. A person-assigned issue and a review start, and a self-pick is held.
Left out
- Feedback, check and conflict runs still count with the picks.
- Picks queued before this deploys have no flag, so they count as a person's assignment until they run.
- The floor is a constant in
src/crew/week.ts, not a config setting.
Try: with the home page saying the crew is slowed, assign an issue to a builder as a person on the preview. Its run starts, while a crew pick queues with the pace sentence in its run log.
Direction fit: this is off the pipeline-first list, but it is the pacer issue (#8962) and small. Three things to fix in one round.
- The floor must be a number in config (box 1). It is the constant
WEEK.floorSlots = 2in src/crew/week.ts. Make it a config value with that default, and document its name in the docs/CREW.md section. - The test does not prove the dispatch path (box 3). It calls
isPriorityRunand the pureweekHoldseparately. If someone reverts the pump wiring (theisPriorityRun(app, fresh) ? fresh.reason : "pick"call), it still passes. Drivepumpwith the pacer at its slowest: a review and a person-assigned run start, a pick stays queued with the pace sentence. Also cover the wall (slots 0 holds all). - Picks queued before this deploys carry no flag and count as a person's assignment, so they use the floor lane. Either treat a missing flag on an operator-assigned run as a pick, or say plainly in docs/CREW.md for how long this lasts.
Smaller: pickedByCrew runs a JSON SQL query per queued and running run on every pump pass. Compute it once per run, or only for assigned runs not yet counted. I checked the code only, not the preview.
All three of juniper's points and the smaller pickedByCrew one are done; pushed as 84930166.
What changed
- The floor is config. It's now the capacity setting
pace.floor, envFORGE_PACE_FLOOR, default 2, range 1–16, read on every pump throughpaceFloor(db).
- WEEK.floorSlots is gone. The pace decision takes floorSlots as an input and weekHold takes a floor argument.
- The name is documented in the docs/CREW.md section.
- weekHold still defaults its floor argument to 2, a second copy of the default. The pump always passes the real value.
- The test goes through
pump.test/pace-floor.test.tsboots the stand-in crew, with two builds filling the slots at the slowest pace.
- A review and a person-assigned run start. - A crew pick stays queued with "Ahead of pace". - At the wall (slots 0), a review and a person-assigned run both stay queued. - Reverting the pump wiring should make these fail. I didn't run a revert to confirm it.
- Cutover for old picks. An assignment now carries
person: true, orpick: truefor the tick's own picks. A run whose assignment event has neither mark is held as a pick.
- Runs queued before this deploys wait under the pace like picks until they start, and then the cutover is over. - A test removes the mark and checks the run is held. docs/CREW.md says this.
- One query per run per pass. The
json_extractquery now runs only forassignedruns and once per run per pump pass (a map local to the pass). Reviews skip it.
Other changes
- The existing
test/week-pump.test.tslane test now writes the person mark on itsassignedruns. Without it they would count as picks, so the test was wrong, not the code. test/week.test.tsdropsfloorSlotsfrom theWEEKsettings check and passes it as an input.
Checks run
tscand the gate are clean, and design-lint passes.- I ran the pace, week, capacity and settings tests, and they pass. I did not run the full suite; the forge's check does that.
scripts/fast.tssaid the diff was too broad to name tests, so I picked the files myself.
Left out
- Feedback, check and conflict runs still count with the picks.
The check failed on one terminal test that doesn't touch the pace-floor code, and I fixed the test rather than the pace code. I could not reproduce the failure here, so the fix is untested against the original failure. I've pushed it.
What changed: the test "a bad endpoint is refused in the page's words" in test/tui-backups.test.ts read the screen straight after Enter. On a loaded machine the refusal can land a moment after the terminal first reports settled. The test now waits, for up to about 2 seconds, until "The endpoint must be…" is on screen and then makes its assertions. No product code changed, and no assertions were weakened.
How I checked: I could not reproduce the failure. test/tui-backups.test.ts passed on its own (7 tests), and three runs together with test/waves-line-date.test.ts passed too. bun x tsc --noEmit is clean. I did not run the full suite, which the forge's check does on this push.
What I left alone: the shared settleApp helper in test/helpers/tui.ts. Other tests may have the same timing weakness, but changing the helper would touch every terminal test.
I pushed a second change to the flaky terminal test, but I still can't say what makes it fail in the check, so the check may fail again.
I could not reproduce the failure here. The test passed three times in a row with eight CPU-burning processes running alongside it, and it passed again after the change. Nothing in the pace-floor diff touches that screen. The log you sent doesn't say which assertion failed, so the fix is a guess at timing.
In test/tui-backups.test.ts I added an until helper that keeps settling the terminal, up to about 2 seconds, until a given text shows. The "bad endpoint" test now uses it twice:
- Before the
a refusal is said whole behindassertion, because the prompt afterdmay not have drawn yet. - Before the
The endpoint must be…assertion. My earlier fix only re-read the screen without forcing a repaint, so it could not help; this one settles again each round.
No product code changed, and bun x tsc --noEmit is clean.
If the check fails again on this test, the new log should name the failing assertion and the screen text it saw. That would show whether this is timing or something specific to the check's environment.
I merged main into the branch and pushed it. The pull should merge cleanly again.
Conflict: the only one was in test/tui-backups.test.ts, in the test for the bad-endpoint message. My side waited for the refusal to appear with until(h, ...). main had fixed the same flake differently: it reads the screen returned by h.press("enter"), and it expects only the start of the line while the prompt is open. I kept main's version and dropped mine. The assertions after it are unchanged.
Checks before the push:
- Tests: the four test files this pull changes pass (27 tests, 0 failures):
pace-floor,week-pump,week,tui-backups. - Type check:
bun x tsc --noEmitis clean. - Gate:
bun scripts/gate.tsran, but the run was cut off by the 120-second timeout, so I didn't see its result. - Skipped:
scripts/fast.tssaid the merged diff is too broad to name tests (1639 of 1997 files). I ran only the four files above rather than the full suite, so the forge's own check is the first run of everything else.
Every earlier ask is met: the floor is the capacity setting pace.floor (env FORGE_PACE_FLOOR, default 2, range 1-16) and is read on every pump. The test goes through the pump, and covers the slowest pace and the wall. Runs queued before the marks existed are held as picks, with a test and a docs note. The assignment query runs once per assigned run per pass. The tui-backups test change is a timing fix only. I read the diff and the call sites; I did not curl the preview.
Next, both non-blocking:
- Only the builder pick at autonomy.ts:549 passes pick=true. The other crew-initiated assignments (planner at autonomy.ts:855, proposals.ts:120, first-boot) are marked person. If any of them queues an assigned run on a paced repository, it takes the floor lane. Decide that on purpose, and say so in docs/CREW.md.
- weekHold keeps a second copy of the default (floor = 2). Make the parameter required, so the pump is the only source of the value.
The pace now limits only the crew's own picks; reviews and issues a person assigned run apart from them, up to a floor of 2.
I found most of this already in
weekHold(#8912). It treated everyassignedrun as a person's, so the crew's own picks, which the tick assigns under the operator's name, also used the floor lane. I fixed that.gate.tsdoesn't exist in this clone. I rantsc --noEmit(clean) andtest/pace-floor.test.ts,test/week-hold.test.ts,test/week.test.tsandtest/build-once.test.ts(all pass). I did not run the full suite ordesign-lint(nosrc/webchanges).pick: trueon theissue.assignedevent (assignIssuehas a new optional argument).isPriorityRuninsrc/crew/dispatch.tsreads that flag. A pick queues as a pick and waits for the pace. A person's assignment or a review uses the floor,WEEK.floorSlots= 2 insrc/crew/week.ts, counted apart from picks.docs/CREW.mdhas a section "What the pace limits" saying the above.test/pace-floor.test.tsqueues a person-assigned issue and a pick on a stand-in crew. At the slowest pace (1 slot, 1 pick already going), the person's issue and a review start and the pick is held.Left out
To try it: push an assignment as a person while the home page says "slowing to 1 slot". It starts. A crew pick queues with the pace sentence in its run log.
Closes #8962