Skip to content

refactor: the audit's last three — a truthful guide, a gate for dead code, and 2.9MB of over-fetch on /art - #54

Merged
IngTian merged 1 commit into
mainfrom
claude/audit-round3-fixes
Aug 31, 2026
Merged

IngTian merged 1 commit into
mainfrom
claude/audit-round3-fixes

Conversation

@IngTian

@IngTian IngTian commented Aug 31, 2026

Copy link
Copy Markdown
Owner

Round three of the structure audit raised three findings; these are all of them, plus two defects the work itself uncovered. Nothing changes what a visitor sees. Two things change what a visitor downloads.

1. CLAUDE.md was stale again — the same way twice

The last refactor deleted three, lib/fanScene.ts, FactorFan.astro, sections/Mountains.astro and three of four /proto-* routes. The guide kept describing all of them, including an explicit "keep it that way" about a dependency that no longer exists, and a route table listing twelve pages where the build prints nine.

That is not untidiness. An agent reading a stale guide edits the files the guide names and not the ones that exist — three live defects this month came out of exactly that.

  • Every backticked path is now checked against disk by script, not by eye (58 spans; the only non-resolving ones appear solely in corrective prose).
  • The three bullet is replaced with what is true: four runtime dependencies, and the site's own answer to wanting 3D is hand-rolled projection in lib/terrain.ts behind a still-frame-first gate.
  • The route count is pinned to the number npm run build prints.
  • index.astro's lead comment claimed the deck was CSS scroll-snap. It is explicit JS, and the measurement that settled it now sits in the comment instead of its opposite.

2. Unused code is a gate's job, not a reviewer's

Two audit rounds hand-deleted dead code by reading files — the expensive way, and the way that misses things. noUnusedLocals + noUnusedParameters are on (two flags rather than Astro's strictest, whose other two would each be a real refactor across src/lib).

They immediately found eight dead declarations in three files nobody had touched:

gone why it was dead
flattenStops in Toc the flattened form is what a rail needs; the Toc renders the tree as a tree
topReturn, deepest in Choice an earlier verdict's extremes — the closing sentence makes the point with the levered row now
vars, leafExp in Rules the tally that slide was rewritten to stop quoting: 10^11 futures is brute-forceable in 282 seconds, so quoting it undercuts the claim
MAINTENANCE import in Choice used inside policyPnl.ts, never here
decisionVariables, scenarioLeaves imports cascaded off with the two consts; both stay exported and covered by tests/complexity.test.ts

The flag is per-file and cannot see an unused export — the tsconfig comment says so, so nobody over-trusts it.

Two assertions were added where "two places must agree" had no enforcement: a paper's mathKey must now match the id the page emits (today the build stays green while /research silently loses id="r-p0-math"), and distSmoke now fails on a nav href pointing at a route or file that does not exist, with the #top waiver hole closed. Both were proven to fail on a deliberately broken copy.

3. /art was fetching 2.9MB it could not display

sizes read (max-width:1024px) 45vw, 30vw — describing a fluid grid this page does not have. Rows are justified, so a tile's width is its own aspect ratio × the row height, and 33 of 42 photographs are portrait, rendering 140–300 CSS px wide.

At 1440/DPR2 the old hint picked the 900w variant for all 42:

bytes
before 6,007,600
after 3,128,936
per-photo ideal 3,056,032

47.9% of the fetch was unusable. The hint is now per photo, computed from the same row-height literal artGallery.ts hands justifyRows, with tests/artSizes.test.ts failing if the two drift. Within 2.4% of optimal, and never under-requesting at any container width from 260→3000px.

The <=1024px branch is deliberately unchanged: below that width a row cannot reach the target height, the structural bound goes loose, and switching phones to it measured +3.2MB at DPR3.

4. Fraunces' preload is per page

It belongs on the three routes with an accent rule (/, /research, /writing/<slug>). The other six were fetching 82,184 bytes at the highest priority, ahead of their own content, for a face no rule on them asks for.

Deleting the preload outright is the wrong fix, and that was measured: FCP 1956 → 2333ms for identical bytes on a page that uses it. The @font-face rules stay ungated on all nine pages.

5. A comment shipped 44 bytes of dead CSS to every page

Found while verifying #4 — and it is the nicest bug here. The note explaining the preload gating contained the bare token font-accent. Tailwind v4's scanner harvests utility candidates from source text without parsing it, so it cannot tell a comment from markup: it emitted the matching font-family utility into the shared stylesheet on all nine pages, for a class no element carried. A comment about not shipping bytes nobody needs, shipping bytes nobody needs.

Written with its leading dashes, the stylesheet is byte-identical to the previous release — hash and all. Confirmed in both directions: putting the dashless token back reproduces both the rule and the content hash.

Verification — no browsers, per the project's guide

gate result
npm run build 9 pages, Complete
npm run typecheck 0 errorsastro check and tsc --noEmit
npm test 610 passed / 40 files

The load-bearing check: index.html is byte-identical to the previous release, and the homepage is where every dead-code removal here lives. The stylesheet is byte-identical too. The only intended dist differences are the 12 dropped preload links, the 42 per-photo sizes attributes, and renamed font files whose bytes md5-match.

A verification pass reviewed the whole branch independently, re-derived the /art bound from lib/justify.ts rather than trusting the comment, and caught #5 and a false count in a comment — both fixed here. It initially returned DO NOT MERGE on a red typecheck (the new flags surfaced the eight dead declarations above); that is what section 2 resolves.

One thing left for you, deliberately not decided

FUND.periods in src/data/desk.ts now has no reader in src — its only consumer is tests/complexity.test.ts, which asserts those helpers against real fund scale rather than toy numbers. That file's own rule is "ONLY the ones something reads," so the field is on probation, but dropping it costs that assertion its real-scale input and needs edits in two test files. That is your call, not a side effect of a typecheck fix, so it is documented in place and left alone.

🤖 Generated with Claude Code

…code, and 2.9MB of over-fetch on /art

Round three of the structure audit raised three findings and these are all of them. None
changes what a visitor sees; two change what a visitor downloads.

**CLAUDE.md was stale again, the same way twice.** A delete-heavy refactor removed `three`,
`lib/fanScene.ts`, `FactorFan.astro`, `sections/Mountains.astro` and three of the four
`/proto-*` routes, and the guide kept describing all of them — including an explicit "keep it
that way" about a dependency that no longer existed, and a route table listing twelve pages
where the build prints nine. A guide that names deleted files sends the next agent to edit
them; three live defects this month came out of exactly that. Every backticked path is now
checked against disk (a script, not an eye), the `three` bullet is replaced by what is
actually true — four runtime dependencies, and the site's own answer to wanting 3D is
hand-rolled projection in `lib/terrain.ts` behind a still-frame-first gate — and the route
count is pinned to the number the build prints. `index.astro`'s lead comment claimed the deck
was CSS scroll-snap; it is explicit JS, and the measurement that settled it is now in the
comment instead of its opposite.

**Unused code is a gate's job.** Two consecutive audit rounds hand-deleted dead code by
reading files, which is the expensive way and the way that misses things. `noUnusedLocals`
and `noUnusedParameters` are on, taken as two flags rather than Astro's `strictest` preset
(whose other two would each be a real refactor across `src/lib`). They immediately found
eight dead declarations in three files nobody had touched: `flattenStops` in `Toc` (the
flattened form is what a rail needs, and the Toc renders the tree as a tree), `topReturn` and
`deepest` in `Choice` (an earlier verdict's extremes — the closing sentence makes the point
with the levered row now), and `vars`/`leafExp` in `Rules`, which are the tally that slide was
rewritten to stop quoting, since 10^11 futures is brute-forceable in 282 seconds and quoting
it undercuts the claim. `decisionVariables` and `scenarioLeaves` came off the import with
them; both stay exported and covered by `tests/complexity.test.ts`. The flag is per-file and
cannot see an unused *export*, and the tsconfig comment says so, so nobody over-trusts it.

Two test assertions were added where "two places must agree" had no enforcement: a paper's
`mathKey` now has to match the id the page emits (build stays green today while
`/research` silently loses `id="r-p0-math"`), and `distSmoke` now fails on a nav href
pointing at a file or route that does not exist, with the `#top` waiver hole closed.

**/art was fetching 2.9MB it could not display.** The `sizes` hint read
`(max-width:1024px) 45vw, 30vw`, describing a fluid grid this page does not have: rows are
justified, so a tile's width is its own aspect ratio times the row height, and 33 of the 42
photographs are portrait and render 140-300 CSS px wide. At 1440/DPR2 the old hint picked the
900w variant for all 42 — 6,007,600 bytes against 3,128,936 for the same page, i.e. 47.9% of
the fetch was unusable. The hint is now per photo, computed from the same row-height literal
`artGallery.ts` passes to `justifyRows`, with `tests/artSizes.test.ts` failing if the two
drift. Within 2.4% of per-photo optimal and never under-requesting at any container width
from 260 to 3000px. The <=1024px branch is deliberately unchanged — below that width a row
cannot reach the target height, the structural bound goes loose, and switching phones to it
measured +3.2MB at DPR3.

**Fraunces' preload is per page now.** It belongs on the three routes with an accent rule
(`/`, `/research`, `/writing/<slug>`); the other six were fetching 82,184 bytes at the highest
priority, ahead of their own content, for a face no rule on them asks for. Deleting the
preload outright is the wrong fix and was measured: FCP 1956 -> 2333ms for identical bytes on
a page that uses it. The `@font-face` rules stay ungated on all nine pages.

**And a comment shipped 44 bytes of dead CSS to every page.** The note explaining the preload
gating contained the bare token `font-accent`. Tailwind v4's scanner harvests utility
candidates from source text without parsing it, so it cannot tell a comment from markup: it
emitted the matching font-family utility into the shared stylesheet on all nine pages, for a
class no element carried. Written with its leading dashes the stylesheet is byte-identical to
the previous release, hash and all — which is how this was confirmed in both directions.

Verified without a browser, as the site's guide requires. Build 9 pages, `astro check` and
`tsc --noEmit` both at 0, 610 tests over 40 files. `index.html` is byte-identical to the
previous release, which is the load-bearing check: every dead-code removal here lives on the
homepage. The stylesheet is byte-identical too. The only intended `dist` differences are the
12 dropped preload links, the 42 per-photo `sizes` attributes, and renamed font files whose
bytes md5-match.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@IngTian
IngTian merged commit eb12233 into main Aug 31, 2026
1 check passed
@IngTian
IngTian deleted the claude/audit-round3-fixes branch August 31, 2026 15:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant