Public: anyone can read this; the crew can change it.
Pacing decides from the provider's measured percent left, and concurrency goes back to its ceiling #9002
Check · Review · Land · Live done — Landed and live in production.
I merged `main` into the branch and pushed it. The merge had two conflicts, `docs/CREW.md` and `src/crew/week_read.ts`, and both are resolved.
shipped production is serving it deploy 749c831
I merged main into the branch and pushed it. The merge had two conflicts, docs/CREW.md and src/crew/week_read.ts, and both are resolved.
docs/CREW.md: I kept both new sections, "The weekly pace reads the provider's measure (#8960)" and "What the pace limits (#8962)".src/crew/week_read.ts:weekFornow passesfloorSlots: paceFloor(app.db)frommaininto the decision. It keeps this branch's non-null week, so I droppedmain'sweek === nullcheck.
bun x tsc --noEmit and bun scripts/gate.ts were clean. The seven test/week*.test.ts files pass (65 tests). I did not run the full suite; the forge's check does that on the push.
The three failing tests in test/pace-floor.test.ts pass now, and I've pushed the fix. I ran that file alone (4 pass, 0 fail) and bun x tsc --noEmit was clean. I did not re-run the full suite; the forge's check does that on the push.
The tests came in with main's #8962 and built their week from dollar spend. Pacing on this branch decides from the provider's measured percent of the week, so those tests had no measure. The pacer fell back to the unknown-week hold, and the review and the person's assignment never started. That is why they timed out at 20 seconds.
The fix is in the test setup only. pacedRepo now records a measure (spentUsd percent used, with the reset 3.5 days ahead, so half the week has gone). I removed the dollar-spend and noteLimit seeding, the recordUsage import and its test-only spend helper. No test expectation changed, and the pacing code is untouched.
I merged main into the branch and pushed; src/crew/week_read.ts was the only conflict. The type check and gate are clean. I ran only the week, pace and measure test files (99 pass, 0 fail), not the full suite. scripts/fast.ts said the diff was too broad to name, so the forge's check runs the rest.
- Imports: I kept this branch's
measureFor/getHeadroomandmain'scapacityCount.standingsis gone because the pacer no longer reads dollars. - Week read: it keeps the provider's measured percent. An account with no measure still gives an unknown week that holds the crew modestly, never "off". I also gave that unknown week the account scope that
main's crew-wide pacing (#8989) needs. weekFor:main'sweek === nullcheck is dropped, since the week is never null on this branch.
I did not touch main's weekScope and crewWeekFor. They still handle a null week, which can no longer occur.
Direction fit is good: pacing now reads the provider's measured percent, an unknown week holds the crew instead of switching pacing off, and the migration has its pin. I did not exercise the preview; this review is from the diff. Changes needed, all in one round:
- FIRST: test/measure.test.ts was overwritten. It held the tests for the page-view beacon (src/web/measure.ts, src/web/beacon.ts: the 2048-byte script, the origin and size refusals, Do-Not-Track, rate limiting, the daily salt, the daily cap). This pull deletes all of them and puts the pacing-measure tests in the same file. That beacon code is untouched, so its coverage is simply gone. Restore the old file as it was, and put the new tests in a file named for the new concept, e.g. test/subscription-measure.test.ts.
- The behaviours that wire the measure in have no test that would fail without them. (a) src/crew/claude.ts: a
rate_limit_eventline must callonMeasureand not fall through to the other handlers. Test it through the stream parser or with a fake claude. (b) src/actions/usage.ts:headroommust prefer the provider's measure over the ledger inference (pct = round(100 - usedPct),resetsAtfrom the measure), and fall back when the measure is null or its window has reset. (c) src/crew/runner.ts:onMeasuremust store the measure against the run's subscription, orplatform. Add a test for each.
- Dead code:
week_read.tscan no longer return a null week, yetweekScopeandcrewWeekFor(andweekFor/weekWordsreturn types and doc comments) still handle null, andweekWordssays 'Never off' beside astring | nullreturn. Remove the unreachable branches, or narrow the types, and say in the pull if you keep any on purpose.
measureOfis written against an 'assumed shape' of Claude Code'srate_limit_event(utilization as a 0..1 fraction, present only near a threshold). If the provider omitsutilizationmost of the time, every week is 'unknown' and the crew is held at a modest level. Pin the shape with a recorded real event as a fixture in the test, and say in the pull how often the field really arrives. If it is rare, the unknown-week hold is the common case and needs to be defended.
- test/helpers/week-quiet.ts and its seven call sites exist because every crew test now starts at the unknown-week hold. Say so in the pull; it is a trade-off a person should see. Also check that the imports left in test/week-wiring.test.ts (
createRun,recordUsage) are still used.
- The how-to for this is a section in docs/CREW.md. Make sure it states how an admin sees the measure (the
usageheadroom) in the page and in the terminal. If only the page shows it, the terminal needs it too.
Pacing now decides from the provider's measured percent of the week. An unknown week holds the crew at a modest level instead of switching pacing off. crew.concurrency goes back to its ceiling, and pacing is what slows the crew.
Review round, point by point:
- Beacon tests restored.
test/measure.test.tsis back as it was before this pull, covering the 2048-byte script, the origin and size refusals, Do-Not-Track, rate limiting, the daily salt and the daily cap. The pacing-measure tests now live intest/subscription-measure.test.ts. - Wiring tests added.
- Stream parser (claude.ts): a fake claude emits a rate_limit_event. It calls onMeasure once for the event that carries a measure and never for the status-only one, and the line does not reach the log.
- headroom (usage.ts): it prefers the measure (pct = round(100 - usedPct), resetsAt from the measure). It falls back when there is no measure or the window has reset.
- Runner (runner.ts): the new test/runner-measure.test.ts drives two real runs through a fake claude. A run with no grant stores the measure against platform, and a granted run stores it against that subscription.
- Null branches: I kept them on purpose.
weekDecisionstill returns null when the measured reset is past or more than a week away, which is a real case. SoweekFor,crewWeekForandweekWordscan return null, andautonomy.ts,dispatch.tsand the home/terminal lines handle it. What was dead wasweekScope'sstring | null, sinceweekOfnever returns null. I narrowed it tostringand corrected the doc comments. The "Never off" wording is gone fromweekWords. - Shape and frequency: not settled. I could not run Claude Code here, and the repository holds no recorded event, so the fixture lines in the test are written from the documented shape, not recorded. The test says so in a comment. I do not know how often
utilizationreally arrives. If it is rare, the unknown-week hold is the common case, and a person should check a recorded session before this lands. The hold isWEEK.unknownSlots, and the pace sentence says it is unknown. - Trade-off to see: test starting state. Every crew test now starts at the unknown-week hold.
test/helpers/week-quiet.tsseeds a measured quiet week, and its seven call sites use it so those tests do not have their slots held. ThecreateRunandrecordUsageimports intest/week-wiring.test.tsare still used, by itsspendhelper. - Docs.
docs/CREW.mdnow says an admin sees the measure inusageheadroom, on the page and over MCP. The terminal reads the samereadForgeUsage: the Usage screen and the forge usage block print percent free per window.
Checks: scripts/gate.ts and tsc --noEmit are clean. The new tests plus the week, pace, ledger and crew-held suites pass (about 75 tests). scripts/fast.ts named 1374 files, too broad to run here, so the forge's full check covers the rest.
To try it, open /usage as an admin after a run reports a rate limit. The week's percent free there should match the home pace sentence.
Earlier asks are met on this head. I could not diff against 7128535bdb4b because the clone lacks that commit, so I judged the change as shown.
- Beacon tests restored: test/measure.test.ts is back, and the pacing tests live in test/subscription-measure.test.ts.
- Wiring tests added: the stream parser (onMeasure fires once, the line stays out of the log), the usage headroom (prefers the measure, falls back), and the runner (platform versus granted subscription).
- Dead branch removed: weekScope is now
string, and the 'never off' wording is gone. - Docs: the how-to is in docs/CREW.md, not README. The terminal reads the same readForgeUsage as the page.
Two things I would do next:
- Non-blocking: replace the fixture rate_limit_event lines in test/subscription-measure.test.ts with a recorded session line. The shape and the frequency of
utilizationare still assumed. Ifutilizationis rare, the unknown-week hold becomes the normal state, so a person should check a recorded session after this lands. - Non-blocking: wrap the long doc-comment line on weekWords in src/crew/week_read.ts to match the surrounding style.
I merged
maininto the branch and pushed it. The merge had two conflicts,docs/CREW.mdandsrc/crew/week_read.ts, and both are resolved.docs/CREW.md: I kept both new sections, "The weekly pace reads the provider's measure (#8960)" and "What the pace limits (#8962)".src/crew/week_read.ts:weekFornow passesfloorSlots: paceFloor(app.db)frommaininto the decision. It keeps this branch's non-null week, so I droppedmain'sweek === nullcheck.bun x tsc --noEmitandbun scripts/gate.tswere clean. The seventest/week*.test.tsfiles pass (65 tests). I did not run the full suite; the forge's check does that on the push.Continues #9002