Performance audit fixes: budget tolerance ceiling, size report, render and inbox refusals (WF-03, WF-01/02/06/18, WF-25, WF-27, WF-50)

Merged
BI
16h ago
+613-74

Fixes the safe, agreed findings from the 11 November 2026 adversarial review of the external "Ward Flow — Deep Performance Audit" (WF-01 to WF-57, written against bd16e4ad). "WF-xx" below means that document's finding numbers, not the repository's own WF-Fxx items. One PR, per D-107's fewest-PRs rule; each finding is its own commit.

Engineering only. No visible change, no wording change, no What's new file. No script budget or size cap is raised or re-baselined.

What changes

Budget gate: tightening only

  • WF-03: the 1.5 KB tolerance can no longer stack.
    • Before: judgeAgainstMain compared each PR with a main that already held the previous PR's tolerance. Run with the repo's own function, 20 PRs at +1.5 KB each all passed, a +30 KB drift.
    • Now: growth within the tolerance passes only while the route stays within 3 KB (2 × tolerance) of its fixed budget.
    • A PR that measures no bigger than main still always passes. So a main that drifted is never pinned on a PR that didn't grow it, which is the 4 Nov intent.
    • The new test shows the 20-PR chain stopping after at most 2 PRs.

Measurement: report only, never judged (WF-01, WF-02, WF-06, WF-18)

  • What's reported: after its verdict, check-script-budget.mjs prints, saves under report in measured.json and writes to the CI step summary:
    • each budgeted route's stylesheet size;
    • the preloaded script the budget does not count;
    • the script every budgeted route shares (the shared shell);
    • four heavy routes with no budget line (REPORT_ROUTES, triage first).
  • It can't fail a build: an error in this part is only printed, and the exit code is unchanged.
  • Measured on a local production build of 8d948c74:
    • Triage: 719.3 KB of script and 161.6 KB of CSS, with no budget. The largest budget is 667.4 KB.
    • CSS: 115.8 to 191.2 KB across the budgeted and report routes.
    • Preloaded script the budget doesn't count: about 70 KB per route.
    • Shared shell: 479.1 KB.
  • Setting any new limit is Josh's decision, from a measured baseline. This PR sets none.

Render cost, no visible change

  • WF-27, Movements: the Back (popstate) listener was removed and re-added on every movements change. It now reads the latest movements through useEffectEvent and is added once.
  • WF-25, governance register (that part only): each event's reviews were found by scanning every review, several times per row. reviewHistoryLookup now builds the lookup in one pass and returns the same reviews in the same order.

Refuse, don't throw (WF-50, inbox half)

  • ACKNOWLEDGE_INBOX_ITEM, and every other inbox row event, with no text row id threw a TypeError in the audit capture (ward-audit.ts) and the reducer. A missing id is now treated as blank, so the existing refusals apply. No wording change. The server already failed closed, so this only affects the local demo reducer.

Evidence (local, box)

  • Tests that fail before and pass after:
    • ward-ci-budget-heal (2 new cases failed on the old judge).
    • ward-inbox-acknowledge-malformed (9 of 9 threw before).
    • ward-movements-popstate-listener (4 listeners added after two escalations instead of 2).
    • ward-governance-review-history and the report-only cases in ward-script-budget are new modules or exports, so they couldn't run before.
  • Related suites pass:
    • movements screen: 43
    • governance registers and admin role
    • script budget: 55
    • budget heal: 24
    • inbox/audit reducer suites: 18 files, 157 tests
  • Checks on the changed files:
    • lint: clean
    • full typecheck (npm run typecheck): clean
    • docs links: 0 broken
    • doc facts: pass
    • protected safety tests: PASS (none touched)
    • diff integrity: PASS (40 to 51 cases)
    • CI contracts: OK
  • Phone script budget: a local production build of the pre-rebase branch (base 72d99b1cf, before the inbox commit) was within all 9 budgets, with 1.2 to 5.2 KB of headroom. No budget line was touched. After rebasing onto 5b08a5ebe and adding the inbox commit (a few bytes in the reducer), I didn't rebuild locally, so this PR's CI build is the measurement of record.

Not in this PR (owned by another open PR or needing Josh)

  • WF-07 (any raise needs an unforgeable approval, and every raise needs a history file): budget-raise-cap.mjs is in #1614 (A5-02), so I left it alone. It needs Josh's decision too: D-82 allows raises of 5 KB or less without asking, and Josh and the agents share one GitHub identity, so no label or review is unforgeable today.
  • WF-24 (TV board fit loop): a deliberate fit loop that runs only on mount and resize. Low value, and it risks the TV layout.
  • WF-25: the derivations part is in ward-screen-derivations.ts (#1608). The capacity part (releaseParts) isn't worth it: it runs once per ward card over a few releases.
  • WF-29: activity-panel.tsx is in #1605, and the unfolded unread notices are deliberate.
  • WF-50, RECORD_ESCALATION half: ward-flow-record-events.ts is in #1606 and #1608. The server already fails closed on it ("invalid-command"). Fix it after those PRs land.
  • WF-26 (RadioGroup): its effect with no dependency array is deliberate (choices can appear or disappear), and it runs at 6 call sites. Left alone.
  • WF-19, WF-05 and WF-15: not verified in the review.
  • WF-12 (icon caching): needs a versioned favicon name first, for a small gain.
  • WF-22 (idle lock) is in #1611.