Repository navigation
refactor: author the islands and stylesheets in src/, serve them where they were - #451
Merged
Merged
Conversation
Contributor
Wire-size diff vs merge base
Brotli q11, the quality the edge serves. Every number here is deterministic: Client assetsNo change. 21 files, 75.15 KiB Brotli total. PagesNo change. 48 files, 502.05 KiB Brotli total. Worker bundleNo change. 230.11 KiB gzip. Dictionary deltas (
|
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
aadhar-sh | a8b443b | Aug 18 2026, 03:05 PM |
oddharsh
force-pushed
the
refactor/islands-in-src
branch
from
August 18, 2026 14:49
456df0c to
66be21b
Compare
oddharsh
added a commit
that referenced
this pull request
Aug 18, 2026
www/scripts/ becomes tools/photos/. Nothing in it ships: it is the encoder chain, the EXIF reader, the histogram bake and the generators, and it sat in the served tree only because that is where the pixels are. Third slice of lifting the parked bun branch's renames onto main (#450, #451). Toolchain-independent: the scripts still run under bash, node, python3 and uv exactly as they did, and only their paths moved. IT RETIRES A BUILD EXCEPTION RATHER THAN RELOCATING IT. www/scripts was 89% of the tree's bytes (the zenc cargo target and the uv venv, ~232 MB across 767 files, against ~27 MB that actually ships), so build.mjs skipped staging it and carried a paragraph explaining why. With the pipeline outside www/ there is nothing to skip: STAGE_SKIP drops to www/images/meta alone and the paragraph becomes a note about what used to be true. An exception that existed only because dev tooling sat in the served tree should disappear when the tooling leaves. ONE SERVED ASSET CHANGES, deliberately, and it is the interesting part. www/icons.svg embedded its generator BY PATH ("GENERATED by www/scripts/gen-desktop-partial.mjs"), which makes a repository path part of a content-addressed artifact: moving the generator moves the bytes and mints a new URL. It now names the generator by FILENAME, so /a/icons.9ff130b8.svg becomes /a/icons.8d4b5912.svg once here and can never move again for that reason. Blast radius, checked rather than assumed: svg sits out the dictionary path by design (build.mjs skips it, and the file's own comment says so), and no a-dict entry names icons, so no committed dictionary is orphaned. Every other served artifact is byte-identical across the 1542 compared. The other differing file is www/.assetsignore, whose comment now says the pipeline USED to live here. It is an upload filter, never served. Gates: 305 tests, lint, typecheck, pages:check, routes:check, perf-budget, check-wrangler.
oddharsh
force-pushed
the
refactor/worker-out-of-served-tree
branch
from
August 18, 2026 14:52
29f6b73 to
79222d5
Compare
oddharsh
force-pushed
the
refactor/islands-in-src
branch
from
August 18, 2026 14:52
66be21b to
2e58b2c
Compare
oddharsh
added a commit
that referenced
this pull request
Aug 18, 2026
www/scripts/ becomes tools/photos/. Nothing in it ships: it is the encoder chain, the EXIF reader, the histogram bake and the generators, and it sat in the served tree only because that is where the pixels are. Third slice of lifting the parked bun branch's renames onto main (#450, #451). Toolchain-independent: the scripts still run under bash, node, python3 and uv exactly as they did, and only their paths moved. IT RETIRES A BUILD EXCEPTION RATHER THAN RELOCATING IT. www/scripts was 89% of the tree's bytes (the zenc cargo target and the uv venv, ~232 MB across 767 files, against ~27 MB that actually ships), so build.mjs skipped staging it and carried a paragraph explaining why. With the pipeline outside www/ there is nothing to skip: STAGE_SKIP drops to www/images/meta alone and the paragraph becomes a note about what used to be true. An exception that existed only because dev tooling sat in the served tree should disappear when the tooling leaves. ONE SERVED ASSET CHANGES, deliberately, and it is the interesting part. www/icons.svg embedded its generator BY PATH ("GENERATED by www/scripts/gen-desktop-partial.mjs"), which makes a repository path part of a content-addressed artifact: moving the generator moves the bytes and mints a new URL. It now names the generator by FILENAME, so /a/icons.9ff130b8.svg becomes /a/icons.8d4b5912.svg once here and can never move again for that reason. Blast radius, checked rather than assumed: svg sits out the dictionary path by design (build.mjs skips it, and the file's own comment says so), and no a-dict entry names icons, so no committed dictionary is orphaned. Every other served artifact is byte-identical across the 1542 compared. The other differing file is www/.assetsignore, whose comment now says the pipeline USED to live here. It is an upload filter, never served. Gates: 305 tests, lint, typecheck, pages:check, routes:check, perf-budget, check-wrangler.
oddharsh
enabled auto-merge (squash)
August 18, 2026 15:02
…e they were The 15 client islands and the two stylesheets move from www/ to src/client/ and src/styles/. Their SERVED paths do not change: /nav.js is still /nav.js, and /a/nav.7ab9cce6.js still carries the same hash, because the build stages them to where they have always been served from. That split is the point. www/ is what a browser can fetch; an island is SOURCE that a build minifies, hashes and ships beside a readable .src.js twin. Keeping authored source and served bytes in one directory is what made it possible to edit a served asset by accident. Second slice of lifting the parked bun branch's renames onto main (#450 was the first). Toolchain-independent: no bun anywhere in it. NO SERVED BYTE CHANGES. Built before and after under node with pnpm: all 1542 served artifacts byte-identical, content hashes included. Gates: 305 tests, lint, typecheck, pages:check, routes:check, perf-budget. CLAUDE.md, MAINTENANCE.md and design/README.md come along, since all three point at these files by path.
oddharsh
force-pushed
the
refactor/islands-in-src
branch
from
August 18, 2026 15:04
2e58b2c to
a8b443b
Compare
oddharsh
added a commit
that referenced
this pull request
Aug 18, 2026
www/scripts/ becomes tools/photos/. Nothing in it ships: it is the encoder chain, the EXIF reader, the histogram bake and the generators, and it sat in the served tree only because that is where the pixels are. Third slice of lifting the parked bun branch's renames onto main (#450, #451). Toolchain-independent: the scripts still run under bash, node, python3 and uv exactly as they did, and only their paths moved. IT RETIRES A BUILD EXCEPTION RATHER THAN RELOCATING IT. www/scripts was 89% of the tree's bytes (the zenc cargo target and the uv venv, ~232 MB across 767 files, against ~27 MB that actually ships), so build.mjs skipped staging it and carried a paragraph explaining why. With the pipeline outside www/ there is nothing to skip: STAGE_SKIP drops to www/images/meta alone and the paragraph becomes a note about what used to be true. An exception that existed only because dev tooling sat in the served tree should disappear when the tooling leaves. ONE SERVED ASSET CHANGES, deliberately, and it is the interesting part. www/icons.svg embedded its generator BY PATH ("GENERATED by www/scripts/gen-desktop-partial.mjs"), which makes a repository path part of a content-addressed artifact: moving the generator moves the bytes and mints a new URL. It now names the generator by FILENAME, so /a/icons.9ff130b8.svg becomes /a/icons.8d4b5912.svg once here and can never move again for that reason. Blast radius, checked rather than assumed: svg sits out the dictionary path by design (build.mjs skips it, and the file's own comment says so), and no a-dict entry names icons, so no committed dictionary is orphaned. Every other served artifact is byte-identical across the 1542 compared. The other differing file is www/.assetsignore, whose comment now says the pipeline USED to live here. It is an upload filter, never served. Gates: 305 tests, lint, typecheck, pages:check, routes:check, perf-budget, check-wrangler.
oddharsh
added a commit
that referenced
this pull request
Aug 18, 2026
www/scripts/ becomes tools/photos/. Nothing in it ships: it is the encoder chain, the EXIF reader, the histogram bake and the generators, and it sat in the served tree only because that is where the pixels are. Third slice of lifting the parked bun branch's renames onto main (#450, #451). Toolchain-independent: the scripts still run under bash, node, python3 and uv exactly as they did, and only their paths moved. IT RETIRES A BUILD EXCEPTION RATHER THAN RELOCATING IT. www/scripts was 89% of the tree's bytes (the zenc cargo target and the uv venv, ~232 MB across 767 files, against ~27 MB that actually ships), so build.mjs skipped staging it and carried a paragraph explaining why. With the pipeline outside www/ there is nothing to skip: STAGE_SKIP drops to www/images/meta alone and the paragraph becomes a note about what used to be true. An exception that existed only because dev tooling sat in the served tree should disappear when the tooling leaves. ONE SERVED ASSET CHANGES, deliberately, and it is the interesting part. www/icons.svg embedded its generator BY PATH ("GENERATED by www/scripts/gen-desktop-partial.mjs"), which makes a repository path part of a content-addressed artifact: moving the generator moves the bytes and mints a new URL. It now names the generator by FILENAME, so /a/icons.9ff130b8.svg becomes /a/icons.8d4b5912.svg once here and can never move again for that reason. Blast radius, checked rather than assumed: svg sits out the dictionary path by design (build.mjs skips it, and the file's own comment says so), and no a-dict entry names icons, so no committed dictionary is orphaned. Every other served artifact is byte-identical across the 1542 compared. The other differing file is www/.assetsignore, whose comment now says the pipeline USED to live here. It is an upload filter, never served. Gates: 305 tests, lint, typecheck, pages:check, routes:check, perf-budget, check-wrangler.
oddharsh
added a commit
that referenced
this pull request
Aug 18, 2026
…452) www/scripts/ becomes tools/photos/. Nothing in it ships: it is the encoder chain, the EXIF reader, the histogram bake and the generators, and it sat in the served tree only because that is where the pixels are. Third slice of lifting the parked bun branch's renames onto main (#450, #451). Toolchain-independent: the scripts still run under bash, node, python3 and uv exactly as they did, and only their paths moved. IT RETIRES A BUILD EXCEPTION RATHER THAN RELOCATING IT. www/scripts was 89% of the tree's bytes (the zenc cargo target and the uv venv, ~232 MB across 767 files, against ~27 MB that actually ships), so build.mjs skipped staging it and carried a paragraph explaining why. With the pipeline outside www/ there is nothing to skip: STAGE_SKIP drops to www/images/meta alone and the paragraph becomes a note about what used to be true. An exception that existed only because dev tooling sat in the served tree should disappear when the tooling leaves. ONE SERVED ASSET CHANGES, deliberately, and it is the interesting part. www/icons.svg embedded its generator BY PATH ("GENERATED by www/scripts/gen-desktop-partial.mjs"), which makes a repository path part of a content-addressed artifact: moving the generator moves the bytes and mints a new URL. It now names the generator by FILENAME, so /a/icons.9ff130b8.svg becomes /a/icons.8d4b5912.svg once here and can never move again for that reason. Blast radius, checked rather than assumed: svg sits out the dictionary path by design (build.mjs skips it, and the file's own comment says so), and no a-dict entry names icons, so no committed dictionary is orphaned. Every other served artifact is byte-identical across the 1542 compared. The other differing file is www/.assetsignore, whose comment now says the pipeline USED to live here. It is an upload filter, never served. Gates: 305 tests, lint, typecheck, pages:check, routes:check, perf-budget, check-wrangler. Co-authored-by: Aadharsh Pannirselvam <19518661+oddharsh@users.noreply.github.com>
oddharsh
added a commit
that referenced
this pull request
Aug 18, 2026
44 developer scripts move from scripts/ to tools/, joining tools/photos and tools/oxlint. Nothing in any of them ships; they are the build, the checks, the generators and the release tooling, and having two sibling directories for one job was a split by history rather than by kind. Fourth slice of lifting the parked bun branch's renames onto main (#450, #451, #452). This one could NOT be cherry-picked: that commit sits on top of the TypeScript conversion and brings 714 conflicts, so the move is done by hand against main's layout instead. THE REFERENCE SWEEP IS BY FILENAME, not by prefix. Rewriting `scripts/` blindly would have renamed package.json's "scripts" KEY and any /scripts/ URL in prose, so each of the 44 moved names was replaced exactly: 47 files updated. design/PORTING.md keeps its `scripts/gen-desktop-partial.mjs` mention, since that file is documented history rather than instruction. ONE VISITOR-FACING STRING CHANGES, and it is the reason this is not a pure rename. /lens prints a command for the reader to run: Use `node scripts/lens-webmcp.mjs <url>` on the current machine That script moved, so the instruction had to move with it or it would name a path that no longer exists. The string lives in src/client/lens-browser.js, which is content-addressed, so it cascades exactly three links: lens-browser.js 821aa61e -> 62f8bf05 the string itself lens.js c0c21217 -> a9ee703b imports lens-browser by hashed URL lens-boot.js a8fd9733 -> c7fac8b7 imports lens.js by hashed URL and from there to /lens.html, its .src.html twin, and the dcz deltas for those three assets. 10 staged files leave, 13 arrive. Everything else is byte-identical. Six Worker sources change only in comments naming a moved script, which reaches the bundle and no served asset. Gates: 305 tests, lint, typecheck, build, photos:check, routes:check, pages:check, tools:check, perf-budget, check-wrangler, the cal suite and a wrangler dry-run. Co-authored-by: Aadharsh Pannirselvam <19518661+oddharsh@users.noreply.github.com>
oddharsh
added a commit
that referenced
this pull request
Aug 18, 2026
…rc/dict/ (#455) www/a-dict/ and www/p-dict/ become src/dict/a-dict/ and src/dict/p-dict/. They are the previously shipped bytes of the content-hashed shell and of each page, kept so build.mjs can compute dcz deltas against what a returning visitor still holds. Nothing in either directory is ever served. Fifth slice of lifting the parked bun branch's renames onto main (#450, #451, #452, #453), and the first of three that finish www/. Done by hand rather than cherry-picked, for the reason #453 records: the branch's split commit sits on the TypeScript conversion and brings 714 conflicts. BEING OUTSIDE www/ IS A STRONGER GUARANTEE THAN THE .assetsignore ENTRY IT REPLACES. An ignore line stops an upload; living outside the served tree means the build never stages the files at all. The staged tree loses 130 files that were copied on every build purely so wrangler could then decline to upload them. The contract test that asserted the ignore entry now asserts the directory is absent from www/ instead, which is the property that actually matters. NO SERVED ARTIFACT CHANGES: 1496 compared, all byte-identical. The only staged differences are those 130 snapshots no longer being copied, and .assetsignore losing two entries that can no longer match anything. Also removes a filter in build.mjs's view-transition tripwire that skipped "a-dict/" while walking www/. With a-dict gone from that tree the exclusion could not match, and a filter that cannot fire reads as protection that is not there. Gates: 305 tests, build, lint, typecheck, routes:check, pages:check, tools:check, perf-budget, photos:check, check-wrangler, cal, wrangler dry-run. Co-authored-by: Aadharsh Pannirselvam <19518661+oddharsh@users.noreply.github.com>
oddharsh
added a commit
that referenced
this pull request
Aug 18, 2026
The last of the www/ split. 36 HTML documents become src/pages/; the other 804 files, which are bytes a browser fetches unchanged, become public/. The staged root renames with them: .build/www is .build/public, and wrangler.jsonc's assets.directory follows. Seventh and final slice of lifting the parked bun branch's renames onto main (#450, #451, #452, #453, #455, #456). The mapping needed no judgement: main's www/ held exactly 840 files, the branch's two destinations hold exactly those 840, and every one lands in exactly one of them. Every HTML is a page; nothing else is. EVERY SERVED ARTIFACT IS BYTE-IDENTICAL, 1497 compared with the root name normalised. The four remaining differences are path references inside cal, serendipity and the Worker: three comments and one JSON import specifier. THE BUILD COMPOSES THE SERVED TREE NOW rather than copying one directory. public/ + src/pages/ + src/content/ + src/client/ + src/styles/ all stage into .build/public, so the URL layout is untouched while the source layout says what each file IS. servedFiles() walks the three authored roots in one place, because a tripwire that walks only some of them reports a clean tree it never read. SIX THINGS THE GATES CAUGHT, and the pattern is worth stating: a blanket path sweep cannot tell a source read from a staged path, an asset from a document, or a path from a regex. * staticShellPages() read www/ for the freshness check * the Worker imported ../../www/images/hashes.json * gen-md-twins looked for page HTML under www/, then read the generated tier from the staged www/ root * the page generators in pipelines/ wrote into www/ * the .src.html twin read its AUTHORED source from the wrong root, so it fell into a catch and shipped the STAGED bytes: readable twins carrying rewritten /a/ hashes, which is the whole point of the twin lost silently. The build's own counter is what showed it, 47 from staged where main says 12. * and one false positive: `assert.match(lens, /… 401 && www/)` is a regex whose DELIMITER the sweep read as a path separator, rewriting the assertion out from under a real variable named www. Gates: 305 tests, build, lint, typecheck, routes:check, pages:check, tools:check, perf-budget, photos:check, check-wrangler, cal, wrangler dry-run. Co-authored-by: Aadharsh Pannirselvam <19518661+oddharsh@users.noreply.github.com>
oddharsh
added a commit
that referenced
this pull request
Aug 18, 2026
* docs: the layout table describes the tree that now exists Seven slices moved almost every path in this repository (#450, #451, #452, #453, #455, #456, #457), and the automated sweeps that repointed code also walked CLAUDE.md. That left the map wrong in two different ways. STALE: the table still said public/ holds "pages, client scripts, CSS, photos, dictionaries". It holds none of the first four now. Rows added for src/pages, src/content and src/dict; scripts/ folded into the tools/ row; every row's description checked against the tree rather than against its own history. FALSE: the sweep rewrote HISTORICAL names, so the file claimed the Worker lived at `public/_worker.js/` and the photo pipeline at `public/scripts/`. Neither ever existed. A stale note is a note somebody distrusts; a false one is a note somebody follows. The naming note now records the full sequence, holding -> www -> the split, and says explicitly that the rewrites deliberately left old mentions alone, so reading `www/` in an older note is correct rather than a typo. It also drops the "duplicate path" explanation, since scripts/ no longer exists to be confused with tools/photos/. And it states the test for where a new file goes, which the table never did: if a build step transforms it, it is src/; if it ships byte-for-byte, it is public/. * docs: the intro names the split tree too The bullet under the header still called public/ the live site including the _worker.js/ dispatcher, which is two moves out of date: the Worker left the served tree in #450 and the documents left it in #457. It now names what each root is for, which is the same question the table below answers per entry. --------- Co-authored-by: Aadharsh Pannirselvam <19518661+oddharsh@users.noreply.github.com>
oddharsh
added a commit
that referenced
this pull request
Aug 19, 2026
…eated (#467) #452 moved the photo pipeline to tools/photos/ and #451/#457 moved the served tree to public/, and four ignore rules kept naming the old locations: www/scripts/zenc/target/ -> tools/photos/zenc/target/ www/scripts/.venv/ -> tools/photos/.venv/ www/ad/ -> public/ad/ www/images/meta/ -> public/images/meta/ None of the new paths existed yet, which is why nobody hit it. The first person to run photos:env or build zenc would have had a 13M venv and 218M of Rust build output appear as untracked, in the one place a git add -A would sweep them in. Verified with git check-ignore: all four new paths are ignored, and none of them is tracked today, so no committed file changes status. Co-authored-by: Aadharsh Pannirselvam <19518661+oddharsh@users.noreply.github.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.
Stacked on #450. Second slice of lifting the parked bun branch's renames onto
main.The 15 client islands and the two stylesheets move from
www/tosrc/client/andsrc/styles/. Their served paths do not change:/nav.jsis still/nav.js, and/a/nav.7ab9cce6.jsstill carries the same hash, because the build stages them to where they have always been served from.That split is the whole point.
www/is what a browser can fetch; an island is source that a build minifies, hashes, and ships beside a readable.src.jstwin. Keeping authored source and served bytes in one directory is what made it possible to edit a served asset by accident.No served byte changes
Built before and after under node with pnpm: all 1542 served artifacts byte-identical, content hashes included. That last part is the one that matters here — a moved island that re-hashed would mint new
/a/URLs and orphan every committed dictionary.Gates
305 tests, lint, typecheck,
pages:check,routes:check,perf-budget.CLAUDE.md, MAINTENANCE.md and
design/README.mdcome along, since all three point at these files by path.Where this sits
www/www/split +scripts/→tools/Both slices so far cherry-picked from #443 with a single conflict each, and that conflict was the bun migration doc, which does not belong on
main.