33 KiB
Row 126 (#1620): build
Status: review round 2: round 4 rebased onto 1df04f52f after row 167
landed (5fa9cc8f2). Candidate manifest f332db76…6fa6, patch c0a7f5d1…fe4a,
public/ +/- digest unchanged at 03e6d9da…ba78. Slot run (round 2) green end
to end, 01:12:31Z to 01:23:26Z: 19 touched, 162 singles, land-work and 30 of
30 mutants killed. Review round 1 was approved by Filbert (29068, rev 1921) on
d18314f7…8e65. In review, round 2, at 2026-10-19T01:25:08Z: request comment 29078 (rev 1925), delta note 29079, reviewer Filbert. Filbert approved round 2 (29080, rev 1927) on f332db76…6fa6: no findings, static delta check. Next: Sage's full gate at origin HEAD, then landing (sage-0988). See "Review round 2" below.
Review round 2: rebase onto 1df04f52f after row 167 (sage-0982, sage-0983)
Filbert approved review round 1 on d18314f7…8e65 (29068, rev 1921). Their
notes 1-4 don't block. Sage's rulings (sage-0983): (a) Sage amends the
brief's scope line to name 3i and the account guard; (b) the land-work
bus-schema gap already exists (the live copy 52f35e7a0 is at 3f, refactor at 3h
since 70504d23a), it is not a blocker, and Sage reports it to Mos. Note 2,
a correction to my addendum 29064: daily-quota-runtime.test.mjs:41 and :53 use
limit 0, so they carry the zone through the loader without exercising the
boundary. The boundary evidence is daily-quota.test.mjs:64 and the DST tests
at :75-:104. Note 3: commit-msg.txt is in the packet as a draft with no
attribution line; Sage writes the landing message.
Row 167 landed as 5fa9cc8f2; origin/refactor is 1df04f52f.
- Tree wt7 =
1df04f52f+ build.patch (round 4) applied withgit apply --3way --index. All 39 paths applied, with no conflicts. Patch sha256c0a7f5d18c571ec43bd2523f694d5a8a4411f40d3fb9ae4bfdb683e563a7fe4a, manifestf332db76476a7fe47774a303058ab467c8e00e0bb9f5ed6421c76dc5ad2e6fa6. Same 39 paths as round 4. - Two paths changed bytes against round 4's manifest. Both are paths 167
also changed:
packages/webui/src/public/bus.js944a8083…475e→899adb06…628f(167's base file isc2a0dffc…54e7);packages/webui/tests/limits-browser.test.mjs6ed64b76…2c37→66841ce1…8f54. 167 changed the save's expected revision to 'b' at :54 and added its own assistant test; the 126 block follows it unchanged.
- public/:
git diff --cached 1df04f52f -- packages/webui/src/public, the +/- lines without headers, sha256 =03e6d9daed6b74878f80f8b05341b89b7defc6611d260542fbf452692653ba78. These +/- lines are byte-identical to round 4's. Only theindexand@@lines moved. The diff file is nowce1028e5…5a19. mos-0833 holds, and nothing goes to Mos (sage-0983). - Every webui test file (38) and every test that reads public/bus.js (browser, bus, editor and shell) is already in the round 4 singles list. No test pins bus.js's sha256.
- The mutant anchor check on wt7 gives 30 mutants and 0 problems.
Slot run, review round 2 (2026-10-19T01:12:31Z to 01:23:26Z), green
Sage gave the slot in sage-0983. ~/dewey-scratch/r126/run-r5.sh is run-r4.sh
pointed at wt7, 1df04f52f and manifest f332db76. It ran the same file lists.
- busy check CLEAR at 01:12:31Z; wt7 matched the manifest at start and end.
- Touched (19 files): tests 158, pass 158, fail 0. limits-browser is 2 of 2; 167 added one test, which is why the total is one more than run 3's 157.
- Singles (162 files, 0 red): tests 1389, pass 1388, fail 0, skipped 1. The skip is editor-provenance, which needs esbuild 0.28.2 (0.28.1 is installed), the same as in run 3.
- test-land-work.sh: 86 passed, 0 failed.
- Mutants: controls ok, 30 killed, 0 survived, 0 timeout,
mutants.shrc 0. D22 (webui line removed) is killed, so mos-0833 condition 1 holds on the new base. - Sweep: no processes of mine left.
Round 4: bus schema 3h → 3i (sage-0970, sage-0972, mos-0835)
Sage chose option (a), the 3h→3i rebuild (sage-0970), and Mos gave the go with
six conditions (mos-0835, relayed in sage-0972). Scope: refactor only, not in
the 8e7ea89a1 window. 3i goes live later through stack-release.sh activate … --migrate with Jason present. MIGRATION.md has the step, the run and the way
back for Mos.
Tree wt6 = 81f26551b + build.patch (round 4), all staged. 39 paths. Patch
sha256 5e7ab310…e9be, manifest d18314f7…8e65.
What changed since round 3
-
Cause A.
packages/bus/src/migrations/3h-3i.sql(new):- drops the three usage_records triggers;
- renames the table to usage_records_3h;
- creates usage_records with schema.sql's text, with
account TEXT,; - copies every row with its seq;
- carries the table's sqlite_sequence value;
- drops the old table and recreates the three triggers byte-identical.
It renames first because
ALTER TABLE … RENAMEstores the new name quoted, and the digest compares that text. -
schema.sql.
account TEXT CHECK (account IS NULL),becomesaccount TEXT,. Nothing else changes. New sha256 is6cde5232…8737. -
store.mjs.
SCHEMA_SHA256is6cde5232…8737andSCHEMA_VERSIONis'3i'. The new step is{ from: '3h', to: '3i' }.stack-release.sh'splan_stepsreads itsfrom:/to:lines as written. -
New 3h pin.
packages/bus/tests/fixtures/schema-3h.sqlis 81f26551b's schema.sql, sha256bb2ba581…6950. -
Migration tests moved to 3i.
- thread-migration, chief-of-staff, comment-migration, spec-migration, name-migration and usage-migration now expect 3i.
- usage-migration now migrates 3g through 3h to 3i.
- comment-migration's "future version" fixture moves to 3i→3j.
-
New test file.
packages/bus/tests/account-migration.test.mjs, three tests (below). -
Cause B. The limits-browser 126 block navigates to
/?daily=1#/settings(:58-61). -
Account guard (condition 3). launcher.mjs refuses
account-is-credentialwhenlaunch.account.nameequals the value of any SESSION_CREDENTIALS variable in the bus host's environment. The message names the variables, never the value.- It has a launch-account test and a cli README refusal-table row.
- launcher.test.mjs's account name changes from "stand-in" to "proxy-login" (:714, :750, :770, :772). Its token value is "stand-in", so the old name would now refuse.
-
bus README.
- The schema paragraph was stale since 3g. It now reads v3i,
6cde5232…, with 16 tables, 71 triggers and five views (counted with grep on schema.sql). - A 3h→3i paragraph is added under "Token accounting per run".
- The schema paragraph was stale since 3g. It now reads v3i,
-
DESIGN.md. The "no schema change" line is corrected (condition 5).
-
Round 3 defect, found in slot run 2. Round 3's broker and usage changes put
accountin the limit-check subject, as the plugin contract (packages/plugins/README.md:150) says:{instance, provider, model, account}. Three tests still pinned the three-key subject and now expectaccount: null: bus land.test.mjs:538, bus limit-stop.test.mjs:61 and harness runner.test.mjs:634. Round 3 never ran singles, so it missed them.
Mos's conditions (sage-0972), each with its check
| # | Condition | Where it is checked |
|---|---|---|
| 1 | One transaction; a failure partway leaves 3h with every row | account-migration test 2. A copy of the engine runs the real step with one extra statement before DROP TABLE usage_records_3h: INSERT INTO usage_records SELECT * FROM usage_records_3h LIMIT 1. It passes the empty replay at import and fails at run time with UNIQUE constraint failed: usage_records.seq, after the triggers are dropped and the table renamed, rebuilt and filled. Afterwards the version is 3h, and sqlite_master and every table's rows equal the before-snapshot. The real step then migrates the same store. Mutant D29 runs the step outside this.transaction and must be killed. Code: store.mjs #migrateSchema wraps step SQL, meta rewrite and digest check in one BEGIN IMMEDIATE (transaction(), ROLLBACK on throw). |
| 2 | sqlite_master for usage_records and its three triggers equals 3h's, apart from the account CHECK | account-migration test 1 compares all of sqlite_master (type, name, tbl_name, sql), not just these four rows. It equals 3h with one substitution in usage_records: account TEXT CHECK (account IS NULL),\n → account TEXT,\n. It asserts that only usage_records carried the old line and that 3h had exactly 3 triggers on the table. The store's open-time digest check (schema-mismatch) and the import-time migration-target-mismatch check give the same result in code. Mutant D28 drops the replace trigger. |
| 3 | account holds an account identifier, never a credential | Three code checks, each with a test (detail below). |
| 4 | MIGRATION.md gives the rollback: 3h refuses 3i, the way back is the pre-migrate backup | MIGRATION.md. Test 3: a 3h engine (3h schema and SHA, step removed) refuses a 3i store with schema-mismatch, with or without migrateSchema, and leaves it at 3i, unchanged. The activate path does take the backup (stack-release.sh:663-669, backup_store before migrate-schema.mjs). |
| 5 | Correct DESIGN.md | DESIGN.md "Item 1", last bullet. |
| 6 | public/ digest stays 03e6d9da…ba78 |
Recomputed on the round 4 patch: git diff --cached 81f26551b -- packages/webui/src/public, +/- lines without headers, sha256 = 03e6d9daed6b74878f80f8b05341b89b7defc6611d260542fbf452692653ba78. Round 4 doesn't touch bus.js. |
Condition 3 in detail. What shape the account accepts:
-
Business config (
packages/business/src/business.mjscheckAccount,requireId).launch.account.nameand everytokens.dailykey must match^[a-z0-9][a-z0-9._-]{0,63}$: lowercase, digits,.,_,-, at most 64 characters. Test: token-limit-settings.test.mjs :108-131. -
Broker (
broker.mjsbindLaunch,identifier()). On the bind, the account must be text of at most 160 characters matching^[a-zA-Z0-9][a-zA-Z0-9_.:/-]*$. This is a weaker bound than (1); it is the second line, and the launcher sends only what (1) allowed. Test: daily-quota-runtime.test.mjs :75-85. Mutant: D25. -
Launcher, new in round 4 (
packages/cli/src/launcher.mjs, insideif (account !== null)). It refusesaccount-is-credentialwhen the account name equals the value of any SESSION_CREDENTIALS variable set in the bus host's environment. A lowercase API key or token could pass the shape checks, so shape alone can't rule a credential out. This equality check closes that case for every credential the launcher could hand the session. Test: launch-account.test.mjs :83 "REQ-LIMIT-2 account-is-credential: launch.account.name is the value of a credential variable the bus host sets". It covers one and two matching variables, checks that the why names each variable, that the value never appears in the log, and that mayLaunch is never called. Mutant: D30.Limit: it compares against the credentials in the launching host's environment, not every secret anywhere.
Test file account-migration.test.mjs
A 3h store is built from the pinned fixture with:
- meta;
- one event;
- one instance_names row;
- three usage rows: begin known, end known 4/5/6/7, end unknown
no-source; - sqlite_sequence set to 50, past the last seq, so the step must carry it rather than recompute it.
No live store is read.
- Migrates; rows, seq and sequence kept; only the account CHECK changes.
- The plain open refuses with
schema-mismatch; migrate reaches 3i. - Every table's rows are unchanged.
- sqlite_master equals 3h with the one substitution.
- quick_check and foreign_key_check are clean.
- The next insert gets seq 51 with account
jason-max. - UPDATE, DELETE, INSERT OR REPLACE and a reused seq all abort with "append-only".
- The plain open refuses with
- A failing step leaves the store at 3h (condition 1).
- A 3h engine refuses a 3i store, migrating or not (condition 4).
Mutants added (30 in all)
- D27-no-sequence-carry. Removes the sqlite_sequence carry, so the next seq would be 4.
- D28-no-replace-trigger. Drops the third trigger's recreation.
- D29-migrate-outside-transaction.
- D30-no-account-is-credential.
Each is checked against account-migration or launch-account.
Run kit
~/dewey-scratch/r126/run-r4.sh runs these in order:
- busy check;
- HEAD and manifest checks;
- the 19 changed or new test files, singly;
- the 162 other test files that read a round 4 path, singly. These are found transitively through source imports, unioned with the round 3 list;
scripts/test-land-work.sh, which reads store.mjs through land-work/read.mjs;- the 30 mutants;
- a manifest check at the end.
Notes for Sage
- land-work between landing and the window. read.mjs takes
SCHEMA_VERSIONfrom the checkout's store.mjs. Once 3i is on refactor, land-work.sh from the canonical checkout refusesbus-schemaagainst the live 3h store until the window runs. Details are in MIGRATION.md. - Row 167. It shares bus.js and limits-browser.test.mjs (Darkwing's
handoff). If 167 lands first, I rebase with
--3way --index, recheck the digest and rerun.
Slot run 1, first attempt of round 4 (2026-10-19T00:34:33Z), red at the touched step
- Manifest
a0df19c1…. 15 of 16 touched files green: limits-browser 1/1, launcher 24/24, daily-quota-runtime 4/4. - account-migration 2/3. Test 1 failed on its own assertion, not the step:
sqlite_sequence.find(...)returned a null-prototype row, anddeepStrictEqualagainst a plain object refuses on prototype. The values were equal (seq: 50). - Fix: spread the row,
{ ...row }, as line 96 already does. The patch and manifest were rebuilt (3eb0b7b9…,5dfe5c90…). Awk sum: tests 103, pass 102, fail 1. - Rerun as slot run 2.
Slot run 2 (2026-10-19T00:35:29Z to 00:44:22Z), red at singles
- Manifest
5dfe5c90…. Touched step green. Awk sum: tests 103, pass 103, fail 0. This includes limits-browser 1/1 (cause B), launcher 24/24 and account-migration 3/3. - Singles: 165 files, 3 red. Awk sum: tests 1439, pass 1435, fail 3,
skipped 1.
- land.test.mjs 16/17;
- limit-stop.test.mjs 4/5;
- runner.test.mjs 31/32.
- Each failure is the subject deepEqual,
+ account: null. It is the round 3 defect above, not the 3i step. The code matches the plugin contract, so the three tests changed, not the code. - The run stopped before land-work and the mutants. The end manifest check passed.
- Fix: those three files now expect
account: nulland move to the touched list (19 touched, 162 singles). The patch and manifest were rebuilt (5e7ab310…,d18314f7…). The public digest is unchanged at03e6d9da…ba78. All 30 mutant anchors still match. - Rerun as slot run 3.
Slot run 3 (2026-10-19T00:50Z to 00:59:04Z), green
- Manifest
d18314f7…8e65, tree wt6 at81f26551b. Packet build.patch is5e7ab310…e9be, build-manifest.sha256 isd18314f7…8e65, public.diff is1964ec2c…a56c. public.diff is byte-identical to round 3's, and its digest is03e6d9da…ba78(condition 6). - Touched. 19 files, singly. Awk sum: tests 157, pass 157, fail 0. This includes limits-browser 1/1, account-migration 3/3, launch-account, land.test 17/17, limit-stop 5/5 and runner 32/32.
- Singles. 162 files, 0 red. Awk sum: tests 1385, pass 1384, fail 0, skipped 1. The skip is editor-provenance's rebuild check. It needs esbuild 0.28.2 and 0.28.1 is installed. 126 doesn't touch the editor.
- land-work.
scripts/test-land-work.shrc 0, 86 selftests passed, 0 failed. - Mutants. Every control passed, then 30 killed, 0 survived, 0 timeouts,
mutants.shrc 0. D22 (webui line removed) was killed, as Mos's decision 123 go asks. D27-D30 (round 4) were killed. - The manifest check at the end passed. No processes of mine were left.
- Two false starts. Both times singles.sh's busy check aborted the run
before singles. The cause was my own monitor's command line, not another
seat's run.
- The first monitor's grep was started from the canonical checkout.
- The second's pattern held the literal text
mutants.sh, which busy.sh's*mutants.sh*glob matches. - The third start used
mutants\.sh. busy.sh is unchanged.
Slot run 1 on 81f26551b (2026-10-19T00:15:07Z), red
Tree wt6 = 81f26551b + build.patch 5e0efbbf, manifest f82bfb7c (checked at
start). The run stopped after the touched step. Awk sum over the 9 touched
files: tests 74, pass 71, fail 3, cancelled 0, skipped 0. No singles or
mutants ran. This was 126's first run on any base; the e8b0 kit never ran.
| File | Result | Cause |
|---|---|---|
| bus/tests/daily-quota-runtime.test.mjs | 3/4 | A |
| cli/tests/launcher.test.mjs | 23/24 | A |
| webui/tests/limits-browser.test.mjs | 0/1 | B |
The other six were green: daily-quota 13, token-limit 11, token-limit-settings 6, launch-account 6, launch-limit-checks 4, limits-tokens 5.
A: usage_records.account is CHECK (account IS NULL)
(bus/src/migrations/3g-3h.sql:10, row 124). DESIGN.md says "The column
already exists, so there is no schema change and no migration". That is
wrong; I didn't read 3g-3h.sql.
- The bound run's
usage.begininserts'jason-max', and SQLite refuses it. - That error isn't a BusError, so
#refusedreturnsstorage-refused(daily-quota-runtime.test.mjs:58, via turn() :37). - The pi test inserts NULL and passes.
- launcher.test.mjs:768 is 126's own
untilfor"stand-in"inusage.byAccount. The probes, the reply and the launch-record assertion before it passed. The coder's records hit the same CHECK, so the wait timed out. It fails on a 126-only line, so a run on81f26551bwithout 126 can't show anything. - The fix needs a durable account on the records: a 3h→3i step that
rebuilds
usage_recordswithout the NULL check. Migration is offline and only in Mos's re-pin window (bus README :822-823, migrate-schema.mjs:2). This is a schema change the approved design doesn't cover, so it waits on Sage's go.
B: a same-URL navigation in 126's test block. 126's block
(limits-browser.test.mjs:58-63) navigates to /#/settings and passes,
including the exact daily text. Row 125's next block, "a limits answer the
server refuses", navigates to the same /#/settings. Page.navigate to an
identical URL with a fragment is a same-document navigation, so the page
doesn't reload or refetch limits. The wait for "Token limits unavailable"
(:67) times out. Before 126, the previous URL was /?send=1#/settings, so it
reloaded.
- Fix: 126's block navigates to
/?daily=1#/settings. The fetch stub reads onlysend. - Test only. bus.js and public.diff don't change, and nothing 165 added is involved.
- Applied in wt6 (not run, not yet in build.patch). It goes into the round 4 patch with A's fix.
- Row 167 changes :54 (
'a'→'b'revision) in this hunk's context, and bus.js at -606, next to 126's -600..606 hunk. If 167 lands first, 126 rebases with a 3-way and the 03e6d9da digest is rechecked.
Reported to Sage as dewey-0660 (2026-10-19T00:20Z), recommending (a) the 3h→3i migration over (b) a per-run account event. Waiting on that ruling.
Rebase onto 81f26551b (sage-0962, sage-0965)
165 landed as 9466f3e5e. Base 81f26551b is cff5f31ed (165's SESSIONS line)
plus one commit that touches only the queue. The landed tree matches 165's r3
manifest e30b54da.
-
Rebase. 126's e8b0 patch (62b44c60) 3-way applied to a detached worktree at
cff5f31ed, with 0 unmerged entries. I took the patch from that index and applied it to a second worktree at81f26551b, where it rebuilt byte-for-byte. -
New artifacts. Each old file is kept with an
-e8b0suffix.File sha256 build.patch(git diff --cached --binary 81f26551b)5e0efbbff993fd2411c7608850dfc7e6925327ef622eebf88ee3f8c00c30b33cbuild-manifest.sha256(25 files)f82bfb7cadab33b95b8e76686c1b098e61edac452075165e0ea2eedadc7f8e8dpublic.diff1964ec2c1d8a5ec1c643ed212f46a17a4aa9cbfafef16340ab9eccb922bca56c -
Public digest. The public.diff file sha moved only because its
indexand@@lines moved. Its +/- digest is still03e6d9da…ba78, so mos-0833 still covers it and nothing goes back to Mos. -
The six shared paths (business, bus and cli READMEs, broker.mjs, runtime.mjs, public/bus.js) differ in bytes only by 165's lines. The +/- digests of all 25 paths are equal to the e8b0 build's. The 25 rebased files are byte-equal to my early trial tree (
e8b09735a+ 165 r3 + 126). -
Hash pins. No 64-hex pin in any test names a 126 path.
-
Singles list. It grew from 109 to 143, all additions. One is 165's
webui/tests/instances-browser, added under the every-webui-test rule. A resolver scan maps each test's relative imports and path strings to repo paths, and follows the test helpers that import 126 modules:cli/tests/helpers.mjs,cli/tests/fixtures/launch-host.mjs,cli/tests/channel-fixture.mjs,bus/tests/spec-fixtures.mjsandtasks/tests/world.mjs. That scan added:- 165's new tests:
bus/tests/instances-verb,cli/tests/instancesandwebui/tests/instances. bus/tests/channel-proof.- 29 tests that reach broker.mjs, runtime.mjs or launcher.mjs only through those helpers.
The e8b0 list was built from direct imports and missed those helper paths. Nothing was dropped.
- 165's new tests:
-
singles.sh now counts nonzero exits and exits 1 on any. Before, a file that died before its summary added nothing to the awk sum and passed.
mutants.mjs checkpasses on the 81f2 tree: 26 mutants, 0 problems.
Rebase onto e8b09735a (sage-0961), superseded by the 81f26551b rebase above
- The round 1 patch (vs
70c9131f0) 3-way applied to a detached worktree ate8b09735a. Every one of the 25 files applied, with no conflict and no unmerged entry. build-manifest.sha256is now082af6eed02cce737e7a68ad90fca2675a18086de6d435ec7464a48c933e9655(25 files).build.patch(git diff --cached --binary e8b09735a) is62b44c60c0691de0b9b725c974606a6f395dd392c5e6ea632d3f3ad38bcd380f.- public/: the +/- lines of
packages/webui/src/public/bus.json the new base are the same aspublic.diff, with digest still03e6d9da…ba78. 156 doesn't touch public/, so per mos-0833 nothing goes back to Mos. - The account still reaches the bus on 156's new paths.
landPassrelaunches throughstart({instance, launchedBy, ...(await check(...))}), andstartre-runscheckitself (launcher.mjs:307), so every launch path carriesaccount. The recovery rebind (launcher.mjs ~630) still passes no account, which is intended (see below). - Run kit (my scratch, not in the packet): busy check, then a check that the
tree is on
e8b09735aand the manifest passes, then the 9 changed or new test files singly, then 109 singles, then 26 mutants. It stops at the first red step. The singles are every test that names a 126 source module orpublic/bus.js, every webui test (they all serve bus.js), and the six path-reading tests below.mutants.mjs checkpasses read-only on the rebased tree: 26 mutants, 0 problems.
Design and Sage's rulings: DESIGN.md (sage-0942, sage-0944).
What it does
launch.account={name, credential}in the business file.nameis an identifier.credentialnames the environment variables of one login set, joined with+, never a value (business/src/launch.mjscheckAccount).launch.limits.tokens.daily.<account>={limit, since, reset, timeZone}. A daily key that isn'tlaunch.account.namerefuses the business read, since it would count nothing and still show as counted.- New plug-in
limit-check/daily-quota(interface 2, pure). A day starts at the earliest instant at or afterreseton that local date. It counts all four kinds as BigInt, and unknown turns apart. - The launcher (claude-code only, Q2): with
launch.accountset, it refusesaccount-credential-missingwhen the named set isn't a login set or the bus host lacks part of it, andaccount-credential-ambiguouswhen the bus host also sets any other credential variable,ANTHROPIC_BASE_URLincluded (Q1). Both run afterno-session-credential. Messages name variables, never values. - A pi launch has account null: no account checks, records NULL, no daily quota covers it (README notes in cli, plugins, daily-quota).
- The account rides
mayLaunch,host.bindLaunch→broker.bindLaunch(identifier or null, kept on the session, not insession.launched's body), usage records (accountcolumn,NULL→ bound value),mustStopand thelaunchrecord. - Recovery (launcher.mjs, the rebind after a host restart) rebinds a run only to end it, without provider, model or account. That's unchanged; it needs no account.
- The fixed line, "Daily quotas count only this stack's own use of the
account, not its use anywhere else.", appears once under the list in
mosaic limits(cliDAILY_NOTE) and under the table in Settings, only when a daily row shows.
public/ (needs Mos's decision 123 go)
packages/webui/src/public/bus.js changes in tokenLimits only: one <p id="token-limits-daily"> after the table when a row's path is
tokens.daily.*, and one comment sentence. The diff is public.diff in
this packet. I won't move to in-review before Mos's go on it.
Go requested from Mos 2026-10-18T23:2xZ (dewey-0650, per sage-0951), naming lines 591-592 (comment) and 604 (the paragraph). If a rebase onto row 165 moves this delta, the new diff and digest go back to Mos first.
Mos's go, mos-0833, is conditional on public.diff sha256 ee024d64…c2a557 with +/- digest 03e6d9da…ba78. It holds only if all three of these hold:
- A green slot run that includes limits-browser.test.mjs (count 0, and count 1 with the exact text), with mutant D22 killed.
- Reviewer approvals.
- The +/- digest is still 03e6d9da…ba78 at landing. If a rebase onto 156 or 165 moves the delta, or any later public/ change is made, the new diff, file sha and digest go to Mos before in-review.
Files
New:
- packages/plugins/limit-check/daily-quota/index.mjs
- packages/plugins/limit-check/daily-quota/README.md
- packages/plugins/tests/daily-quota.test.mjs
- packages/bus/tests/daily-quota-runtime.test.mjs
- packages/cli/tests/launch-account.test.mjs
Changed source: business/src/{launch,business,launch-settings}.mjs, bus/src/{limits,usage,broker,runtime}.mjs, cli/src/{launcher,limits}.mjs, webui/src/public/bus.js.
Changed tests: business/tests/token-limit-settings.test.mjs,
plugins/tests/token-limit.test.mjs, cli/tests/launch-limit-checks.test.mjs
(the op now carries account: null), cli/tests/launcher.test.mjs,
cli/tests/limits-tokens.test.mjs, webui/tests/limits-browser.test.mjs.
Docs: bus, business, cli and plugins README.md.
Paths shared with other rows
| Path | Row 165 (Darkwing) | Row 156 (mine) |
|---|---|---|
| packages/bus/src/broker.mjs | yes | yes |
| packages/bus/src/runtime.mjs | yes | yes |
| packages/cli/README.md | yes | yes |
| packages/bus/README.md | yes | |
| packages/business/README.md | yes | |
| packages/webui/src/public/bus.js | yes | |
| packages/cli/src/launcher.mjs | yes | |
| packages/cli/tests/launcher.test.mjs | yes |
From candidate-manifest.sha256 (165) and build-manifest-r2.sha256 (156). I rebase onto whichever lands, then rerun the whole gate on the new base.
Sage's order (sage-0962): 165 lands first, so 126 does not run on
e8b09735a. 165 r3 goes through Darkwing's slot run, its in-review (my review
included) and Sage's gate. Then it lands, I rebase 126 onto it and check the
bus.js digest, and only then does 126 get its slot run, with the plan
unchanged.
Early conflict trial, no tests (2026-10-18T23:5xZ): on a scratch worktree
at e8b09735a, I applied 165's round 2 build.patch (cc886c47) and then
126's build.patch (62b44c60), both with --3way --index. Both applied with
no unmerged entries. The 126-only bus.js delta over e8b09735a + 165 r2 still
digests to 03e6d9da…ba78. This is provisional: I repeat it on round 3
(cefce7a4) once that patch is in agents/darkwing/work/queue-165/, and the
digest that counts is the one taken on 165's landing.
Round 3 trial, no tests (2026-10-19T00:00Z): on a new scratch worktree at
e8b09735a, 165's round 3 build.patch (cefce7a4) applied with --3way --index
and its candidate manifest (e30b54da) matched. 126's build.patch (62b44c60)
then applied with --3way --index, with 0 unmerged entries. 165's bus.js is
byte-equal between rounds 2 and 3. The 126-only bus.js delta over e8b09735a +
165 r3 is still 03e6d9da…ba78. Apart from index and hunk-offset lines, the r3
patch body is line-for-line the same as r2. The landing digest still decides.
156 landed at 4f45cadc0 and 126 is now on e8b09735a, so the 156 column is
history. 165 hasn't landed yet. If its round 3 lands before 126, I rebase
again on broker.mjs, runtime.mjs, the bus, business and cli READMEs and
public/bus.js, and rerun the whole gate. If that rebase moves the public/
delta, it goes to Sage for Mos before in-review.
Tests (to run singly once rebased and slotted)
- plugins/tests/daily-quota.test.mjs (13 tests): count, account,
since, unknown turns, the Chicago 05:00 boundary, 23 h and 25 h DST days, the skipped and the repeated reset, refusal at the quota, stop, next-day allow, BigInt, bad leaves. - plugins/tests/token-limit.test.mjs: token-limit ignores
tokens.daily. - business/tests/token-limit-settings.test.mjs: the schema (account, reset, zone, the daily key names the account) and the writer stamps and resets a daily leaf.
- bus/tests/daily-quota-runtime.test.mjs: through the real loader,
limitscounts the daily row;mayLaunchrefuses only the account at the quota; a bound claude-code run writes its account and stops at the quota; a pi run writes null;bindLaunchrefuses a bad account and keeps it offsession.launched. - cli/tests/launch-account.test.mjs:
mayLaunchgets the account; each of the three sets is accepted held exactly; four missing cases; three ambiguous cases with the exact text; no value in the log; pi null; no account means no ambiguity check. - cli/tests/launcher.test.mjs (sandbox): the pi chief's
launchrecord hasaccount: null; in the real Claude Code test, the business names accountstand-inwithANTHROPIC_AUTH_TOKEN+ANTHROPIC_BASE_URL, the coder'slaunchrecord saysstand-in, andusage.byAccountshows[null, "stand-in"]. Unverified: that the cto test client may readusage. The first run settles it. - cli/tests/limits-tokens.test.mjs: the line once after the list with a daily row, absent without.
- webui/tests/limits-browser.test.mjs:
#token-limits-dailyabsent with no daily row, present once with the exact text with one.
Tests that read a 126 file by path, to run singly too (grep 2026-10-18T23:1xZ; no test pins a 64-hex digest of a 126 file):
- bus/tests/usage-runtime.test.mjs:33 (usage.mjs source: loader only);
- bus/tests/project-runtime.test.mjs:13 (broker.mjs import list; 126 adds no import);
- bus/tests/limit-checks.test.mjs:319 (limits.mjs imports; no
token-limitorlimit-check/in runtime, broker or process); - cli/tests/launch-limit-checks.test.mjs:72 (launcher.mjs reaches no check);
- cli/tests/host.test.mjs:826 (cli README names the unit's file);
- webui/tests/shell.test.mjs:36 (bus.js shipped strings and its five writes; 126 adds no write).
The read-only greps found nothing in these that the 126 edits break. The run will confirm it.
Mutants (one per run, each against its killing test)
| # | Mutant | Killed by |
|---|---|---|
| D01 | never steps back to yesterday | daily-quota: day turns at the reset |
| D02 | window in UTC | daily-quota: day turns at the reset |
| D03 | offset taken once from now |
daily-quota: spring and fall DST |
| D04 | skipped reset +gap (03:30) | daily-quota: skipped reset |
| D05 | repeated reset takes the later 01:30 | daily-quota: repeated reset |
| D06 | ignores since |
daily-quota: count |
| D07 | ignores account | daily-quota: count |
| D08 | >= becomes > |
daily-quota: refused at the quota |
| D09 | token-limit walks daily |
token-limit: ignores tokens.daily |
| D10 | schema drops the reset check |
token-limit-settings: schema |
| D11 | schema drops the timeZone check |
token-limit-settings: schema |
| D12 | no daily-key/account cross-check | token-limit-settings: schema |
| D13 | launcher skips the missing-set refusal | launch-account: missing |
| D14 | launcher skips the ambiguity refusal | launch-account: ambiguous |
| D15 | launch record omits the account |
launcher: real Claude Code test |
| D16 | usage inserts NULL |
daily-quota-runtime: bound run |
| D17 | broker mustStop omits the account |
daily-quota-runtime: bound run |
| D18 | runtime drops the account from mayLaunch |
daily-quota-runtime: limits and mayLaunch |
| D19 | bus DEPTH lacks daily |
daily-quota-runtime: limits and mayLaunch |
| D20 | settings DEPTH lacks daily |
token-limit-settings: writer stamps |
| D21 | CLI line removed | limits-tokens |
| D22 | webui line removed | limits-browser |
| D23 | the launcher's host.bindLaunch call omits the account |
launcher: real Claude Code test (usage) |
| D24 | a pi launch takes the account | launch-account: pi null |
| D25 | broker drops the identifier check | daily-quota-runtime: bindLaunch |
| D26 | account written into session.launched |
daily-quota-runtime: bindLaunch |
D15 and D23 need the sandbox and claude on PATH; if the gate host lacks
either, those two are skipped and I'll say so.
Org-file change for Jason (not made by me)
Add "account": {"name": "<name>", "credential": "<set>"} to launch,
where <set> is the one login set the bus unit's EnvironmentFile sets. I
don't read that file. With an account set, that file must set no other
credential variable, or every claude-code launch refuses
account-credential-ambiguous.