Skip to content

refactor: authored prose moves to src/content/ - #456

Merged
oddharsh merged 1 commit into
mainfrom
refactor/content-into-src
Aug 18, 2026
Merged

oddharsh merged 1 commit into
mainfrom
refactor/content-into-src

Conversation

@oddharsh

Copy link
Copy Markdown
Owner

www/writing/, www/md/ and the four root .md documents become src/content/. The writing posts and their posts.json, the hand-authored twins for the three Worker-rendered pages, and index/README/auth/resume are authored text, not assets that happen to sit in the served tree.

Sixth slice of lifting the parked bun branch's renames onto main, and the second of three that finish www/.

Every public URL is unchanged

Unlike the dictionaries in #455, most of this is served: /writing/<slug>.txt, /index.md, /resume.md. So build.mjs stages src/content back into the served tree at the same paths. Source layout and URL layout are different questions, and only the first one moved.

Staged tree byte-identical across 1497 files, except three Worker sources whose comments name a moved path. Those reach the bundle and no served asset.

Two silent failures the gates caught

Both from one path sweep that could not tell a source read from a served path:

routes:check caught the first. gen-md-twins.mjs holds HAND_DIR = "www/md" with no trailing slash, so a sweep keyed on www/md/ missed it and the three hand-authored twins stopped publishing. Five routes went 404: /security.md, /lens.md, /coffee.md, /around.md, /terminal.md.

The byte control caught the second, and nothing else would have. The same script decides a committed twin wins by looking for it under www/. With index.md moved, that lookup silently failed and the generator wrote its own homepage twin over the hand-written one: 2170 bytes where the real file is 2642. No error, no warning, a live URL quietly degraded to a machine extraction of the page.

Both are the lesson from icons.svg in #452 in a different costume: a path naming a repository location from inside a program keeps working right up until the location moves, and the failure is quiet.

Gates

305 tests, build, routes:check, lint, typecheck, pages:check, tools:check, perf-budget, photos:check, check-wrangler, the cal suite, and a wrangler deploy --dry-run.

What is left of the split

Only the largest piece: pages to src/pages/, assets to public/. That one renames the staged root and touches wrangler.jsonc's assets.directory, which is a different class of risk from these two, so it stays its own PR.

www/writing/, www/md/ and the four root .md documents become src/content/. The
writing posts and their posts.json, the hand-authored twins for the three
Worker-rendered pages, and index/README/auth/resume are AUTHORED text, not
assets that happen to sit in the served tree.

Sixth slice of lifting the parked bun branch's renames onto main, and the second
of three that finish www/.

EVERY PUBLIC URL IS UNCHANGED. Unlike the dictionaries in #455, most of this IS
served (/writing/<slug>.txt, /index.md, /resume.md), so build.mjs stages
src/content back into the served tree at the same paths. Source layout and URL
layout are different questions; only the first one moved.

Staged tree byte-identical across 1497 files, except three Worker sources whose
COMMENTS name a moved path. Those reach the bundle and no served asset.

TWO THINGS THE GATES CAUGHT, both from a path sweep that could not tell one kind
of reference from another:

  * gen-md-twins.mjs's HAND_DIR was the string "www/md" with no trailing slash,
    so the sweep missed it and the three hand-authored twins stopped publishing.
    routes:check found it: /security.md, /lens.md, /coffee.md, /around.md and
    /terminal.md all 404.
  * the same script decides a COMMITTED twin wins by looking for it under www/.
    With index.md moved, that lookup silently failed and the generator wrote its
    own homepage twin over the hand-written one: 2170 bytes where the real file
    is 2642. Nothing errored. The byte control is what surfaced it.

Both are the same lesson as the icons.svg path in #452: a path that names a
repository location from inside a program keeps working right up until the
location moves, and the failure is quiet.

Gates: 305 tests, build, routes:check, lint, typecheck, pages:check,
tools:check, perf-budget, photos:check, check-wrangler, cal, wrangler dry-run.
@oddharsh
oddharsh enabled auto-merge (squash) August 18, 2026 15:39
@github-actions

Copy link
Copy Markdown
Contributor

Wire-size diff vs merge base

b531e6c → aafdbfe

Brotli q11, the quality the edge serves. Every number here is deterministic:
an unchanged file produces no row. Advisory — this check fails on nothing.

Client assets

No change. 21 files, 75.10 KiB Brotli total.

Pages

No change. 48 files, 502.10 KiB Brotli total.

Worker bundle

No change. 230.10 KiB gzip.

Dictionary deltas (pd/)

No change. 144 deltas, 456.84 KiB total.

@oddharsh
oddharsh merged commit 1ead759 into main Aug 18, 2026
7 checks passed
@oddharsh
oddharsh deleted the refactor/content-into-src branch August 18, 2026 15:40
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 24, 2026
…26-08-14

`src/content/md/` holds seven hand-authored twins (/around, /bot, /coffee,
/lens, /security, /terminal, /whoareyou). CLAUDE.md's key-files row, its layout
table, `.assetsignore`'s comment and the generator's own header all said three,
and all four still called the directory `public/md/`.

The count could rot because the set is not a list anywhere in the code.
`buildTwins` looks for `<path>.md` per SURFACE, so a page joins by someone
dropping a file in: /terminal joined in #227 and /around, /coffee and /lens in
#354, and nothing announced either. What the row now records is the mechanism
rather than the membership, plus the fact that `checkTwinFacts()` pins only
three of the seven, so the other four can drift from their page silently.

Two neighbouring rows named prose that moved in the same #456 split:

- `public/writing/` is `src/content/writing/`, and its render code is in
  `src/worker/` rather than `_worker.js`.
