Commit Graph

11 Commits

Author SHA1 Message Date
Edward Langley cc113c732f docs(context): answer the review of the repo-map split
- repo-stats reads the gotcha list from context/gotchas.md (the digest in
  repo-map.md no longer measures the doc-growth signal) and reports it as
  `docs.gotchas`; the test fixture and its derived expectations follow.
- AGENTS.md and the wave agent-rules protect gotchas.md and
  file-inventory.md like the repo map they were split from.
- doc-site.md's publication table lists the two new pages.
- file-inventory.md's Context docs rows describe the three files correctly.
- the code-review skill points at gotchas.md for the long form.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-09 21:42:24 -07:00
Edward Langley f0119a437d docs(context): condense repo-map to a digest; move gotchas and inventory to their own pages
repo-map.md was 173 KB (~43k tokens), too long to be part of every new
agent's context. The full gotcha text moves verbatim to context/gotchas.md
and the per-file inventory (with the old Wasm/CLI/Testing sections) to
context/file-inventory.md; the map keeps a one-or-two-line digest per
gotcha under the same numbers, so every `gotcha N` citation still resolves.

The doc-site manifest publishes the two new pages, and `gotchas` gets the
same gotcha-N anchors as `repo-map`.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-09 21:13:55 -07:00
Claude (agent-1xfb4) 676347674a chore(site): stop tracking generated site/ (improvise-1xfb.4)
site/ is web/assemble.sh's default output, and site/docs is
scripts/build-doc-site.mjs's. 27 generated pages under site/docs were
committed by accident and went stale. Nothing reads the tracked copy:
CI's build job assembles with `nix build .#site` into the store, the
harness assembles into harness/serve/, and build-web-release.sh
assembles into a temporary stage.

