Done (Phases 0-3) 2026-07-23. Shipped as four gated commits: docs fold-in (434ddcd),src/consolidation (5f6703e),content/grouping (d43c8a1), and theview/layer (33934e7). Phase 4 (standards/relocation) was deliberately NOT done — it stays root-pinned as the PANTRY package export. Final gate: tsc, 110 unit, 89 e2e, export + verify:export. The root now separates code (src/), content (content/), view (view/), docs (docs/), and config.
Proposed 2026-07-23. The repo root carries 23 tracked top-level dirs plus 8 loose source.tsfiles sitting next to config, with tests scattered across three conventions and long-form docs split between the root anddocs/. Nothing is broken, but the root no longer reads as a map. This plan tidies it in risk-ordered phases, each behind the full gate (unit + desk e2e + export + verify:export). Phases are independent — green-light any subset.
The mess (what an opener sees today)
- Loose source at root (8 files):
server.tsconfig.tscontent.tsrender.tsseo.ts
llms.ts plans.ts interleaved with package.json / tsconfig.json / playwright.config.ts. No src/ — code and config share the same ls output.
- Test conventions split three ways: colocated at root (
content.test.ts,seo.test.ts),
colocated in ai/ and routes/, and separate in e2e/.
- Docs split:
PHILOSOPHY.mdHACKING.mdCONTENT-BACKLOG.mdlive at root while
docs/ holds the rest (architecture/, grain/, batch/).
- Content mixed with code at the top level:
notes/events/tours/standards/data/
media/ are authored content, sitting as siblings of ai/ routes/ components/ render.ts.
- 23 top-level dirs — high cognitive load before you find the file you want.
Landmines (why this is not a blind git mv)
standards/is a published package export.package.jsonmaps `"./standards/*":
"./standards/"` so PANTRY resolves those files as a package subpath*, and content.ts:166 reads them via join(import.meta.dir, "standards"). Moving standards/ OR moving content.ts away from it breaks /standards rendering and the PANTRY resolve. Treat standards/ as pinned; relocating it is its own phase with the export map + constant updated together.
content.tspath constants reference content dirs as strings —notes/(7 refs),
standards/ (2), media/ (1), data/ (1). Every content-dir move updates these.
- Entry paths in
package.json:bun server.ts,bun --hot server.ts,bun tools/export.ts.
Moving server.ts updates 3 scripts + tsconfig + playwright.config webServer command.
import.meta.diris location-sensitive. Any file that resolves a sibling dir by
import.meta.dir breaks when the file moves; audit each before moving (content.ts is the known one).
dist/export + GitHub Pages —tools/export.tswalks the routes; confirm every phase with
bun run export && bun run verify:export (the dead-link walk) before pushing.
audit/is generated (bun run auditwritesaudit/report.{json,md}) but tracked as an
intentional build baseline. Leave it tracked; do not gitignore.
Blast radius (measured)
- Only 4 files import the root source modules (
server.ts,render.ts, and the two colocated
tests). Moving them into src/ is a ~4-site import rewrite plus the entry paths.
- Content-dir string coupling is ~11 refs, almost all in
content.ts. - So the reorg is cheap — the cost is discipline (one phase at a time, gate each), not volume.
Target layout
tjakoen.github.io/
├─ src/ app + composition code
│ ├─ server.ts (entry — package.json points here)
│ ├─ config.ts content.ts render.ts seo.ts llms.ts plans.ts
│ ├─ ai/ routes/
│ └─ *.test.ts (colocated, one convention)
├─ view/ GRAIN view layer (components/ + pages/ — Phase 3, optional)
├─ content/ authored words
│ ├─ notes/ events/ tours/ data/ media/
│ └─ standards/ (Phase 4 only — PANTRY-coupled; may stay at root)
├─ docs/ ALL long-form docs (PHILOSOPHY, HACKING, CONTENT-BACKLOG folded in)
├─ tools/ scripts/ e2e/ vendor/ audit/
├─ config: package.json tsconfig.json playwright.config.ts bun.lock
└─ root-pinned: README NOTICE LICENSE CLAUDE.md AGENTS.mdstandards/ and components/+pages/ are the two expensive moves; both are gated behind their own phases and can be skipped without blocking the cheap wins.
Phases (risk-ordered — green-light any subset)
Phase 0 — docs tidy (zero risk, no code)
git mv PHILOSOPHY.md HACKING.md CONTENT-BACKLOG.md docs/(ordocs/architecture/for the
latter two). Update the 3–4 relative links in CLAUDE.md + README.md that point at them.
- Gate:
bun run verify:export(dead-link walk catches any missed doc link). No tests needed.
Phase 1 — src/ consolidation (low risk)
git mvthe 8 root.ts+ai/+routes/intosrc/; keep tests colocated.- Rewrite ~4 relative imports; update
package.jsonscripts (server.ts→src/server.ts,
keep tools/export.ts path or move under src/ too), tsconfig.json, playwright.config.ts webServer command.
- Audit
import.meta.dirusers —content.tsresolvesstandards/; ifcontent.tsmoves to
src/, its standards join must become join(import.meta.dir, "../standards") (or standards moves in Phase 4). This is the one real gotcha in Phase 1.
- Gate: full — unit + desk e2e + export + verify:export.
Phase 2 — content/ consolidation (low–medium risk)
git mv notes events tours data media content/.- Update the ~11 string path constants in
content.ts(and any tool that walks these). - Leave
standards/out — Phase 4. - Gate: full. Watch the export route walk (content dirs feed
/notesetc.).
Phase 3 — view/ layer (optional, medium risk)
git mv components pages view/. Heaviest render-path coupling; only worth it if the top-level
count still grates after 0–2. Update render/template path constants + export walker.
- Gate: full + a browser drive of
/,/notes,/grain/docs(render paths are the risk).
Phase 4 — standards/ relocation (deferred / maybe never)
- Only if
content/feels incomplete without it. Movestandards/undercontent/, and in the
SAME commit update package.json export ("./content/standards/*") and the content.ts join. Verify PANTRY still resolves (this repo is a PANTRY package source) — the reason this is last and may be judged not worth the coupling churn.
- Gate: full + confirm
/standardsrenders + PANTRY resolve unbroken.
Out of scope
- No content edits, no route/URL changes (paths on disk move; published URLs stay).
- No gitignore change for
audit/(intentional baseline). - No grain/batch/mill changes — this is the app repo only.
Recommended cut
Phases 0 + 1 + 2 buy most of the tidiness for little risk and leave the two coupled moves (view/, standards/) as opt-in. Do them as three separate commits, gate between each.