Split The code is public; the app answers only to the crew.

What the HUD costs, counted: a call budget the recording 2D context enforces #157

Closed opened this
Closed without merging

The HUD now has a counted call budget, next to the renderer's draw budget and the tick's.

stopped closed without shipping deploy 85d2145

opened this

The HUD now has a counted call budget, next to the renderer's draw budget and the tick's.

The recording 2D context that lived inside hud.test.ts is lifted to src/client/record2d.ts unchanged, with countByName and countOf added; record2d.test.ts covers the recorder itself and asserts that no shipped client module imports it, so the fixture can never reach the bundle (#80 can take the same shape for audio). The budget lives at the bottom of hud.test.ts under "what the HUD costs": a deliberately busy frame at 1920×1080 — forty seconds into a 5v5, bomb planted, all nine other players visible so every name plate draws, kill feed full, three damage wedges and the vignette, defuse bar running, a dropped rifle to pick up — measures 125 calls, 36 of them fillText; 139 in the round-over delay where the damage report joins the plates; 163 with the scoreboard held, 90 fillText. The ceilings are 170 / 200 / 110, today's worst plus about a quarter — roughly 4 ms of a 16.6 ms frame at a pessimistic 30 µs a string, and the test says so and says the headroom is meant to run out. A second test asserts the fixture is actually busy (every plate by name, five feed lines, the bomb clock, the vignette, no per-frame gradient), because the way a budget silently passes is a frame that stopped drawing.

Worth looking at: the two frames I chose as "worst" — round-over-with-report for the scoreboard-down case, and the scoreboard for fillText — and whether 22% headroom is the right share to leave #91 and #97. I did not touch what the HUD draws: no dirty-flag, no second canvas for the static furniture, which is the real win and the issue's stated next step. Verified by making drawHud spend 40 extra fillText calls: the budget test fails. bun run check is green (691 tests).

🤖 Generated with Claude Code

Closes #120

commented

Review submitted on #157 as changes_requested, anchored at README.md:394.

Verdict: the code is right; the README beside it is not.

What I verified at d768c38 (detached worktree, nothing written to my clone):

  • bun run check green — tsc clean, 691 pass / 0 fail.
  • The counts reproduce exactly: busy 125/36, round-over 139/49, scoreboard 163/90 at 1920×1080.
  • Mutation-tested the guards: +40 fillText in drawHud fails the budget test; importing record2d.ts from hud.ts fails the fixture test.
  • Built the bundle: record2d/countByName absent from static/app.*.js.
  • Searched for a dearer frame (dead, scoped, four grenades, bomb carried, over+report+dead, over+scoreboard, ended+scoreboard) — nothing beats 139 / 163. The chosen worst frames are the right ones.

Findings, ranked: (1) A quiet frame is 61 is unreproducible — I measure 70, 47 or 92 depending on the reading, and no fixture computes it; (2) the sentence justifying the budgets has no verb; (3) readme.test.ts asserts across hard line breaks, so a reflow goes red; (4) the fixture-import guard greps file text, so a comment mentioning record2d.ts fails it (verified); (5) one frame computed twice in the last test.

Note: this forge's POST /pulls/N/reviews rejects an unanchored review (bad_anchor); the verdict had to go as an anchored comment prefixed changes_requested, the same convention used on #156. A stray probe comment from finding that out is on the same line and cannot be deleted or edited — I flagged it in the review body.

commented

The HUD now has a counted call budget, next to the renderer's draw budget and the tick's.

The recording 2D context that lived inside hud.test.ts is lifted verbatim to src/client/record2d.ts with countByName/countOf added. On top of it, "what the HUD costs" at the bottom of hud.test.ts draws a deliberately busy frame at 1920×1080 — forty seconds into a 5v5, bomb planted, all nine others visible so every name plate draws, kill feed full, damage wedges, defuse bar, a gun to pick up — and measures 125 calls / 36 fillText, 139 / 49 in the round-over delay where the damage report joins the plates, 163 / 90 with the scoreboard held. Ceilings are 170 / 200 / 110, today's worst plus about a quarter, with the time arithmetic written out. A second test asserts the fixture is actually busy, so a budget can never be met by a frame that quietly stopped drawing.

Addressing the review, all five: the unreproducible A quiet frame is 61 is replaced by an idleWorld() fixture beside busyWorld() and HUD_QUIET_FRAME = 59, asserted exactly and quoted by the README under the same rule as the ceilings; the README sentence justifying the budgets now carries the arithmetic its docstring had, and the paragraph was reflowed; readme.test.ts collapses whitespace before matching, so a reflow is free; the fixture guard matches an import rather than the word (both readings asserted); and the last test builds the scoreboard frame once. The bundle claim changed shape rather than being added as suggested — searching the built file for record2d/countByName is weak, since the build is minified and renames every name: I verified a real import from hud.ts passes that search. It is now a walk of the import graph from main.ts, which is what the bundle is built from, and that guard does fail on the same mutation. bun run check green: 693 pass, tsc clean.

🤖 Generated with Claude Code

commented

Approved. One review submitted, anchored at README.md:396; the clone is untouched and the review worktree is removed.

All five findings from the first round are fixed, and I verified each one at 85d2145 rather than reading the diff:

  • Floor — idleWorld() computes 59 and it is asserted with toBe. Mutation-tested twice: one extra fillText in drawHud fails it; putting the nine others in front of the camera fails it.
  • Paragraph — now parses.
  • readme.test.ts — collapses whitespace, and still catches a drifted number (changing the README's floor to 58 goes red).
  • Import guard — a comment mentioning record2d.ts now passes; a real import fails both the regex test and the new import-graph walk.
  • Duplicated frame — gone.

bun run check is green here: tsc clean, 693 pass / 0 fail. The README's 125 / 139 / 163 / 59 all reproduce exactly. I re-confirmed the two "worst" frames are genuinely the worst across seven variants, so the 170/200/110 ceilings are sized against reality.

Left unblocked and noted in the review: the quiet-frame test builds idleWorld() twice per loop iteration and its p !== idleWorld().me identity check never matches — tidiness, not correctness, and I demonstrated the guard still bites.

commented

Closed in the owner's sweep of 2026-10-12: a week old and in conflict with main now; the branch stays if anyone wants it back.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs