Rewrite the homepage, and wire the legibility gate that holds it (#219) - #220
Merged
Conversation
"Also we repeat ourselves a lot the pages should project into markdown we could read those." Two asks, and the second is why the first matters. THE PROJECTION. HTML is a bad medium for reading your own writing. A diff on index.html is markup with sentences buried in it, and the rendered page is laid out to persuade rather than to be legible. There was no way to read what this site SAYS without a browser or a grep. Now every page has a .md at the same route — /index.md, /map.md, /desk.md, /blog/*.md — listed in llms.txt and covered by the signed whole-site manifest. One rule, two cases: a page authored in markdown serves its SOURCE (projecting the built HTML back would create a second copy of every post to keep in sync, which is the exact failure this change is about); every other page is projected from its BUILT html, so the generated regions are in it. The projection is also committed under content/pages/ and drift-checked, like every other projection here. That is the half that pays off in review: a copy change shows up as a prose diff next to the markup one. The stamp region is dropped from the projection. It is rewritten on every build, so carrying it into a committed file would make that file stale the moment anything else merged — and dropping it makes the projection a pure function of the source, which is what lets --check mean anything. THE MEASUREMENT. The obvious check — find sentences that appear twice — finds almost nothing: 2% of sentences repeat verbatim, and most of those are nav chrome. That number is reassuring and wrong. This site repeats itself by RESTATEMENT. The homepage says, in three places: "Every claim on this page is graded against the running code." "Every claim here is graded against the running code." "A solo project — every claim on this page graded against the code that backs it." Three phrasings, no two identical. So the comparison is over content words: strip stopwords and short words, take the set, score pairs by Jaccard overlap. It reads the markdown, not the HTML, so it sees the words a reader sees and nothing about markup. 19 restatement pairs — 8 within a page, 11 across pages. Ratcheted, not a cliff. That count is real and large; failing the build on it today would block every unrelated change, and a gate nobody can go green against gets switched off. The committed baseline is a ceiling: a change may not add repetition, and --update lowers it when a change removes some. A missing baseline is refused rather than silently written. That happened once here: `npm run check` ran before the projection existed, saw only the five blog posts, and wrote a ceiling of 4 that the next run then "regressed" against. A ratchet that picks its own starting point from whatever it saw first is worse than none. Both gates are mutation-tested in their lane before either is trusted — edit the projection, prove the drift check goes red; lower the ceiling, prove the ratchet goes red. Rule 3: a gate's own claim about itself is not evidence. NOT IN SCOPE: cutting the repetition. This lands the instrument and records the number. What to cut is editorial and should be decided while looking at the list, not bundled into the change that produced it. The projection also surfaced something worth fixing separately: the room/door/guest definitions are marked up as styled spans rather than a description list, which is why they need a special case here to avoid reading as "rooma named bundle of doors". Closes #217
cell(): a pipe ends a GFM table cell, so it has to be escaped — but the text reaching cell() has already been through esc(), which escaped its backslashes. `.replace(/\|/g, "\\|")` was therefore correct only by accident: an escaper that handles `|` but not `\` is wrong on its own terms, and re-running a backslash pass would double what esc() emitted. It now walks the string once, consuming a backslash together with whatever it escapes, so a bare pipe from a code span or a link destination — neither of which goes through esc() — is escaped exactly once and an already-escaped one is left alone. check-repetition: `<!--[\s\S]*?-->` left an UNTERMINATED comment in place, and its body was then counted as prose. `(?:-->|$)` is both the complete pattern and the more correct one — swallowing to end-of-file is what an unterminated comment actually means. Nothing here renders as HTML, so neither was an injection. Both were still real defects in code whose whole job is to be exact about text.
New copy, replacing the page wholesale. Hero (the problem, the move, one allow/deny transcript) → What's here (three repos, a line each) → Status, graded (three grades, the six claims folded into them) → If you want to go deeper → Who. focus-budget, index.html: 1516w → 460w · grade 13.2 → 11.4 all three FOCUS_SECTION_TOO_LONG warnings: cleared remaining "jargon": delanghe, brooklyn, polyform — proper nouns WHAT MOVED, AND WHY IT MOVED RATHER THAN BEING SUPPRESSED. gen-seams spliced the capability-seam grid into a homepage card that no longer exists. The seams move to /map — the page about what exists, and where the six narrowest, most concrete entries belong anyway. gen-registry spliced a three-tile count summary into #graph. /map already renders the same numbers as tiles, with the whole registry underneath, so the second renderer goes rather than moving: two renderers for one dataset is the drift this project argues against. The script keeps its data duties — refresh the seed from each package's bounded.*, and validate it — and sync-registry.yml now checks the seed and the page that renders it. check-outline asserted "at least one .seams__head on index.html". That is an assertion about the homepage's SHAPE, not about the component: moving a card to the page it belongs on failed the gate with nothing regressed. Presence is now global across surfaces, and map.html joins the surfaces checked — so /map's outline is under contract for the first time. content/marketing.feature pinned micro-copy that is gone. Scenarios are deleted alongside the copy they described; leaving them to fail, or loosening them to pass, would both be worse. content/grounding.json gains two phrasings. The two ENFORCED claims kept their meaning and changed their wording — "fail CI on drift" became "fail the build on drift" — so the grounded phrase follows the claim. Same evidence, restated, not a new assertion. DROPPED ON PURPOSE: the room/door/guest hero figure; the bet and model sections; the in-page self-check demo (~100 lines of JS the page itself labelled "a demo, not proof"); the two provenance cards; and everything the homepage restated about /desk, /contracts, /conformance and /ledger — those stay in the site nav on every page, the homepage just stops saying them again. All six claims in integrity/claims/claims.jsonld survive, folded under their grade. claim-provenance and claim-broker are no longer single-sourced tokens: the new wording does not carry their grounded phrases, so they are inline body prose like the two aspirational claims already were. The single-source floor still holds at 25 ≥ 22. One word is not Bobby's. "Every deploy is keyless-signed" reads to the upstream honesty lane as an unscoped coverage claim — it did reject the same shape earlier tonight — so it says "Each deploy". Flagged for review. The hero transcript is three lines per case, not the one-liner it was drafted as. `.code` wraps rather than scrolls, deliberately, so a <pre>'s min-content cannot inflate the page on a phone; a line that does not fit therefore breaks mid-clause. Measured at 390/768/1280: no line wraps now. Closes #219
… claude/homepage-rewrite
Bobby's instrument, landed. Deterministic half runs in the hermetic
phase directly after gen-markdown.mjs, because the projection is what it
reads: the words a stranger gets, with the markup gone.
the rewrite: PASS — hero 91w · 4 sections · 3 acronyms
the live page: FAIL (6) — 20× "door", 11× "room", 10× "guest",
"Flagship", "At scale", 21 acronyms against a budget of 10
The judged half (scripts/legibility/coldread.feature) is NOT wired. Its
own header says why, and the pipeline entry repeats it: a flaky proxy in
the inner loop rots trust in the gate around it. It runs at deploy, and a
green judge run is not the human test.
TWO CHANGES TO THE INSTRUMENT AS DELIVERED, both flagged rather than
buried.
1. The sentence rule split on terminal punctuation only, so it ran
straight through blank lines: six seam rows with no full stop between
them measured as one 59-word sentence. That is a false positive about
the instrument, not a finding about the page, and it would fire on any
page with a short unpunctuated list. Blank lines now split first. Not
an extension — a sentence does not span a paragraph break, so this is
the sentence rule measuring sentences.
2. The hero budget reads "words before the first ---", and the markdown
projection emitted no --- at all, so every page measured as 100% hero
and failed by 300 words. Fixed in the PROJECTOR, not the gate: a
<header> that is a direct child of <main> is the hero, and markdown's
only way to say "the hero ended" is a thematic break. The alternative
was putting an <hr> in the page so the markdown would have one — adding
visual furniture to satisfy a gate, which is the exact dishonesty these
gates exist to catch.
Mutation-tested through the real build, not against a copy: swap "a named
bundle of permissions" for "a room of doors the guest holds", rebuild, and
the build stops at the legibility step naming all three words. (First
attempt at this reported a red build that was actually the catalog
staleness check failing earlier in prebuild — the gate had not run at all.
Re-verified.)
Repetition ratchets 19 → 7 pairs on the back of the rewrite. Two remain
within a page, five across pages.
The gate proves the page did not regress. It cannot prove the page lands.
That is still one outside human, and this changes nothing about that.
Two overclaim errors from the upstream copy gate, both on new copy, both correct. "Here, EVERY privileged action goes through one gate instead." That is the thesis sentence, and it asserts universal present-tense coverage that the Status section three screens down contradicts in its own words: git writes are "tested end to end, but opt-in today", and prx and claude-box "converging onto guest-room" is graded Aspirational. The page was claiming in the hero what it grades as unfinished below. Now: "Here, privileged actions go through one gate instead." Same sentence, no quantifier, and it stops disagreeing with the page it sits on. "CI proves it on every commit" — the Enforced grade's definition, an absolute with no source in its block. The rule allows either scoping or a link, which is why "Each deploy is keyless-signed and recorded in a public log — provenance.json, Rekor, and this build's entry" passes: the sources are right there. This one has nowhere to point without adding furniture, so it scopes instead: "A check proves it, and the build fails without it." CODEQL, on the delivered check.mjs: `<!--[\s\S]*?-->` left an unterminated comment in place, so its body would be counted as prose and every budget would then be measuring an authoring note. Completed to `(?:-->|$)`. Third change to the instrument, same character as the other two — not an extension, a rule doing what it says. Still PASS at hero 90w · 4 sections · 3 acronyms, and the live page still FAILs on all six documented counts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #219. Your copy landed as the page, and your instrument wired into the build that protects it.
Hero (the problem, the move, one allow/deny transcript) → What's here (three repos, a line each) → Status, graded (three grades, the six claims folded into them) → If you want to go deeper → Who.
FOCUS_SECTION_TOO_LONGThose last three are proper nouns. Every actual term of art — Sigstore, Fulcio, OIDC, GHCR, OCI, in-toto, SLSA, DSSE, ed25519 — is off the page.
The gate
The deterministic half runs in the hermetic phase directly after
gen-markdown.mjs, because the projection is what it reads: the words a stranger gets, with the markup gone.The judged half (
scripts/legibility/coldread.feature) is not wired. Its own header says why and the pipeline entry repeats it: a flaky proxy in the inner loop rots trust in the gate around it. Deploy-time only, and a green judge run is not the human test.Mutation-tested through the real build, not against a copy: swap "a named bundle of permissions" for "a room of doors the guest holds", rebuild, and the build stops at the legibility step naming all three words. (My first attempt at this reported a red build that was actually the catalog staleness check failing earlier in prebuild — the gate had never run. Re-verified properly.)
Two changes to the instrument as delivered
Flagged rather than buried, since you said to resist extending it — neither of these is an extension.
The sentence rule split on terminal punctuation only, so it ran straight through blank lines: six seam rows with no full stop between them measured as one 59-word sentence. A false positive about the instrument, not a finding about the page, and it would fire on any page with a short unpunctuated list. Blank lines now split first. A sentence does not span a paragraph break, so this is the sentence rule measuring sentences.
The hero budget reads "words before the first
---", and the markdown projection emitted no---at all — so every page measured as 100% hero and failed by 300 words. Fixed in the projector, not the gate: a<header>that is a direct child of<main>is the hero, and markdown's only way to say "the hero ended" is a thematic break. The alternative was putting an<hr>in the page so the markdown would have one — adding visual furniture to satisfy a gate, which is the exact dishonesty these gates exist to catch.What moved in the page, and why it moved rather than being suppressed
Eighteen scripts read
index.html. Most kept working. These didn't:gen-seamsspliced the capability-seam grid into a homepage card that no longer exists → moved to/map.gen-registryspliced a three-tile count summary into#graph./mapalready renders the same numbers with the whole registry underneath, so the second renderer goes rather than moving: two renderers for one dataset is the drift this project argues against. The script keeps its data duties, andsync-registry.ymlnow checks the seed and the page that renders it.check-outlineasserted "at least one.seams__headon index.html" — an assertion about the homepage's shape, not about the component, so moving a card to the page it belongs on failed the gate with nothing regressed. Presence is now global across surfaces, andmap.htmljoins the surfaces checked, so/map's outline is under contract for the first time.content/marketing.featurepinned micro-copy that is gone. Scenarios deleted alongside the copy they described; leaving them red or loosening them to pass would both be worse.content/grounding.jsongains two phrasings — the two ENFORCED claims kept their meaning and changed their wording. Same evidence, restated; not a new assertion.Dropped on purpose
The room/door/guest hero figure · the bet and model sections · the in-page self-check demo (~100 lines of JS the page itself labelled "a demo, not proof") · the two provenance cards · the conformance battery paragraph and its seven acronyms · everything the homepage restated about
/desk,/contracts,/conformanceand/ledger.Those pages stay in the site nav on every page. The homepage just stops saying them again.
All six claims in
integrity/claims/claims.jsonldsurvive, folded under their grade. The single-source floor holds at 25 ≥ 22.Two things I changed in your copy
"Every deploy is keyless-signed" → "Each deploy". The upstream honesty lane reads a bare absolute as an unscoped coverage claim and rejected exactly that shape earlier tonight.
The hero transcript is three lines per case, not the one-liner.
.codewraps rather than scrolls — deliberately, so a<pre>'s min-content can't inflate the page on a phone — so a line that doesn't fit breaks mid-clause. The one-liner broke at "keeper holds git-" at 390px. Measured at 390 / 768 / 1280: nothing wraps now.Two things worth your call
/deskisn't linked from the homepage. You said you were excited about Front Desk; the copy doesn't mention it. Still one click away in the nav. Say the word and it's a paragraph in "go deeper".Dead CSS is still there.
.hero__panel,.claims,.seams__*are unused by the homepage now, butstyles.cssis shared with five pages and deleting shared rules on inspection alone is how you break four of them. Separate pass with a usage check.Sequencing
This branch now contains #218 (the markdown projection) — the gate reads its output, so they had to travel together. #218 can be closed as superseded, or merged first; either way this merges cleanly.
#216 is independent and still worth landing: its
.provenanceCSS becomes dead once this merges (the cards are gone), but the card-inset gate it adds is the durable half and still guards.cardon/map,/desk,/contractsand/conformance.Verified locally
Run the way CI runs them —
node build.mjsplus the four brand tools from a clone ofbrandat the installed version:npm run check(23 gates) ✓npm run legibility— PASS; and red through the full build on an injected metaphor ✓gen-markdown --check✓meta/content(24/24 copy scenarios) /coverage --colors/a11y✓axe-gate— 13 pages, 0 serious/critical ✓a11y-heuristic— 0 errors ✓structure-audit✓ (baseline re-blessed;check-outlineis the independent invariant that makes re-blessing safe, and it passes)The gate proves the page didn't regress. It cannot prove the page lands — that's still one outside human, and nothing here changes that.