refactor: the audit's last three — a truthful guide, a gate for dead code, and 2.9MB of over-fetch on /art - #54
Merged
Conversation
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.astroand 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.
threebullet is replaced with what is true: four runtime dependencies, and the site's own answer to wanting 3D is hand-rolled projection inlib/terrain.tsbehind a still-frame-first gate.npm run buildprints.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+noUnusedParametersare on (two flags rather than Astro'sstrictest, whose other two would each be a real refactor acrosssrc/lib).They immediately found eight dead declarations in three files nobody had touched:
flattenStopsinToctopReturn,deepestinChoicevars,leafExpinRulesMAINTENANCEimport inChoicepolicyPnl.ts, never heredecisionVariables,scenarioLeavesimportstests/complexity.test.tsThe 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
mathKeymust now match the id the page emits (today the build stays green while/researchsilently losesid="r-p0-math"), anddistSmokenow fails on a nav href pointing at a route or file that does not exist, with the#topwaiver hole closed. Both were proven to fail on a deliberately broken copy.3. /art was fetching 2.9MB it could not display
sizesread(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:
47.9% of the fetch was unusable. The hint is now per photo, computed from the same row-height literal
artGallery.tshandsjustifyRows, withtests/artSizes.test.tsfailing if the two drift. Within 2.4% of optimal, and never under-requesting at any container width from 260→3000px.The
<=1024pxbranch 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-facerules 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
npm run buildnpm run typecheckastro checkandtsc --noEmitnpm testThe load-bearing check:
index.htmlis 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 intendeddistdifferences are the 12 dropped preload links, the 42 per-photosizesattributes, and renamed font files whose bytes md5-match.A verification pass reviewed the whole branch independently, re-derived the
/artbound fromlib/justify.tsrather 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.periodsinsrc/data/desk.tsnow has no reader insrc— its only consumer istests/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