feat(doc-site): the published set is derived, and the generator must run last (improvise-res9)

The manifest only ever grows, so a doc added without a line in it published
nothing and said nothing: build green, link check green, doc simply absent.
The generator now walks context/**/*.md plus docs/design-notes.md, subtracts the
manifest, and fails unless the remainder is exactly the OMITTED map -- checked
in BOTH directions, because an entry naming a file nobody can find asserts a
reason for nothing.

Filed as latent; it was live. context/code/kb-tool.md had been unpublished,
unargued and unnoticed since it was added, while doc-site.md's own table says
context/code/*.md is published. It is published now, with context/code/tooling.md
and context/test-evidence/tooling.md: 23 pages to 26.

Second gate, same file: the generator reads web/assemble.sh and refuses unless
its own invocation is that script's last command. Its outward-link check resolves
../index.html and ../app/ against the filesystem, so a line appended below it
would make that check pass against a half-built tree.

Both run at module scope, before any page is generated, so a manifest error is
answered before the 48 outward-link failures a bare run reports -- a missing doc
is actionable wherever you ran the generator, and the two are unrelated
diagnoses.

Three faults injected and observed; evidence in context/test-evidence/tooling.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
This commit is contained in:
Edward Langley
2026-09-20 21:12:46 -07:00
parent 8af37d44a1
commit 1c3e1bd968
3 changed files with 98 additions and 1 deletions
+22
View File
@@ -40,6 +40,21 @@ 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.
**And "not published" is now derived rather than assumed** (improvise-res9).
The generator walks `context/**/*.md` plus `docs/design-notes.md`, subtracts
the manifest, and fails unless the remainder is exactly the `OMITTED` map in
the script — which carries the reason beside the name, so this section and the
code cannot drift into disagreeing. An allowlist and not a count, because an
omission is a decision that should have to be argued in code; and the map is
checked in *both* directions, because an entry naming a file nobody can find
asserts a reason for nothing, which is the failure a dead ticket id is.
The check was latent when it was filed and **live by the time it was written**:
`context/code/kb-tool.md` had been added after this table was drafted and was
publishing nothing, with no argument for its absence anywhere. The table above
says `context/code/*.md` is published; the manifest disagreed and nothing could
see it. That is the whole failure mode in one file.
**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
@@ -136,6 +151,13 @@ filesystem instead. That is the whole reason `assemble.sh` runs the generator
version skipped those 48 links while still counting them as checked, behind a
comment promising they were "checked below" when there was no below.
That ordering is itself checked now (improvise-res9). The generator reads
`web/assemble.sh` and refuses to run unless its own invocation is that script's
last command. PR #63 appended a loop and it happened to land above the
generator; one landing below would have made the outward-link check pass
against a half-built tree, silently and in the one direction this section says
must not go unnoticed.
**And the link check is not sufficient, which is the more important lesson.**
It answers one question — does every href resolve — and a defect outside that
question is invisible to it however carefully it is run. The worked example is