feat(docs): publish the context/ prose as a site folded into Pages
The landing page is the site root and the app is /app/; the prose now sits at /docs/, assembled by the same web/assemble.sh the Pages job and the browser harness both run, so what ships and what is tested are one tree. Published: design-notes, design-principles, repo-map, the three ADRs, the seven context/code/ arguments, and the 501 test-evidence entries as their own clearly-labelled section. Not published: context/plan.md, which is an agent prompt about a launch rather than a description of the program, and context/cloud-agents.md, which is addressed to nobody who visits a website. Neither is referenced by a published document outside a code fence, so leaving them out dangles nothing. The reasoning is context/code/doc-site.md, which is itself published. Two things the corpus needed that a generator would not have given it. The ~190 improvise-xxxx citations resolve to a generated ticket index built from the already-public .beads/issues.jsonl rather than to an invented URL that would 404; an id the export does not yet carry stays plain text. And a code span that is wholly a repo path links to GitHub at the assembled ref, but only when the file exists on disk, so a stale citation degrades to text. The generator link-checks its own output and exits non-zero on a break, and assemble.sh runs under set -eu, so a dead cross-reference fails the assembly rather than shipping. It earned that on its first two runs: it caught a fence-close regex comparing a three-character marker to a one-character string, which swallowed every document from its first code block onward, and a list parser that ended the gotcha list at the first blank line, emitting 1 of the 48 anchors the jump strip links to. Proved by injection afterwards -- renaming the anchors reports 48 missing anchors, pointing the ticket links at an ungenerated page reports 763 missing pages. No framework, no node_modules for the site, no build step beyond one zero-dependency node script, matching the landing page's posture. improvise-djoo Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
# The documentation site — the arguments
|
||||
|
||||
`scripts/build-doc-site.mjs` turns the prose in `context/` into the static site
|
||||
served at `/docs/` on GitHub Pages. This is the reasoning behind what it
|
||||
publishes, what it deliberately does not, and why it is a hundred-odd lines of
|
||||
node rather than a static site generator (improvise-djoo).
|
||||
|
||||
---
|
||||
|
||||
## 1. What is published, and what is not
|
||||
|
||||
The manifest is the `SECTIONS` array at the top of the script, and **a document
|
||||
is published only by being named there**. That is the point: a glob over
|
||||
`context/**` would publish the next working file somebody drops in, and the
|
||||
decision about what a stranger should read is not one to make by accident.
|
||||
|
||||
Published, in four sections:
|
||||
|
||||
| Section | Contents | Why |
|
||||
|---|---|---|
|
||||
| Start here | `docs/design-notes.md`, `context/design-principles.md`, `context/repo-map.md` | What the project is for, the doctrine, and the single best explanation of how it is built. |
|
||||
| Decision records | `context/adr/*.md` | An ADR is written for an outside reader by construction — it states the options that were rejected, which is the part that is worth reading and the part that is nowhere else. |
|
||||
| The arguments, by area | `context/code/*.md` | This is the reasoning that used to live in code comments and was moved out under the two-line cap (design-principles §6). It is the most valuable prose in the repository and it is addressed to exactly the reader a documentation site has. |
|
||||
| Test evidence | `context/test-evidence/*.md` | 501 entries, one per test whose narrative is longer than its code. |
|
||||
|
||||
Not published:
|
||||
|
||||
- **`context/plan.md`** is a *prompt*: it opens "You are working in the
|
||||
`improvise` repository… Your job is to prepare this repository for a public
|
||||
Show HN launch", and it instructs a reader to audit a `context/SPEC.md` that
|
||||
no longer exists. It is an instruction to an agent about a launch, not a
|
||||
description of the program, and publishing a launch plan on the site the
|
||||
launch points at inverts the two.
|
||||
- **`context/cloud-agents.md`** is operational notes for agents running in a
|
||||
cloud session — tracker hydration, `bd` pitfalls, what the sandbox blocks.
|
||||
It is true and it is useful and it is addressed to nobody who visits a
|
||||
website.
|
||||
|
||||
Neither is referenced by any published document except as a row in
|
||||
`repo-map.md`'s file inventory, which is inside a code fence, so leaving them
|
||||
out creates no dangling link. That was checked rather than assumed.
|
||||
|
||||
**Test evidence is the one judgement call worth stating.** The argument for
|
||||
leaving it out is that 501 entries about individual tests are noise to a reader
|
||||
who wanted to know what a pivot table is. The argument for keeping it is
|
||||
stronger: every one of the seven `context/code/*.md` documents points at its
|
||||
evidence file by name, so omitting it would break the published prose in
|
||||
exactly the way this site is otherwise careful not to; and the evidence file is
|
||||
the most distinctive artefact the repository has — a written record of the
|
||||
injected fault behind each load-bearing test is not a thing most projects can
|
||||
show. It is therefore published as its **own section**, last, described as a
|
||||
contributor reference rather than a tour, so nobody wanders into it expecting
|
||||
a narrative.
|
||||
|
||||
## 2. Ticket ids resolve to a page, never to a URL that does not exist
|
||||
|
||||
The prose cites `improvise-xxxx` roughly 190 times. On a public site those are
|
||||
meaningless strings, and the obvious repair — link them to an issue tracker —
|
||||
is unavailable: beads keeps its issues in a Dolt database on a git ref, and
|
||||
there is no public issue URL to point at. Inventing one would produce 190
|
||||
links that 404, which is worse than plain text because a link is a promise.
|
||||
|
||||
So the ids resolve **inward**. `tickets.html` is generated from
|
||||
`.beads/issues.jsonl` — already committed, already public — and carries id,
|
||||
status and title, nothing else; descriptions are internal working notes and are
|
||||
not the site's business. Each citation in the prose becomes a link to that
|
||||
page's anchor, with the title in a `title=` attribute so a hover answers the
|
||||
question without a navigation.
|
||||
|
||||
**`issues.jsonl` is a passive export and lags the database**, which is not a
|
||||
defect to work around but a fact to render honestly: an id the export does not
|
||||
carry is emitted as plain text with a dotted underline and no link, and the
|
||||
ticket page lists it as "not exported". The site therefore never links to an
|
||||
anchor it did not generate, whatever state the export is in. At the time of
|
||||
writing 8 of 178 linkified ids are in that state. A generator that instead
|
||||
linked every id it saw would produce a page that is right today and broken the
|
||||
first time somebody cites a fresh ticket.
|
||||
|
||||
## 3. Source paths link only where the file exists
|
||||
|
||||
A code span whose entire content is a repo path becomes a link — to the
|
||||
generated page when the path is one of the published documents, to GitHub
|
||||
otherwise. Two constraints make that safe. The whole span must be the path, so
|
||||
a sentence that merely mentions a directory is untouched; and **the path must
|
||||
exist on disk at generation time**, so a citation that has gone stale degrades
|
||||
to plain text rather than to a 404. The GitHub ref is the cache-bust token
|
||||
`web/assemble.sh` already receives, used only when it is a 40-hex sha and
|
||||
falling back to `main` otherwise — a local `dev` build must not emit
|
||||
`blob/dev/…`.
|
||||
|
||||
## 4. The link check is the test, and it is part of the build
|
||||
|
||||
The generator collects every anchor it emits and every internal href it writes,
|
||||
resolves them against each other, and **exits non-zero** if any href names a
|
||||
page it did not generate or a fragment that page does not carry.
|
||||
`web/assemble.sh` runs under `set -eu`, so a broken cross-reference fails the
|
||||
assembly rather than shipping.
|
||||
|
||||
This is not ceremony. It caught two real defects on its first two runs: a
|
||||
fence-close regex that compared a three-character marker against a
|
||||
one-character string, which swallowed every document from its first code block
|
||||
onward; and a list parser that ended the gotcha list at the first blank line,
|
||||
so 47 of the 48 gotcha anchors were never emitted while the "jump to gotcha N"
|
||||
strip linked to all of them. Both were invisible in a rendered page that
|
||||
otherwise looked fine.
|
||||
|
||||
It is proved by injection rather than asserted: renaming the emitted anchor ids
|
||||
makes it report 48 missing anchors, and pointing the ticket links at a page
|
||||
name that is never generated makes it report 763 missing pages — two different
|
||||
diagnoses, because "the page is absent" and "the page is there but the anchor
|
||||
is not" call for different repairs.
|
||||
|
||||
What it does **not** cover is links that leave the `docs/` subtree — the ones
|
||||
to `../index.html` and `../app/`. Those are checked against the assembled
|
||||
`site/` tree by hand at the moment, and the honest statement is that a rename
|
||||
of the app directory would not be caught here.
|
||||
|
||||
## 5. Why a node script and not a generator
|
||||
|
||||
The landing page is hand-written HTML with no build step and one pinned CDN
|
||||
script; the same posture applies here. A static site generator would add a
|
||||
dependency tree, a lockfile and a toolchain to a repository whose site is
|
||||
currently four files, in exchange for features this corpus does not use — the
|
||||
docs contain no front matter, no shortcodes and, as it happens, **not a single
|
||||
markdown link**. Every cross-reference in them is prose naming a path or an
|
||||
id, which is precisely why §2 and §3 are the interesting parts and Markdown
|
||||
rendering is the boring part.
|
||||
|
||||
So the renderer is a deliberately small subset covering exactly what the corpus
|
||||
contains: ATX headings, fenced code, pipe tables, blockquotes, ordered and
|
||||
unordered lists with indented continuations, thematic breaks, and inline code,
|
||||
strong, emphasis and links. Anything it does not recognise becomes paragraph
|
||||
text, so **no input line is ever dropped** — the failure mode is an ugly
|
||||
paragraph, never a missing one.
|
||||
|
||||
Two details in it are load-bearing and easy to undo. **Code spans are replaced
|
||||
by placeholders before emphasis is matched**, because this corpus routinely
|
||||
opens `**` on one side of a code span and closes it on the other; matching
|
||||
emphasis per segment silently drops that bold, which is a fidelity loss with
|
||||
no visible symptom. And **strong is matched before emphasis, with a body that
|
||||
may contain a single `*` pair**, because `[^*]+` cannot match
|
||||
`**a *b* c**` and the leftover `*` then pairs across the bold — which is how a
|
||||
single sentence in gotcha 33 came out as three italic paragraphs.
|
||||
|
||||
## 6. Navigation
|
||||
|
||||
`repo-map.md` is one long document whose gotcha list is numbered 1 to 48, and
|
||||
splitting it per gotcha was rejected: the gotchas cross-reference each other
|
||||
constantly ("see gotcha 35", "for the reason gotcha 39 watches…"), and a reader
|
||||
following those wants a scroll, not 48 page loads. It stays one page, with an
|
||||
id on every list item and a compact strip of 48 numbered links under the
|
||||
heading. The ids come from the list item's **own number**, not from a counter,
|
||||
so an inserted gotcha renumbers the anchors exactly as it renumbers the prose
|
||||
that cites them.
|
||||
Reference in New Issue
Block a user