- `public/index.md` is `src/content/index.md`. That path confusion has already
  cost something: gen-md-twins looked in `public/` after the move, silently
  stopped finding the hand twin, and overwrote it with a generated one 472
  bytes shorter. The row records that now.

`.assetsignore` also opened on "Files in `www/`" and called the deltas
`www/ad/`. Its two paragraphs about `www/scripts` and the old dictionary
location stay, since those are the record of why there is no entry at either
path.
oddharsh added a commit that referenced this pull request Aug 24, 2026
…26-08-14

`src/content/md/` holds seven hand-authored twins (/around, /bot, /coffee,
/lens, /security, /terminal, /whoareyou). CLAUDE.md's key-files row, its layout
table, `.assetsignore`'s comment and the generator's own header all said three,
and all four still called the directory `public/md/`.

The count could rot because the set is not a list anywhere in the code.
`buildTwins` looks for `<path>.md` per SURFACE, so a page joins by someone
dropping a file in: /terminal joined in #227 and /around, /coffee and /lens in
#354, and nothing announced either. What the row now records is the mechanism
rather than the membership, plus the fact that `checkTwinFacts()` pins only
three of the seven, so the other four can drift from their page silently.

Two neighbouring rows named prose that moved in the same #456 split:

- `public/writing/` is `src/content/writing/`, and its render code is in
  `src/worker/` rather than `_worker.js`.
- `public/index.md` is `src/content/index.md`. That path confusion has already
  cost something: gen-md-twins looked in `public/` after the move, silently
  stopped finding the hand twin, and overwrote it with a generated one 472
  bytes shorter. The row records that now.

`.assetsignore` also opened on "Files in `www/`" and called the deltas
`www/ad/`. Its two paragraphs about `www/scripts` and the old dictionary
location stay, since those are the record of why there is no entry at either
path.
oddharsh added a commit that referenced this pull request Aug 24, 2026
…#563)

* docs: the runbooks still describe the pnpm tree and the www directory

`pnpm exec` refuses on a bun tree outright (it reads package.json's
`packageManager`), and there is no pnpm workspace left for `--filter` to read,
so every pnpm command in these files fails rather than degrading. Fixed in the
clone-and-verify recipe, cal/README.md, `checkpoints:check`, `bun why esbuild`,
and three live instructions in CLAUDE.md (`deploy:direct`, `infra:apply`, the
cal secrets block).

Four claims contradicted a value something else checks:

- MAINTENANCE.md's rollout recipe told you to type `pnpm exec wrangler versions
  upload` into the Workers Builds dashboard, where infra.json declares `bash
  .github/deploy-wrangler.sh versions upload` and check-infra.mjs exact-matches
  it. Following the recipe reddens `validate`. CLAUDE.md's gotcha 29 carried the
  same stale string, and infra.json's own prose said "both moved to `pnpm exec`"
  fifteen lines above the value that is the wrapper.
- MAINTENANCE.md said the wrapper picks its invocation from the lockfile. It
  stopped doing that on 2026-08-20 and runs the pinned entry file under node,
  because wrangler does not support bun and the refusal is per-command.
- CLAUDE.md said the ramp calls `pnpm exec wrangler`. Spawns go through
  `wranglerCommand()` in tools/lib/wrangler-bin.ts, which names no package
  manager. This one already cost a release: five tools were still spawning
  `pnpm exec` the day the tree became bun, and two of them swallowed the
  refusal and reported a build that measured nothing.
- CLAUDE.md's root-files list named `pnpm-workspace.yaml` and `pnpm-lock.yaml`.
  The lockfile is `bun.lock` and package.json carries the four workspaces.

`www/` survived in two places. design/DESIGN.md cited `www/index.html` and
`www/garage/*` for the two canonical Luna surfaces, and src/content/README.md
was a whole fossil of the old directory, titled `# www/`, describing
`_worker.js` and `scripts/` and linking a MAINTENANCE.md that resolves to
nothing from where it now sits. That file BUILDS to .build/public/README.md, so
it has been serving at aadhar.sh/README.md the whole time.

Gotchas 29 and 30 keep their pnpm, since they are the record of that era, and
so does the note that one dashboard string has to suit a pnpm branch and a bun
one. Where a fix landed inside those blocks the old state is kept and the newer
move appended rather than overwritten.

* docs(twins): the hand-twin set says three and has been seven since 2026-08-14

`src/content/md/` holds seven hand-authored twins (/around, /bot, /coffee,
/lens, /security, /terminal, /whoareyou). CLAUDE.md's key-files row, its layout
table, `.assetsignore`'s comment and the generator's own header all said three,
and all four still called the directory `public/md/`.

The count could rot because the set is not a list anywhere in the code.
`buildTwins` looks for `<path>.md` per SURFACE, so a page joins by someone
dropping a file in: /terminal joined in #227 and /around, /coffee and /lens in
#354, and nothing announced either. What the row now records is the mechanism
rather than the membership, plus the fact that `checkTwinFacts()` pins only
three of the seven, so the other four can drift from their page silently.

Two neighbouring rows named prose that moved in the same #456 split:

- `public/writing/` is `src/content/writing/`, and its render code is in
  `src/worker/` rather than `_worker.js`.
- `public/index.md` is `src/content/index.md`. That path confusion has already
  cost something: gen-md-twins looked in `public/` after the move, silently
  stopped finding the hand twin, and overwrote it with a generated one 472
  bytes shorter. The row records that now.

`.assetsignore` also opened on "Files in `www/`" and called the deltas
`www/ad/`. Its two paragraphs about `www/scripts` and the old dictionary
location stay, since those are the record of why there is no entry at either
path.

---------

Co-authored-by: Aadharsh Pannirselvam <19518661+oddharsh@users.noreply.github.com>
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