docs/content-model.md opens with "Engine specification". It is also where the rule lives that a leading underscore makes a file unaddressable — and the human who owns this site did not know that rule, because nothing in this repository is addressed to an author. Twelve documents named docs/ while being exclusively about building the parser is a signpost pointing at the wrong room. Naming the directory for its audience makes the gap visible instead of hiding it. docs/ is now reserved and deliberately absent: an empty docs/ is an honest statement that end-user documentation does not exist, where docs/ full of parser specs was a claim that it did. HARNESS.md stays at the root. Root holds the three entry points — README.md for a human, CLAUDE.md for an agent, HARNESS.md for whoever maintains the machine — and harness/README.md is the map of the directory, so moving the guide inside would have collided with it for nothing. Mechanical and wide: 100 path references across 24 files. Every verify.sh gate that names a doc by path, the directory lists the dangling-path and ADR-number gates scan, surface.sh's output target, the Makefile, CLAUDE.md's read order, the skill, four commands, and two Go package comments. A first pass with a shell loop silently edited only four files and the rest still said docs/; the fix was to write the file list out and check the remaining count was zero rather than trust the loop's exit status. No rule, threshold, gate or obligation moved — this is a rename, and the gates demonstrated it twice: they stayed green on the new paths, and the ADR-number gate caught ADR-0082 before the entry existed. Deferred, both on the human's call: the end-user documentation site itself, which wants its own decision about where it lives and whether its claims are gated; and moving examples/ under docs/, since demo-site is a live site root that verify.sh, the coverage test and make demo all point at, and moving it would couple a rename to a design nobody has made. 31 files, +146/-106. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
4.2 KiB
Roadmap
Arcs, not sprints. An arc ends when its earn-triggers have fired, not on a date. Nothing from a later arc gets built early "since we are in the file anyway."
Arc 1 — the spine
- HTTP server, one binary, no cache.
- Bundle with language variants and fallback (ADR-0004).
- Permalinks per ADR-0008, including aliases and permanent redirects.
- Language routing per ADR-0009: English at root,
/bn/prefix,/en/…redirects to root. - Path traversal guard before anything faces the internet. The target is a self-hosted container (ADR-0010), so "not yet internet-facing" expires at the first deploy.
Done when: a URL reliably maps to a bundle, in two languages, with URLs you are willing to promise are permanent.
Arc 2 — earn the primitives at first real use
In this order, each at its trigger, never before:
- Query — at the first collection page (section index, latest posts).
- Stage pipeline — when the transform counter is due.
- View layer — per-bundle override; the data contract is already frozen by then (ADR-0023).
Done when: the View layer lands. The core stops growing here. Everything after is
composition; core growth past this point means the plan was wrong. Record the core number
verify.sh prints on freeze day in the retro line — from then on it is the figure that must not move.
Arc 3 — the networked layer
- Feeds, JSON-LD, sitemaps, microformats — all Query + Stage + Effect.
- Interaction primitive: comments, with the ADR-0003 trust boundary.
- A thin internal ContentAPI, introduced with the comment write path — not before.
- Access control as exactly one permission check, in one place.
Done when: an external party can react to a page without touching the content graph.
Arc 4 — pure composition
IndieWeb adapters (Micropub first — it buys an existing editor ecosystem for free), then ActivityPub only if still wanted, then delight: backlinks, transclusion, page-as-program, revision-as-craft. Optional admin panel here, as a second client over the ContentAPI.
Cross-cutting — when actually needed
Cache (when requests feel slow) — in-process first; Redis only once a second process must share it, and never as the only copy of anything (ADR-0010). Containerisation: one Dockerfile, one binary, no orchestration until a second service exists, and the site root is a mounted volume rather than baked in (ADR-0022). Updating that volume is the host's job; the engine only notices. Admin/editor deferred — git already works, and Micropub may remove the need entirely.
Admin/editor position
Reframed as a second client over the thin internal ContentAPI. If built: a Markdown editor with live preview, not WYSIWYG. Better: a Micropub endpoint, which buys an ecosystem of existing editor apps and requires no UI. Likely optional forever, given git.
Closing an arc
Earn-triggers fired, /invariants run and clean, latent list triaged (fixed, scheduled, or accepted with
a reason), retro line written in state.md.
Governors
- No abstraction before its second concrete use; the counters in
state.mddecide, not intuition. - Own the data; borrow only infrastructure you can walk away from. Test: if this vanishes, do I lose data or just convenience?
- Every feature is a leaf (
architecture.mdinvariant 7). Trunks wait for a human decision. - Typography split: smart quotes, dashes, ellipses, widow prevention = server-side markup-aware Stage after Markdown parse, skipping code spans. Glyph shaping = the browser's job. Ligatures, text-balance, variable fonts = CSS.
Bengali and context specifics
Unicode slugs, NFC-normalised at every identifier boundary with hand overrides for terms and sections
(ADR-0015). Bengali numerals and relative dates are a cheap Stage (২ ঘণ্টা আগে) applied to chrome —
UI strings and server-pulled dynamic fragments — never to authored body text, which stays as written.
Bengali tokenisation for search is the genuinely novel problem here — solve it well
and deliberately, not as a side effect of adding search. The low-bandwidth, low-carbon ethos is
coherent with Gemini output, PWA offline, and no-JS defaults; let that coherence break ties when two
designs are otherwise equal.