S9 — plain language: plain meaning first, term after in parentheses - #93
Conversation
The Guide is deleted rather than rewritten. A term a reader has to look up is a term the copy should have explained where they met it, and most of this product's copy is read outside the app — in digests, tickets and screenshots — where no glossary is reachable at all. - `src/lib/guide.ts` and its test are gone. The file had no importer in `src/` besides its own test, so the glossary was already unreachable; what it carried that still matters has moved. - `/guide` now redirects to `/issues`, alongside the other retired destinations. It used to aim at `/settings#reference`, an anchor that never existed and now never will: the operational content is distributed beside the controls it explains, so there is no single section to land on. - The operational half moves to Settings as short lines beside the controls they explain. What a run does and how often it happens is said in the Digest group's help, where the reader meets the phrase "per run". What each system contributes is a new "Always on" block in Connected systems, keyed by the registry's evidence sources so a system added to the ledger cannot arrive unexplained, and carrying its own cadence — nightly for the lab test and the agent checks, weekly for the visitor figures. One screen-level claim about "how often" would have been false for whichever system it did not describe. - The Ora row keeps its public-scan disclosure, which was the one thing in the glossary that was an obligation rather than a definition. - Registry v10 renames `evidence_source.kitesurf`'s label from the codename "Kitesurf" to "Rendered page". The codename was the only name a reader ever saw for that system, which is precisely the term-as-jargon this chunk forbids. The KEY is unchanged, so no stored reading migrates. - `systemLabel` in `agentIssueCases.ts` now reads the ledger's labels instead of holding its own three strings — one of which, "Page Watch HTTP", was a spelling the registry never named (rule 20). Its test asserts against the registry rather than a literal (rule 21). `banned_global.allowlist` loses its `src/lib/guide.ts` entry with the file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The pattern, applied to the screens that carry measurements: plain meaning
first, industry term after it in parentheses. Internal shorthand is gone.
`plain-language.ts` states the pattern and the two groups it applies to, and
`appositive()` builds an introduction so the ordering is structural rather than
remembered.
What changed and why:
- Lighthouse audit ids no longer reach a reader anywhere. `culpritEvidence`'s
titles were half-translated already ("DOM structure", "Image encoding
waste"); they now say what the finding is ("Deeply nested elements", "Images
heavier than they need to be"). The keys are untouched — they are how a
reading is matched to the report it came from.
- The evidence facts said "Main-thread time" and "Blocking time", which name
the browser's internals rather than the reader's problem. They now say what
the number cost: "Time the browser spent running it", "Time the page could
not respond".
- The metric chip led with three letters — "TBT · 30%" — and buried the only
part a reader can use. `metricDisplay` puts the plain word first and keeps the
acronym after it. The spelled-out term is introduced once per screen in the
prose beside the chips, never inside them.
- The score-card tooltips listed their measurements as bare acronyms. Each is
now introduced by what it measures. The tooltip is not the pattern and must
not become it — the comment says so — but its own copy still has to be plain.
- The lab/field comparison called its two sides "Lab" and "Visitor", words that
only mean that to us. It now says "the nightly test" and "real visitors"
throughout. The acronyms stay on the metric rows, because the responsiveness
row deliberately compares two DIFFERENT measurements and stripping the names
would hide that.
- The page-detail history table introduces every measurement in its caption,
once, so the four-character column headings are a second mention rather than
the reader's first. "A11y" is gone; it was a numeronym.
- "PSI" is gone from copy. It is our shorthand for a service nobody else calls
that, and the device is the part a reader can act on.
Three headline/label sets moved into named exported maps —
`LAB_FIELD_HEADLINE`, `VISITOR_CONFIDENCE_LABEL`, `CULPRIT_GROUP_LABELS`.
Each was a set of literals inside one if/else, asserted by a second set of
literals in a test: a rewording had to be made twice and agreed nowhere
(rule 20). The tests now assert against the map, so they check which
conclusion was reached rather than how it was spelled (rule 21).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ring
The rest of the sweep, plus the test the pattern needed.
`plain-language.test.ts` asserts the ORDERING rather than the punctuation: no
reader-facing string may open with an industry term, no internal shorthand or
rule id may appear in one at all. Checking for a parenthesis would have passed
"CLS (cumulative layout shift) 0.24", which is the same defect wearing the
pattern's clothes. It was proved by injection — three deliberate violations
shaped like the brief's own examples, each caught by a different assertion.
It does NOT check "one appositive per term per screen". A screen is not
something a test can see, and the brief says that half is a review call.
Copy:
- The 20 agent-readiness checks now have display labels separate from their
names. A separate map rather than a rename, because those names are KEYS: an
exclusion is stored as `group + name`, the scanner's results are keyed by
check name, and the issue families refer to checks by name. Renaming them
would have stranded every exclusion a site has recorded — a migration, not a
copy change. "DNS for AI Discovery (DNS-AID)" was already written this way
and is the shape the other nineteen now follow.
- The agent-access cause lines said "origin" and "parse" — our word for a site,
and what a program does to a document. S4 locked them; S9 re-decides them,
and the test's lock moved with the decision.
- Six issue titles opened with an acronym ("MCP resources are missing…",
"API errors are not machine-readable"). Each now leads with what the reader
loses and names the standard after it.
- The demo data's case titles were Lighthouse audit titles carried through
verbatim — "Reduce unused JavaScript", "Properly size images" — which the
`IssueCase` contract explicitly forbids: "one plain sentence… Never an audit
title." The mock provider in `psi.ts` carried four more, and reaches demo
mode as real copy.
- "PSI" is gone from the last of the copy, including the collection-status
lines, the chart's excluded-reading label, and the demo digest note.
- `robots.txt` and `sitemap.xml` are deliberately NOT treated as terms needing
an appositive. The decided copy is the argument: "Your robots.txt file tells
ChatGPT's crawler not to read this site" opens with the filename and needs no
gloss, because a file on the reader's own site is something they can go and
look at.
Verified: lint, typecheck, 1333 tests and build all pass. The new Settings
block was rendered against the compiled token layer at 1280px — two columns,
no name wrapping, no clipping, no overflow — and its media query collapses to
one column below 620px.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Trial merge against #84 (S5)Ran locally, because overlapping-but-non-conflicting merges are this repo's documented failure mode. Three files are touched by both branches:
The pair I was worried about auto-merges correctly. S9 adds a
So the order costs #84 one trivial import union on top of a rebase it already needs. Nothing here requires S9 to change, and nothing requires #84 to wait — but per One thing #84 may want to pick up while rebasing: 🤖 Generated with Claude Code |
Base commit
95d77a98e77cafef99492d0964f5fb92e599a510lint0,typecheck0, 104 files / 1335 tests,buildOK.origin/mainwas identical, andREPAIRS.mdhad nothing open.What & why
S9 — plain language. Option 11b: plain meaning first, technical term after it in parentheses. Tooltips and glossary links were both considered and rejected, and the reason is where this copy is read: a digest arrives in email, a fix is pasted into a ticket, a case is screenshotted into Slack. None of those carry a hover target or a link back into the app, so the meaning goes in the sentence.
Two groups, two treatments. Group A — internal names, vendor codenames, and Lighthouse rule ids — is rewritten out of reader-facing copy entirely. Group B — industry-standard measurements and web standards — keeps the term, because renaming "cumulative layout shift" would cut the reader off from every other tool that measures it, and introduces it with its plain meaning.
Changes
The glossary is retired, not rewritten.
src/lib/guide.tsand its test are deleted. The module had no importer insrc/besides its own test, so the glossary was already unreachable./guidenow redirects to/issues, alongside the other retired destinations. It used to aim at/settings#reference— an anchor that never existed and now never will, because the operational content is distributed beside the controls it explains.The copy sweep (~25 files)
TBT · 30%) and buried the only part a reader can use.metricDisplay()puts the plain word first.A11yis gone; it was a numeronym.PSIis gone from copy — our shorthand for a service nobody else calls that.group + name, the scanner's results are keyed by check name, and the issue families refer to checks by name. Renaming them would have stranded every exclusion a site has recorded — a migration, not a copy change."DNS for AI Discovery (DNS-AID)"was already written this way and is the shape the other nineteen now follow.IssueCasecontract explicitly forbids: "one plain sentence… Never an audit title."Registry v10 renames
evidence_source.kitesurf's label from the codenameKitesurftoRendered page. That codename was the only name a reader ever saw for that system — squarely the term-as-jargon this chunk forbids. The key is unchanged, so no stored reading migrates.Orastays: it is a vendor the user opts into by name. See Notes — this is the one call in here that is arguably product's rather than mine.Three literal-sets consolidated into named exported maps (
LAB_FIELD_HEADLINE,VISITOR_CONFIDENCE_LABEL,CULPRIT_GROUP_LABELS). Each was a set of strings inside one if/else, asserted by a second set of literals in a test — a rewording had to be made twice and agreed nowhere (rule 20). The tests now assert against the map, so they check which conclusion was reached rather than how it was spelled (rule 21).plain-language.test.tsasserts the ordering, not the punctuation: no reader-facing string may open with an industry term, and no internal shorthand or rule id may appear in one at all. Checking for a parenthesis would have passed"CLS (cumulative layout shift) 0.24", which is the same defect wearing the pattern's clothes. Proved by injection — three deliberate violations shaped like the brief's own examples, each caught by a different assertion. It deliberately does not check "one appositive per term per screen": a screen is not something a test can see, and that half is a review call.Verification
npm run buildpassesnpm run lintpassesnpm run typecheckpassesnpm test -- --runpasses — 104 files / 1333 testsREPAIRS.mdand kept in its own commit — n/a, no repair hereNotes
Two premises in the brief were wrong at this base sha, and one of them costs a VERIFY item.
1.
banned_global.allowlistcannot be emptied by S9. The brief said guide.ts "should be the last, but verify rather than assume." It is not. Linting each allowlisted file with the rule forced on:src/lib/guide.tssrc/components/bits.tsx"Verifying recovery: …"and"Returned after a confirmed resolution"src/components/store.tsx"cleared from Inbox"Both are left alone.
store.tsxespecially: #84 (S5, open) edits that same file and deletes the adjacent "Tasks board" string but not the Inbox one, and does not remove the entry. Editing it here is precisely the non-overlapping-merge collisionAGENTS.mdandREPAIRS.mdexist to prevent. So the brief's "allowlist is empty and lint passes with no exemptions" is not met, and cannot be by this chunk.A trap for whoever clears the last entry:
eslint.config.mjsspreadsALLOWLISTED_FILESintofiles:, and ESLint rejectsfiles: []with "Expected value to be a non-empty array." The last removal must delete the whole exemption block, not just empty the list.2. Two locked examples describe copy that does not exist here.
"Structured data absent on 2 templates"and"robots.txt disallows GPTBot"have no counterpart in the tree —GPTBotappears only inside a regex inagentReadiness.ts. Consistent with "these eight are the pattern, not a sample", so the shape was applied rather than the strings invented.Deliberate exemptions, both recorded in the test.
/admin's unmapped-audit screen keeps its audit IDs — it is app-admin-only and mapping those ids to the remediation catalogue is the screen's entire job. Androbots.txt/sitemap.xmlare not treated as terms needing an appositive: the decided copy is the argument, since "Your robots.txt file tells ChatGPT's crawler not to read this site" opens with the filename and needs no gloss. A file on the reader's own site is something they can go and look at.Browser verification is partial, and here is exactly what was and was not done. The new Settings block was rendered against the compiled token layer at 1280px — two columns, every system name on one line, no clipping, no overflow — and its media query collapses to one column below 620px. The integrated Settings screen was not driven in a browser: port 3100 was held by a dev server running from another checkout, and the production build correctly refuses the dev-session bypass. Worth a reviewer's eye on
/settingsand/pages/[id].Overlap with #84 (S5, open). Both branches touch
webflowPerformance.ts, its test,bits.tsx,pages-content.tsxandseed.ts. A trial merge of the two was run locally and the combined result is reported in a comment below.🤖 Generated with Claude Code