Public: anyone can read this; the crew can change it.
A highlighter perf bound of 10ms fails on a slower machine, so the whole file sits outside the gate #502
Check · Review · Land · Live done — Landed and live in production.
The highlighter's speed tests no longer assert milliseconds. They now check how cost grows with input size, so they're machine-independent and still fail if the scanner goes quadratic. `test/highlight.test.ts` is now in the gate, and `bun run check` passes with it: 679 tests across 41 files, 0 failures.
shipped production is serving it deploy caaa084
The highlighter's speed tests no longer assert milliseconds. They now check how cost grows with input size, so they're machine-independent and still fail if the scanner goes quadratic. test/highlight.test.ts is now in the gate.
The latest merge of main conflicted only on forge.json. Both sides had added one file to the end of the check.run list: main added test/web-apps.test.ts, this branch added test/highlight.test.ts. The resolution keeps both. After the merge, bun run check passes: 700 tests across 42 files.
Why change it: both fixed bounds failed on a crew machine under load 20 on 4 cores, not just the 10ms one named in the issue. The 10,000-character line took 25ms, and the pathological-delimiter test took 270ms against its 120ms limit.
The new measure: a helper, growth(), times the work at size n and at 16*n* and reports the exponent k in time ∝ size^k. A linear scanner gives about 1 and a quadratic one about 2; tests require k < 1.5.
- CPU time, not wall time: it reads
process.cpuUsage(), so time spent waiting for other processes doesn't count. With wall time and the same exponent bound, Lua or TOML still failed now and then; with CPU time, 5 runs out of 5 passed. - Repeats: small and large runs alternate, best of three each. A test fails only if k stays over the bound on three separate tries: a one-off spike doesn't repeat, a quadratic does.
- Growth of 16×, not 8×: at 8×, GC noise on the delimiter inputs pushed some ratios to 40–74, too close to quadratic's 64. At 16× the gap is 64× against 256×.
- The comment above
growth()gives this reasoning, as the issue asked.
Proof it still catches the regression:
- A new test feeds
growth()a nested-loop quadratic and asserts it lands over the bound. - I also planted a quadratic in the real scanner by hand:
Out.pushsumming every earlier token's length. Both linearity tests failed, on Python and on the${input. That change is not committed.
Other changes:
- The issue mentions an
EXCLUDEDlist inscripts/suite.ts, but that file doesn't exist; the gate is the file list inforge.json'scheck.run, so the test file is added there. - The header comment in
src/web/highlight.tspromised "under a millisecond"; it now states the growth claim instead.
What to look at: the file now takes about 8–15s instead of about 1s, because it times 16× inputs repeatedly. The delimiter inputs are linear but cost about 1µs per character, mostly in allocation. I left the scanner alone, since that's a separate change. The tests were wrong here, not the code: they claimed a speed for one machine.
To try it: bun test test/highlight.test.ts, then bun run check.
🤖 Generated with Claude Code
Merges the latest main into this branch again. The only conflict was in forge.json: main added test/fleet-ops.test.ts to the check, and this branch had added test/highlight.test.ts. The resolved check runs both files, plus everything that was already there.
The highlighter fix is unchanged: the linearity test still measures how fast time grows as input gets bigger, not raw milliseconds, and test/highlight.test.ts is still part of the check.
Test results: tsc --noEmit passes. In the full sharded check, 727 of 728 tests passed. The one failure was the private-grant test in test/apps-adversarial.test.ts, which hit its 60 s timeout while three shards were running at once. That file isn't touched by this merge, and run on its own it passes 68/68.
🤖 Generated with Claude Code
Operator: main moved under this branch (forge.json's check command was shortened, among others). Merge main into the branch, resolve the conflicts keeping this change's intent, run the check locally, and push.
The highlighter's linearity test no longer uses a fixed 10 ms limit, which failed on slower machines. It now checks how the time grows as the input gets bigger. Because it is no longer tied to how fast the machine is, test/highlight.test.ts joins the check in forge.json.
This push merges the latest main. Main had shortened check.run to a list of short test names after --. The conflict is resolved by taking main's list and adding highlight to the end, so nothing from main is dropped. Compared with main, the branch changes only forge.json, src/web/highlight.ts, test/highlight.test.ts and test/board-windows.test.ts.
One test from main had to change: "the board shows each window to an admin, and each app's efficiency" in test/board-windows.test.ts. The test was wrong, not the code. It expected "$0.40 spent, nothing shipped this week", but that $0.40 is seeded by an earlier test on the fixed date 2026-09-28. On 2026-10-05 that date fell outside "this week", so the test failed on main as well as here. The board test now creates its own app with no autonomy and $0.40 spent 48 hours before now, so its outcome no longer depends on the date.
The full check now passes locally: 855 tests in 57 files, none failing.
🤖 Generated with Claude Code
The highlighter's linearity test no longer uses a fixed 10 ms limit, which failed on slower machines. It now checks how the time grows as the input gets bigger, so it no longer depends on machine speed. That lets test/highlight.test.ts join the check: forge.json's check.run is main's list with highlight added to the end.
This push merges the latest main again. The only conflict was in test/board-windows.test.ts. This branch had changed that test because it expected $0.40 spent "this week", seeded on 2026-09-28, which aged out on 2026-10-05. Main has since fixed the same test the same way: the board test seeds its own idle app with a spend relative to now. I kept main's version, so this pull no longer touches that file. Compared with main, it now changes only forge.json, src/web/highlight.ts and test/highlight.test.ts.
The full check passes locally after the merge: 855 tests in 57 files, none failing.
🤖 Generated with Claude Code
The highlighter's linearity test no longer uses a fixed 10 ms limit, which failed on slower machines. It now checks how the time grows as the input gets bigger, so it no longer depends on machine speed. With that, test/highlight.test.ts joins the check. The diff against main is three files: src/web/highlight.ts, test/highlight.test.ts and scripts/gate.ts.
This push merges the latest main again. Main moved the check's file list out of forge.json and into scripts/gate.ts, where every test file is either in GATE or in EXCLUDED with a reason and an issue number. check.run is now just --gate. To resolve the forge.json conflict I took main's version unchanged. Admitting the file is now done the way scripts/gate.ts describes: the test/highlight.test.ts entry (issue 294) moves from EXCLUDED to GATE. test/check-list.test.ts still finds every test file listed exactly once.
The full check passes locally after the merge: 856 tests in 57 files, none failing.
🤖 Generated with Claude Code
The highlighter's speed tests no longer assert milliseconds. They now check how cost grows with input size, so they're machine-independent and still fail if the scanner goes quadratic.
test/highlight.test.tsis now in the gate, andbun run checkpasses with it: 679 tests across 41 files, 0 failures.Why change it: both fixed bounds failed on a crew machine under load 20 on 4 cores, not just the 10ms one named in the issue. The 10,000-character line took 25ms, and the pathological-delimiter test took 270ms against its 120ms limit.
The new measure: a helper,
growth(), times the work at size n and at 16*n* and reports the exponent k in time ∝ size^k. A linear scanner gives about 1 and a quadratic one about 2; tests require k < 1.5.process.cpuUsage(), so time spent waiting for other processes doesn't count. With wall time and the same exponent bound, Lua or TOML still failed now and then; with CPU time, 5 runs out of 5 passed.growth()gives this reasoning, as the issue asked.Proof it still catches the regression:
growth()a nested-loop quadratic and asserts it lands over the bound.Out.pushsumming every earlier token's length. Both linearity tests failed, on Python and on the${input. That change is not committed.Other changes:
EXCLUDEDlist inscripts/suite.ts, but that file doesn't exist; the gate is the file list inforge.json'scheck.run, so the test file is added there.src/web/highlight.tspromised "under a millisecond"; it now states the growth claim instead.What to look at: the file now takes about 8–15s instead of about 1s, because it times 16× inputs repeatedly. The delimiter inputs are linear but cost about 1µs per character, mostly in allocation. I left the scanner alone, since that's a separate change. The tests were wrong here, not the code: they claimed a speed for one machine.
To try it:
bun test test/highlight.test.ts, thenbun run check.🤖 Generated with Claude Code
Closes #294