Remove site/ from the index, ignore /site/, and say so in
context/code/doc-site.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
2026-09-24 20:22:23 -07:00
Edward Langley 6034a216e1 docs(site): publish the two new context docs, and de-number two stale counts
The `build` job failed, correctly: `scripts/build-doc-site.mjs` requires every
`context/**/*.md` to be either in the SECTIONS manifest or a named omission
(improvise-res9's cure), and the two files this branch added were neither.
Both are now published, beside the TUI in each section.

While there: `context/code/doc-site.md` asserted 'the seven
`context/code/*.md` documents' when there were already twelve, and '501
entries' when the checker reports 801. Both were stale before this branch and
this branch makes them staler, so they are de-numbered rather than re-asserted
— a count in prose that nothing checks is a fact with no owner, and the shape
that stops drifting is not having one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
2026-09-22 08:26:12 -07:00
Edward Langley 787e354b9b doc-site: name context/code/pr-review.md as an omission
The `build` job fails on main and on every branch merged from it:

  doc-site: context/code/pr-review.md is neither published nor a named omission

NOT a merge loss -- scripts/build-doc-site.mjs in this branch is
byte-identical to main's, and neither lists the file. PR #96 added
context/code/pr-review.md without a manifest line, which is exactly the
gate improvise-res9 added: a doc added without a line publishes nothing
and says nothing, so the generator refuses.

Omitted rather than published, and the line is cloud-agents.md's rather
than tooling.md's. tooling.md explains checkers a reader of the code will
meet; pr-review.md is about THIS REPOSITORY'S PROCESS -- where a Codex
review appears, how to tell it from our own agents' comments, why absence
is not approval. Nobody visiting a website is about to read a PR thread.

Both homes updated, because an omission is restated beside the manifest
AND in doc-site.md section 2, and the prose there counted them ("Neither
is referenced...") so it went stale at three.

This strays into scripts/, which is Wave N's ground. Flagged in the
hand-back; it is a one-line data entry either way if the orchestrator
would rather publish it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
2026-09-21 11:12:00 -07:00
Edward Langley 1c3e1bd968 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
2026-09-20 21:12:46 -07:00
Edward Langley 702c1d43ff docs: correct the alt-text argument, which was right for the wrong reason
The commit that added `content: "\2630  " / ""` claimed Firefox ignores the
alt-text form and falls back to reading the content, making the change "an
improvement in two engines and a no-op in the third". The mechanism is wrong:
Firefox shipped the syntax in 133, and all three engines parse it -- measured
in Chrome 141, Firefox 142 and WebKit 26, each computing the declaration
intact. The conclusion survives; the reason given for it does not, and the
reason was the load-bearing half, because it was what made the change look
like a one-way door. It is one because every engine shipped to supports the
syntax, not because the unsupported path is harmless.

The unsupported path is NOT harmless, and that is the risk neither the change
nor its first review named: `/ alt` in an engine that does not know it is a
parse error for the whole `content` declaration, so the glyph does not lose
its alt text -- it VANISHES. A silent visual regression, the opposite of the
assumed failure mode. The glyph is now verified painted in all three engines
by pixel diff against `::before { content: none }`, text starting 20px further
right in each.

This is written to context/ rather than left in the commit message because
design-principles.md §6 says an argument must not live only in a commit
message -- a message is write-once, unauditable, and no check in this repo can
read one. The rule landed in the same merge this corrects under, which is a
reasonable first exercise of it.

It also records this file's fourth wrong-property instance, and the sharpest,
because it produced a confident FALSE POSITIVE: the first probe measured the
label's width with and without ::before and reported the glyph missing in all
three engines. The label is display:block, so its width comes from the
container and cannot see inline content. Right question, wrong property --
and this time the wrong instrument accused working code rather than clearing
broken code.

Gates on the merged tree: 1379 rust, 26 node --test, 16 playwright (3 skipped,
webkit OPFS), fmt clean, clippy -D warnings clean, all three
check-comment-runs modes, 3473 site links / 0 broken, 0 nested anchors, 189
bead ids all resolving.

improvise-djoo

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
2026-09-20 14:16:17 -07:00
Edward Langley c0b786cf0d fix(docs): the phone nav toggle needs a keyboard, not a mouse
A regression the previous commit introduced, found in review. The checkbox
driving the collapsed nav carried `hidden`, which is `display:none`, which
removes it from the tab order AND from the accessibility tree. A <label> is
not focusable on its own -- the checkbox it labels is what carries focus -- so
at <=900px the navigation could be clicked and reached no other way, and the
accessibility snapshot contained neither the checkbox nor any of the 25 links.

The toggle is now visually hidden but focusable at narrow widths, and
display:none at wide ones. Keeping display:none on the wide side matters:
merely un-hiding it puts focus on an invisible control whose label is
display:none, which is its own wart.

Measured at 390, 900, 901 and 1280, because the regression lives on one side
of a media query. At 390 and 900 the tab order is brand -> navtoggle, the
toggle is focusable, the a11y tree carries a checkbox node, and Space takes
visible nav links 1 -> 26. At 901 and 1280 the toggle is display:none, not
focusable, absent from the a11y tree, and tab runs straight through the 26
links -- desktop identical to before. 0 of 23 pages scroll horizontally at
320, 390 and 1280, so the absolutely-positioned 1px input introduces nothing.

WHAT IS WORTH MORE THAN THE FIX, and why doc-site.md gains a paragraph rather
than the code gaining a comment: my probe for the previous commit counted
VISIBLE nav links. That answers "does operating the toggle reveal the links" --
correct and complete for a mouse -- and is silent on whether any non-mouse
route exists. It could not have seen this, however sharp the visibility
instrument was. Last round's lesson was that a measurement which cannot
distinguish the two states is not a measurement; this round adds the half it
was missing: a measurement of the wrong PROPERTY is not a measurement either.
checkVisibility fixed the visibility instrument; it does not turn a visibility
count into an operability check. The instrument here is tab order, .focus()
and the accessibility snapshot.

Gates: 1378 rust, 26 node --test, 16 playwright (3 skipped, webkit OPFS), all
three check-comment-runs modes, 3462 site links / 0 broken, 0 nested anchors,
0 of 23 pages overflow at 320/390/1280.

improvise-djoo

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 14:09:37 -07:00
Edward Langley 41f694f535 fix(docs): collapse the phone nav, and stop the header comment lying
Round 2 of review. Three notes and the mobile-nav call.

THE NAV, and the correction that prompted it. I had called the 25-link
sidebar wall pre-existing and declined to fix it. That was wrong: the entire
CSS is new in this PR, so the wall ships for the first time here and it is
mine. Every phone page opened on ~1500px of nav before any content.

The mechanism is a checkbox and not <details>, and the reason is measured
rather than assumed. <details> was the obvious control, and it collapses
correctly on a phone -- but the sidebar layout has to force it OPEN, and a
closed <details> hides its content through ::details-content, which `display`
cannot override. Chromium showed the desktop sidebar collapsed to one link
with its toggle hidden: unusable. So the hiding is ours, in a media query,
where overriding it is just cascade.

Measured at both widths: 390px shows 1 link and the toggle, with the h1 at
189px instead of ~1500; 1280px shows all 26 links with no toggle and the h1
at 40px, unchanged; and the phone toggle really expands 1 -> 26 on click.
Note the first probe was wrong -- getBoundingClientRect reports a height for
content-visibility-hidden children, so it read 26 visible in both states and
would have passed the broken version. checkVisibility() is what separates them.

The same argument applies to the table of contents, which is equally new
here: repo-map opened on 1397px of TOC on a phone. Capped at 45vh so it
scrolls; 1397 -> 924, desktop identical at 652.

THE HEADER COMMENT. The generator stopped running standalone when the link
check began resolving ../index.html and ../app/ on disk, and its header still
advertised a default OUT_DIR that now exits 1. That is gotcha 48's third
paragraph exactly -- a fact stated in two places and updated in one, with the
copy that was updated being doc-site.md and the copy someone about to run the
script actually reads being the header. It now says so, keeps the strictness
(the hole it replaced was counting those links as checked while skipping
them), and records the other thing the reorder changed: the DIRECTION of a
failure, from an obviously-broken tree with no app/ to a complete-looking one
with a partial docs/.

And one documented subset: a \| inside a code span in a table cell keeps its
backslash where GFM would unescape it. Splitting is suppressed inside code
spans, so the escape is never needed there and never consumed. No corpus
instance; recorded as a decision rather than left to be rediscovered.

Gates: 1378 rust, 26 node --test, 16 playwright (3 skipped, webkit OPFS), all
three check-comment-runs modes, 3460 site links / 0 broken, 0 nested anchors,
0 of 23 pages overflow at 390 and 1280.

improvise-djoo

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 14:04:19 -07:00
Edward Langley 1a0f04856e fix(docs): the review's four rendering defects, and the check that could see one
Review of PR #62 found four live defects and four smaller ones. The most
valuable is the first, because of what it says about the rest.

A TOC entry wrapping a heading whose rendered text already contained a link
emitted <a href="#anchor"><a href="github...">...</a></a>. The parser closes
the outer anchor early, so the entry became an empty unclickable stub and
clicking "src/draw.rs" in the TOC navigated to GitHub. 19 occurrences over 10
pages. Both hrefs resolved perfectly, so the link check, the whole-site link
check and a screenshot pass all missed it -- it answers "does every href
resolve" and this is not that question. So the fix is two things: the TOC keeps
a tag-free variant of each heading, AND a new check asserts no <a> appears
inside another <a>. Proved by reverting the first half: 66 nested anchors
reported, assembly exits 1. Clicking that TOC entry now lands on #src-draw-rs.

esc() escaped & < > but not the quote it was interpolated into title="..."
with. Five shipped bead titles contain one, and Chromium parsed the resulting
tag as having three junk attributes. Now escapes " and ' as well. Today's
corpus being friendly is not a reason to interpolate tracker text unescaped.

Seven pages overflowed at 390px by up to 92%. The reviewer attributed it to the
TOC; the TOC was real but not sufficient, and the first fix measured clean by
getBoundingClientRect while the page still overflowed, because the heading text
overflows its box without widening it. Only per-element scrollWidth names the
culprit: hundreds of snake_case test names as headings. overflow-wrap on the
headings and on .toc a, plus dropping display:table from .tickets, which had
been cancelling the mobile overflow-x guard. 0 of 23 pages overflow at 390 and
1280 now, measured.

table() split rows on every pipe before inline parsing, so `|Time Period|` --
this repo's own quoting syntax, in a corpus whose subject is that syntax -- was
shredded into extra columns. A row that still parses, so no column count could
see it. Now walked, respecting code spans and \|. Proved with a fixture: 9 tds
with the fix, 12 with the old splitter reinstated.

The four smaller ones: the link check counted 48 links it never verified behind
a comment promising they were "checked below" when there was no below -- those
are the links leaving the docs tree, and they are now resolved against the
filesystem, which is why assemble.sh runs the generator LAST, once its siblings
exist. The gotcha jump bar's hardcoded 48 is derived from the emitted anchors,
because it failed closed on deletion and silently on addition, and the list only
grows. context/code/doc-site.md cited improvise-xxxx and so broke the audit the
surrounding paragraph describes; the repo-wide audit is now 189 ids, all
resolving. And the Conventions paragraph's own example path now goes through
inline(), so the sentence demonstrates the linking instead of asserting it.

The ticket index keeps all 180 rows, open bugs and titles included, by the
user's ruling: the data is already public in .beads/issues.jsonl, the page only
makes it legible, and filtering it while the lede links the raw JSONL would be
theatre.

Gates: 1378 rust tests, 26 node --test, 16 playwright (3 skipped, webkit OPFS),
all three check-comment-runs modes, 3460 site links / 0 broken, 0 nested
anchors, 189 bead ids all resolving.

improvise-djoo

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-20 13:56:56 -07:00
Edward Langley f1011d7f6f 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>
2026-09-20 13:37:20 -07:00