agents agents/SHELL.md
Written 2026-08-16 by lane P. Travis's ruling: shared shell, distinct character. ~33 surfaces should read as one program without becoming 33 identical pages.

The portfolio shell

REVISION `shell-r16`, 2026-08-17. Record this string in your surface's records/surface.json — and in your repo's `CLAUDE.md`, which is the file every agent reads first and which the r9 bump missed on two surfaces. A gate should check the two agree. Record this string in surface.json as shell_revision, and print it in your report. Every lane so far has found a defect in this document; a surface that says which revision it was built against is the only way to know, later, which pages carry a fix and which predate it. | rev | change | found by | |---|---|---| | r1 | first extraction from GPSCoord + toolboxhvac | lane P | | r2 | §8 identifying animation added — required on every surface | Travis | | r3 | `--fg3` `.34` → `.55` — was a 2.78:1 WCAG failure; plus the hover/focus rule | Travis | | r4 | tier-4 band variant (attribution, not membership); "zero JavaScript" corrected to "whole content with zero JavaScript" | Travis · globe lane | | r5 | 390 px `.top` stacking fix; `rung_witness` in the surface record; check `§N` citations against the spec's real headings; the band check must refuse in both directions | govtrio lane | | r6 | TWO GATE HOLES inherited from the GPSCoord reference — every surface copied them; the nav breakpoint is surface-specific, not `430px`; three band variants, not two | bendrune lane | | r7 | `.top nav a` overrides `.btn` on ALL NINE surfaces — the header CTA ships unreadable; h1 `line-height` must clear the font's ink box, not its em size | Travis | | r8 | EVERY GATE'S TEXT EXTRACTION IS WRONG — <[^>]+> does not remove an HTML comment containing >; check the CASCADE on the artifact, not declared tokens | os-anim lane | | r9 | CONTACT IS A FORMSPREE FORM — RULED, endpoint given. The [TRAVIS] item fourteen surfaces were blocked on is answered | Travis | | r10 | r6's OWN FIX HAS A RESIDUAL — onPage === inBlock still permits unlimited repetition inside the retraction, and the retraction is record-authored | brafix lane | | r11 | BISECT the nav breakpoint, never compute it, and bind it to the labels; a gate needs soundness probes, not only refusal breaks | graph-anim lane | | r12 | r8's fix is necessary and NOT sufficient — tag-stripping shreds text nodes, so every multi-word blocklist rule is unfalsifiable; structural checks must be scoped to the element; a break harness needs a CONTROL run | geotick lane | | r13 | §8 gets a measurable acceptance test: COVERAGE and DYNAMISM — and fixing one breaks the other | graph-anim lane | | r14 | THE GATE READS ONE FILE; THE HOST SERVES A DIRECTORY. Also: r7 is not universal, and r8/r12 were narrower than claimed | projfix lane | | r15 | THE BLOCKLIST MUST COVER EVERY FILE THE BUILD PUBLISHES, NOT JUST THE PAGE — three of four published files were exempt; plus String.length is UTF-16, not bytes | os-anim2 lane | | r16 | EVERY OVERFLOW SWEEP IN THIS PORTFOLIO IS BLIND TO `position: fixed` — the nav's CTA is off-screen on every surface and no check has ever seen it; plus the shared nav ships a mailto: | navback lane | ### r16 — scrollWidth cannot see a fixed element, so nine widths × fifteen surfaces proved nothing about the nav A `position: fixed` box does not extend scrollable overflow. documentElement.scrollWidth stays clean no matter how far past the viewport it runs. Every "no horizontal overflow at 390/700/…/1600" result in this project was therefore silent about the fixed nav bar, on every surface, from the beginning. Measured, and it is a live defect: the nav's Join → CTA is off-screen between roughly 861 and 1116 px on every surface that ships the nav. Bisected on geofleetic — at clientWidth 985 the CTA's right edge is at 1098, i.e. 113 px past the viewport. Confirmed identical on deliberatic.com, which has carried the nav all along, so it predates the restoration and is not caused by it. So an overflow sweep must additionally walk every fixed/sticky element and compare its own `getBoundingClientRect().right` against `innerWidth`. scrollWidth is necessary and not sufficient — the same shape as r8/r12: the check ran, passed, and could not have failed. ### r16 — the shared nav publishes a mailto: on every surface that ships it ampersand-nav's registry carries contact: { tagline: "[email protected]", href: "mailto:hello@…" } in a rendered section. So every surface with <amp-nav> publishes an email address and a mailto: link, against the standing rule (Travis, 2026-08-11: no `mailto:`, not even as a fallback). It has been true on the eleven surfaces that kept the nav all along, and every gate's !landing.includes('mailto:') passed — because the rule read index.html and the mailto: lives in a script. That is r15, in the wild, on the majority of the portfolio. Only ampersand-nav/ can fix it, so gates carry a declared, dated, bounded exception (mailtos <= 1) rather than a silent exemption — and it passes at 0 the moment lane N removes it. Never widen a rule to make a violation fit; bound the violation and date it. ### r15 — a retracted claim can ship in a script and pass the gate Demonstrated, not hypothesised. 117 laws was planted in a comment inside _rebuild/build/idanim.js, shipped in a published file, and the build passed — because the blocklist read index.html and nothing else. On that surface the build emits four downloadable files; three were exempt. This is r14 one layer in: r14 says the host serves a directory the gate never read. r15 says the build itself publishes files the gate never read. A retraction that only governs the HTML is not a retraction. Enumerate what the build writes and run every text check over all of it — page, scripts, stylesheets, JSON, manifests. Anything a visitor can fetch. ### r15 — String.length is UTF-16 code units, not bytes The same build printed html.length as "bytes": 91,020 for a 91,362-byte file. SITES.md §0.1 makes local-vs-served byte comparison the check that a deploy is current — so this would have reported a correct deploy as stale, or masked a real drift, on any page containing a non-ASCII character. This portfolio's pages are full of κ, §, — and ▸. Use `Buffer.byteLength(s)` / `new TextEncoder().encode(s).length`. Never .length for a byte figure. ### r15 — prove a review port is yours before believing anything measured through it That lane's first before/after screenshots were silently of another lane's site: port 8126 was already bound by a sibling serving graphonomous.com. Ports in this tree are shared and a python3 -m http.server on a taken port fails quietly enough to miss. Verify the served bytes match your own artifact's hash before trusting a measurement through that port — the same discipline --session gives you in the browser. ### r14 — a gate that reads index.html has audited one file, not a site Every gate here reads the emitted artifact. The host serves the whole directory. On wrand.cc the gate was green while `/old_scrap/v1/` served a 35,861-byte previous revision opening "Forking the Landscape of Tomorrow" — a superseded page, publicly reachable, invisible to every check. It fired the moment someone looked. Enumerate what the host would serve and require every `.html` outside the gated set to be accounted for — redirected, renamed out of the served extension, or deliberately listed. A _redirects file is a declaration; until a live host honours it, say so. graphonomous.com has the same exposure (/old_scrap/, /demo/ 18 pages, /benchmarks/ 4), as does any repo with an old_scrap/. ### r14 — two portfolio-wide claims of mine were narrower than I stated - r8's comment-stripping delta on `wrand.cc` is 0 characters — none of its five comments contains a bare >, so no earlier count there was invalidated. The defect is real where it occurs and is not universal. - r12's phrase-shredding was not live on either projfix surface — both blocked phrases sit inside a single text node. Real, worth fixing everywhere, not everywhere broken. The pattern in all three (r7, r8, r12): I generalised from one measured instance to "every surface" without measuring the others. Fixing them everywhere was right; claiming they were broken everywhere was not. Say "found here, check yours" — not "confirmed on all nine." ### r13 — an identifying animation has two failure modes, and they are opposites "More dynamic" and "less clumped" are matters of taste until they are numbers. They are two numbers, and they pull against each other. COVERAGE — what fraction of the picture has a trace reached by 5 s / 15 s / 30 s? A plain random walk gets trapped in a neighbourhood, so one corner animates and the rest is dead field. Fix with blue-noise / best-candidate node placement (not hubs-with-jitter, which clumps by construction), k-nearest-neighbour edges instead of long chords, several concurrent traces biased toward least-recently-visited nodes, and a union-find pass to bridge stranded components — a component no walk can reach is the same bug in miniature. DYNAMISM — what fraction is "resolved" at an instant, and how many elements change state per second? Fixing coverage causes the opposite failure: full coverage every few seconds saturates everything and the picture sits uniformly crisp — exactly as unreadable as the clump, because a viewer needs something vague beside the sharp part to see the difference. Fix with a decay whose time constant is shorter than the coverage period, so the resolved set is a moving subset. Measured on graphonomous.com after both fixes: coverage 95% at 3 s, 100% at 5 s, last node first touched at 3.7 s; dynamism 13/9 crisp-to-haze at 3 s, 17/5 at 10 s, 11/11 at 25 s, with 8.7 of 22 nodes changing state per second. How to measure it without violating §8. The animation may expose nothing, so do not instrument the shipped file. Run the shipped artifact headless against a stub document with the smallest possible instrumentation inserted into a copy, and print the diff size so the claim is checkable — that lane's was 2 lines, 54 bytes on 12 KB. It proves the simulation, not the painting; the painting is still checked by looking, at several widths and several points in the cycle. ### r13 — check the canvas is not being clipped before blaming the animation The same lane found .idanim positioned at right:-34px, and right:-28% at narrow widths, hanging the canvas past the hero's overflow:hidden. Harmless while the margin was empty — but at 800 px it was discarding 207 px of a 470 px canvas, and the remainder read as scattered specks. Measure `animRight - heroRight` at every width and require 0. A "sparse" animation may simply be a cropped one. ### r12 — the phrase blocklist has never been able to fire r8 said strip comments first. Necessary, and not sufficient. The usual next step — ``js const visible = html.replace(/<[^>]+>/g, ' ') // then split/normalise on whitespace visible.includes('Get started') // ← can never be true ` — **replaces every tag with a space, which shreds each text node into single words.** Any blocklisted **phrase** is therefore unfindable. On the geotick surfaces, two multi-word rules were silently unfalsifiable, including *"no signup CTA at the spec rung"* — one of the rules the whole status-boundary argument rests on. **Split ON tags so a text node survives as a node:** `js const nodes = html.replace(/<!--[\s\S]*?-->/g,'') .replace(/<script[\s\S]*?<\/script>|<style[\s\S]*?<\/style>/g,'') .split(/<[^>]+>/) // ← each element is one string .map(s => s.replace(/\s+/g,' ').trim()).filter(Boolean) ` Then test phrases against nodes, not against a flattened blob. **Every gate that copied the phrase-blocklist pattern has this.** It was found because a deliberate break *failed to fire* — which is only visible if you notice a break that should have refused and did not. ### r12 — scope structural checks to the element, not the raw HTML /name="_gotcha"/.test(html) **passed with the honeypot deleted**, because the inlined stylesheet carries .say input[name="_gotcha"]. A selector in CSS satisfied a check about markup. Same shape: an above-the-fold check that greps CSS passes when a @media rule still mentions the property. **Parse to elements and resolve through the cascade; never .test() the whole document.** ### r12 — a break harness without a CONTROL run is worthless The geotick lane froze two environment-dependent facts ("3 upstream contracts checked", "0 PULSE manifests"). Both are absent in a standalone clone, so its **first break run produced 20 refusals that were all refusing for an unrelated reason** — a perfect-looking table that proved nothing. A relative path in the harness had also silently applied no patch at all. **Run the harness UNMODIFIED first and require it to pass.** Only then are the refusals evidence. This is r11's soundness probe applied to the harness itself rather than to the gate. **And never freeze an environment-dependent fact into a record** — freeze what the repo contains, not what happens to be beside it. ### r11 — the nav breakpoint must be measured by bisection and bound to the labels r6 said the breakpoint belongs to the surface. It did not say how to find it, and **the arithmetic way is wrong.** The graph-anim lane's estimate **under-read by 38 px**, because flex-wrap distributes inside the nav's *own* width rather than the container's. Worse, it is **fragile in a way nothing was checking**: renaming one nav item to *"Correct us"* — five characters — moved the wrap point from **538 → 576**, past a 560 breakpoint, marooning the logo in a broken two-row state between 561 and 575. A label edit silently invalidated a measured number. **So: bisect the real wrap point in a browser, then record the labels you measured it with** — nav_labels_at_measure in records/surface.json — **and have the gate refuse when the live labels drift from the recorded ones.** A measurement whose inputs can change without notice is a stale number waiting to happen. ### r11 — a gate that only proves it REFUSES is half a gate Every prove-gate in this portfolio forces deliberate breaks and asserts each one is refused. **That harness cannot see a false refusal.** A check that refuses everything — including correct input — scores perfectly on it. **Add a soundness stage: assert the gate PASSES on known-good input**, and on inputs that are unusual but legitimate. The graph-anim lane added one and it belongs in every gate. Both halves matter: *refuses what it should* **and** *permits what it should*. ### r10 — the fix to the gate hole has a gate hole r6 said *"count occurrences and bound them."* The obvious implementation is: `js if (onPage !== inBlock) refuse() // every occurrence accounted for inside the retraction ` **That is still wrong, and it was made to approve a lie.** The retraction paragraph is authored from a **record field**, so it is editable content — and the check permits the blocklisted string to repeat *without limit* inside it. On agentelic.com the sentence was appended three times, putting **"Start building today" on the artifact four times**, and the gate reported 114 passed, 0 refused. **The bound must be a number, not an equality:** `js outsideRetraction === 0 && insideRetraction <= 1 // and refuse hidden occurrences outright ` **If you have already implemented r6, check which shape you wrote.** The onPage === inBlock form is the natural one to reach for and it is the defective one. This is the third time in this project that a check passed *vacuously* — a regex matching a stylesheet header and measuring nothing; a substring test satisfied by an unrelated listener; and now an equality that is trivially satisfiable by editing both sides. **The general rule: a check whose two sides are both under the author's control is not a check.** ### r9 — contact. **Ruled by Travis 2026-08-17: use the ComputeDriven form.** Every lane so far has reported the same blocker — *"no Formspree endpoint exists for this surface, so corrections point at GitHub issues"* — and flagged it [TRAVIS]. **It is now answered: use the same Formspree mechanism computedriven.com uses.** **Endpoint: https://formspree.io/f/xaewoadr.** **Copy the shape, not just the URL.** From computedriven.com/index.html, and every part of it is deliberate: `html <form class="say" action="https://formspree.io/f/xaewoadr" method="POST" novalidate> <label><span>Your email</span> <input type="email" name="email" autocomplete="email" placeholder="so a reply can reach you" required></label> <label><span>Message</span> <textarea name="message" rows="3" placeholder="a question, a correction, a number of ours you think is wrong" required></textarea></label> <!-- Formspree's honeypot: a bot fills it, a person never sees it. --> <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" aria-hidden="true"> <div class="say-act"> <button type="submit" class="btn">Send</button> <p class="say-msg" role="status" aria-live="polite"></p> </div> </form> ` - **A real <form action>, not a fetch bolted to a button** — it must work with JavaScript off, like everything else on these pages. - **JS only upgrades it to an inline reply** so the visitor is not handed to somebody else's thank-you screen. **Print success only on an actual 2xx from the endpoint** — never optimistically. - **The _gotcha honeypot is required.** So is role="status" aria-live="polite" on the reply. - **Still no mailto: anywhere**, and the build must still refuse one. This replaces the GitHub-issues fallback as the primary correction channel; keep the issues link as a second route if you like. - The placeholder text is worth keeping almost verbatim — *"a number of ours you think is wrong"* invites the one kind of message this portfolio most needs. **Every surface already built points corrections at GitHub issues and should be migrated.** ### r8 — the text-extraction regex is broken in every gate that copied it Every surface's gate extracts "page text" with some form of: `js const text = html.replace(/<script.*?<\/script>|<style.*?<\/style>/gs,'').replace(/<[^>]+>/g,' ') ` **<[^>]+> stops at the first >.** An HTML comment that *contains* a > — and source comments in this portfolio are full of them: κ > 0, -->, a > b — is therefore only partially removed, and **the remainder of the comment is counted as visible page text.** Confirmed on the served opensentience.org: naive stripping leaves κ from a hero comment that never renders. Two checks were reading it, **one of them the retraction counter whose entire job is telling visible from hidden.** **Strip comments FIRST, as their own pass:** `js html.replace(/<!--[\s\S]*?-->/g,'') .replace(/<script[\s\S]*?<\/script>|<style[\s\S]*?<\/style>/g,'') .replace(/<[^>]+>/g,' ') ` This invalidates, to an unknown degree, every "no constant leaked" and "retracted string absent" result reported before r8 — a false positive was possible in both directions. ### r8 — resolve the cascade over the artifact; a declared token is not a painted colour r7's .btn defect survived **every** contrast check in the portfolio because those checks read **declared tokens**. The token was always fine; the element never received it. **The gate must resolve the cascade over the emitted artifact** — specificity, then source order, then !important, @media included — and refuse when a button's colour is decided by a non-button rule. The os-anim lane built this and cross-checked its verdicts against the browser's computed styles for all 8 buttons in both states; they agreed on every one. **That is the check to copy, not the :not(.btn) patch, which only fixes today's instance.** ### r7 — the header CTA is unreadable on every surface built so far `css .top nav a{color:var(--fg2);…} /* specificity 0,2,1 — WINS */ .btn{background:var(--acc);color:var(--acc-ink);…} /* specificity 0,1,0 — loses */ ` A .btn inside .top nav therefore paints **--fg2 on --acc** — washed-out light text on a saturated accent — while the *same* button in the hero, outside the nav, correctly paints dark ink on accent. Measured on runefort: header CTA rgba(233,234,228,.66) on #9ee04f; hero CTA rgb(11,20,3) on the same green. > **CORRECTED 2026-08-17 — "confirmed identical on all nine surfaces" was WRONG, and the error was > in the method.** I established it by grepping for the two rules co-existing, which every surface > does. **That does not establish the defect**, because the selector still has to match the element. > > **wrand.cc measured clean before anyone touched it**: its header CTA is a > <button class="btn">, and **.top nav a cannot match a <button> at any specificity.** > agentelic, deliberatic, delegatic and agentromatic were **latent** — the vulnerable rule > present, no .btn in the nav to hit. Only some surfaces were actually shipping it. > > **Grep finds a rule. Only the cascade resolved over the real element tree finds the defect.** > That is why r8's resolver is the deliverable and :not(.btn) is not. **A second half, unreported until 2026-08-17 and arguably worse than the resting state:** the unscoped **.top nav a:hover** repainted the CTA --acc **on --acc** — *the button vanishing under the pointer.* **Scope the :hover rule too, and resolve every button in BOTH states.** **Fix — scope the nav rule instead of raising .btn:** `css .top nav a:not(.btn){color:var(--fg2);…} ` Then assert it: **every .btn must resolve to its declared ink colour wherever it appears.** **Gate check to add:** for each .btn on the page, compare computed color against the .btn rule's declared colour and refuse on mismatch. A CTA that loses its colour to a cascade conflict is invisible to every check we currently run — contrast checks pass because they read *declared* tokens, not *computed* ones on real elements. ### r7 — line-height must clear the font's ink box agentromatic's h1 is Syne at 65.6 px with line-height: 1.02 → a **66.9 px** line box. The glyph run actually measures **78.7 px** (ascent 34 + descent 14 at that size, plus leading). Consecutive lines therefore **overlap by 11.8 px**, and a descender — the g in *"agents"* — collides with the cap-height of the line beneath it and reads as clipped. **Display faces vary enormously in ink height; 1.02 is safe in one and broken in another.** > **CORRECTED 2026-08-17 — the metric below was wrong, and taken literally it was a no-op.** > **The binding number is fontBoundingBox, not actualBoundingBox.** On agentromatic at 65.6 px: > fontBoundingBoxAscent+Descent = **79.0 px** (what I called "the glyph run"); > actualBoundingBoxAscent+Descent = **59.0 px** — which already fitted the 66.9 px line box, so > *"raise line-height until the ink fits"* would have changed nothing on the site that prompted it. > > **And the g was never clipped.** Rendered in isolation with no line box at all, Syne's g has > the same flat square terminal it has on the page. **It is simply short and flat-tailed, and reads > as cut because the line beneath it is too close.** Right conclusion, wrong mechanism. > > **Check BOTH — each site failed on a different one:** agentromatic (Syne 700) fontBox **−12.09** > → 1.26 · delegatic (Archivo 600) fontBox **−5.09** → 1.18 · deliberatic (Newsreader 400) > fontBox fitted and **ink −0.91** → 1.10. **Neither metric alone is sufficient.** Measure rather than assume: render the string to a canvas in the element's own computed font and require **both** fontBoundingBox… **and** actualBoundingBox… to fit the line box, at every width — the tightest case is usually 390 px. **A gate with no font engine cannot enforce this; record it as a measurement and say so rather than implying a check that does not exist.** ### r6 — the two gate holes. **Every gate built before this inherited them.** Found by a lane breaking its own gate. **Both made a real deliberate break report PASS**, which means the break counts reported by earlier lanes are upper bounds, not proofs. **1. The retraction allowlist DETECTED where it should have COUNTED.** A blocklisted string is allowed to appear once, inside the retraction paragraph that quotes it. The check asked *"is the retraction present?"* and stopped. So a page that **kept** its retraction could reinstate the retracted sentence **anywhere else on the page** and pass. **Count occurrences and bound them; do not test for presence.** **2. Nothing checked that the artifact came from its own source.** If build-site.mjs threw, the previous index.html stayed on disk and the gate happily approved **a stale artifact**. A gate that reads the artifact must also prove the artifact is *this* build's — compare a hash of the emitted file against one recorded during the emit, or refuse. **Both are fixed in bendscript.com and runefort.com. GPSCoord, wrand.cc, graphonomous.com and the governance trio still carry them.** ### r6 — the nav breakpoint belongs to the surface r5's @media (max-width:430px) was measured on a four-item nav. **With six items the logo is dropped into a two-row nav at 700 px** and is only clean by 790. The bendrune lane measured **780 px** for its surfaces. **So the value is the site's own, not the shell's** — the shell fixes the *rule* ("stack .top before the nav wraps to two rows"), and each surface measures its own breakpoint and records it. ### r6 — there are THREE band variants, not two §1 documents the layer band and the tier-4 attribution band. **amp-nav's renderPlacement() gives the layer sentence to place: 2 ONLY.** place: 3 renders *"X is **a specification** in the ComputeDriven world"* plus a link to the real spec. Writing a layer sentence on a place-3 surface puts the band in direct contradiction with the nav rendered immediately beneath it — the gpscoord defect, one rung up. **Read renderPlacement() before writing any band.** ### r5 additions, all three earned by a lane finding them the hard way **1. .top breaks at 390 px.** justify-content:space-between puts the logo in the middle of a two-row nav once the links wrap. One media query fixes it: `css @media (max-width:430px){ .top{flex-direction:column;align-items:flex-start;gap:11px} .top nav{width:100%} } ` **2. rung_witness — name the gate that witnesses the rung.** GPSCoord's rule *"any pending gate blocks live_deployed"* is too blunt: independent_use is pending **forever by construction**, so a surface can never advance. The record names *which* gate witnesses its rung, and the others stay pending without blocking it. **3. Every §N on the page must resolve to a real heading in the spec it cites.** Cheap, and it catches a citation that drifted when a spec was rewritten. Watch the extractor: markdown headings inside fenced code blocks are not headings — strip fences first, or # 3 lines to join a cluster` reads as a section. 4. The band check must refuse in both directions. Refusing a layer claim a tier has not earned is only half of it; a tier-2 band that quietly drops its layer word is the same defect inverted, and it passed until someone tried it.

Written 2026-08-16 by lane P. Travis's ruling: shared shell, distinct character. ~33 surfaces should read as one program without becoming 33 identical pages.

Extracted from a real build, not designed in the abstract: ~/Projects/GPSCoord/ is the working reference and every fragment below is copy-pasted out of a page that serves. Structure and gates are borrowed from ProjectAmp2/toolboxhvac.com/, which is the best-engineered site in the portfolio and the reason §3 exists at all.

Scope. This is the page-level treatment. It does not touch ampersand-nav/ — lane N owns the nav source, and the band below is designed to sit above the nav and pair with it later. Do not edit ampersand-nav/ from here.

0. What is fixed and what is the site's own

Fixed — copy verbatimThe site's own
Placement bandmarkup, class names, the rung vocabulary, the ? rulethe layer word, and what its rung covers
Rung chipmarkup, the five rung names, the colour rolenothing
Status blockthe four rows and their order, LIMIT last and highlightedthe text
CTA patternthe verbs available at each rung — this is mechanicalthe copy under each verb
Type scalethe ramp and the variable nameswhich families fill --display / --ui / --mono
Spacing / radiusthe variable namesthe values
Palettethe variable names and their rolesevery value
Identifying animationthat there IS one, above the fold or close; and that it asserts nothing — see §8what it depicts, entirely
Everything else—hero, imagery, layout, voice

The mechanism is one CSS file with a marked block at the top:

/* TOKENS-START — GPSCoord: survey amber on ink */
:root{
--ink:#07090e;--ink2:#0b0f16;--ink3:#111721;          /* page · surface · raised */
--line:rgba(255,255,255,.07);--line2:rgba(255,255,255,.14);
--fg:#e9ecf1;--fg2:rgba(233,236,241,.62);--fg3:rgba(233,236,241,.55);
--acc:#ff8a3d;--acc-soft:rgba(255,138,61,.1);--acc-line:rgba(255,138,61,.32);
--data:#5ad1c8;--data-soft:rgba(90,209,200,.1);--warn:#f5c451;
--display:"Instrument Serif",Georgia,serif;
--ui:"DM Sans",system-ui,-apple-system,sans-serif;
--mono:"JetBrains Mono",ui-monospace,Menlo,Consolas,monospace;
--r:10px;--rl:16px
}
/* TOKENS-END */

A sibling surface replaces only what is between the markers. Everything below TOKENS-END is copied unchanged. The roles are what carry the family resemblance:

## CONTRAST — corrected 2026-08-16, measured, and it was shipping broken `--fg3` was `.34` and that is a WCAG failure: 2.78:1 on the band, against a 4.5:1 minimum. Travis reported it as "the top left button i can't read" — it is the .covers span in the placement band, 11 px at 34 % alpha. Measured in a browser, not eyeballed: | --fg3 alpha | on band #0b0f16 | on page #07090e | |---|---|---| | .34 (was) | 2.78 ✗ | 2.75 ✗ | | .45 | 4.00 ✗ | 3.96 ✗ | | .55 (now) | 5.43 ✓ | 5.45 ✓ | | .65 | 7.20 ✓✓ | 7.29 ✓✓ | The rule, and it is checkable: no text token may fall below 4.5:1 against the surface it sits on. --fg3 is used for the .covers span and every .status dt label, so this was dim on the two elements whose whole job is to keep a page honest — the rung's scope and the status labels. A caveat that cannot be read is not a caveat. At 11 px, aim for 7:1 rather than 4.5:1. Small uppercase at the AA floor is legible in a screenshot and not on a laptop at arm's length. Where a --fg3 element is 11 px and load-bearing, use --fg2. Every interactive element needs a visible `:hover`. .logo had none — which is the other half of what Travis hit: hovering the top-left changed nothing, so there was no way to confirm it was even a link. .top nav a:hover exists; .logo:hover did not. Add .logo:hover{color:var(--acc)} or equivalent, and check every <a> and <button> on the page has a hover and a :focus-visible state. Add to the publication gate: refuse the build if any declared text token measures below 4.5:1 against its own surface. It is a dozen lines of colour maths and it makes this class of defect unshippable rather than reported.

  • --acc is the site's character. It is the one colour a visitor will remember.

  • --data is measured values only — never decoration. Seeing the same colour

mean "this number has a witness" on every surface is most of the shared feeling.

  • --warn is an unbacked or refused claim — the claim tag, the ? rung, the

LIMIT row. Never an error state, never a brand colour.

Shared ramp, below TOKENS-END, not overridden:

:root{--wrap:1080px;
--xs:.6875rem;--sm:.8125rem;--md:1.0625rem;--lg:1.25rem;
--h2:clamp(1.7rem,4vw,2.5rem);--h1:clamp(2.3rem,6.4vw,4.1rem)}

Spacing is a 4 px base used directly (7px, 11px, 14px, 20px, 22px, 26px, 56px). There is deliberately no --s1…--s10 scale: the first draft had one, every value was still chosen by eye, and it added a layer of indirection that bought nothing.

1. The placement band

Sits at the very top of <body>, above the nav. Answers "where am I in this thing?" before anything else loads.

<div class="band">
  <span class="where">GPSCoord is the <b>coordinate</b> layer of ComputeDriven</span>
  <span class="rung" data-rung="live_local" title="spec · in_tree · live_local · live_deployed · external">live_local</span>
  <span class="covers">That rung covers the converter at /convert/ — and nothing else on this domain.</span>
</div>
.band{display:flex;flex-wrap:wrap;align-items:center;gap:8px 16px;padding:11px 22px;
  background:var(--ink2);border-bottom:1px solid var(--line);font-size:var(--xs)}
.band .where{color:var(--fg2);text-transform:uppercase;letter-spacing:.05em}
.band .where b{color:var(--fg);font-weight:600}
.band .covers{color:var(--fg3);flex:1 1 300px;min-width:0}

.rung{display:inline-flex;align-items:center;gap:7px;font-family:var(--mono);
  font-size:var(--xs);padding:2px 9px;border-radius:999px;
  border:1px solid var(--line2);color:var(--fg2);white-space:nowrap}
.rung::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}
.rung[data-rung=spec]         {color:#9aa4b2;border-color:rgba(154,164,178,.35)}
.rung[data-rung=in_tree]      {color:#7aa2f7;border-color:rgba(122,162,247,.35)}
.rung[data-rung=live_local]   {color:var(--data);border-color:rgba(90,209,200,.4);background:var(--data-soft)}
.rung[data-rung=live_deployed]{color:#4ade80;border-color:rgba(74,222,128,.4)}
.rung[data-rung=external]     {color:#c4a1ff;border-color:rgba(196,161,255,.4)}
.rung[data-rung="?"]          {color:var(--warn);border-color:rgba(245,196,81,.45);border-style:dashed}

## SUPERSEDED IN PART — 2026-08-17: `<amp-nav>` goes on EVERY website, tier 4 included. Travis: "the ampersand-nav needs to be on each website!" — and, asked specifically whether the two tier-4 surfaces were exempt: "yes add the nav to those too." The band variant below still stands. A tier-4 surface still takes "A ComputeDriven project" rather than a layer sentence. What is reversed is only the "no nav" half of the 2026-08-16 ruling: the portfolio nav now ships everywhere. Seven surfaces had dropped `<amp-nav>` when they adopted the shell — gpscoord, wrand.cc, bendscript, runefort, geofleetic, ticktickclock, webhost.systems — each lane independently, to keep the page's content free of JavaScript, and none of it was ever ruled. All seven get it back. The property that must survive: the nav is CHROME. Every rung chip, the status block, the band, all copy and every evidence figure stay build-rendered and complete with JavaScript off. Only the nav bar itself may require JS. Gate the presence of `<amp-nav>` — it vanished silently seven times, which is exactly what a gate is for.

The tier-4 variant — attribution, not membership. Ruled by Travis 2026-08-16.

The band above claims a layer: "GPSCoord is the coordinate layer of ComputeDriven." For some surfaces that is a false claim, and the nav already knows it. ampersand-nav/src/amp-nav.js records:

toolboxhvac: { place: 4 },  brokenrecord: { place: 4 },  gpscoord: { place: 4 },
istrav:      { place: 4 },  subvind:      { place: 4 },  wrand:    { place: 4 },
container:   { place: 4 },

`place: 4` means "Outside — nav removed." These sites are deliberately not in sync-nav.sh's target list. The positioning plan files them as "not part of this story": a coordinate utility or an HVAC instrument earns its audience on its own merits, and handing that visitor a stack diagram costs them something and buys nothing.

So a tier-4 surface takes the band, drops the layer claim, and gets no portfolio nav:

<div class="band" data-tier="4">
  <span class="where">A <b>ComputeDriven</b> project</span>
  <span class="rung" data-rung="live_local" title="spec · in_tree · live_local · live_deployed · external">live_local</span>
  <span class="covers">That rung covers the converter at /convert/ — and nothing else on this domain.</span>
</div>

What is kept and why: the rung chip and the covers span are honesty machinery, not branding. They tell a visitor what on this page is real, which is worth having whether or not they have ever heard of ComputeDriven. What is dropped: the layer sentence, and any nav listing sibling domains.

One line of attribution, no pitch. A tier-4 site is judged as the thing it is.

This was a live contradiction, not a hypothetical. GPSCoord shipped *"GPSCoord is the coordinate layer of ComputeDriven"* while amp-nav recorded it as place: 4 — the page asserted membership in a portfolio whose own nav deliberately excludes it. The template was applied without checking the tier. Check the tier before you write the band.

The chip renders the stored rung, and ? when there is none. Never a default.

const RUNGS = ["spec","in_tree","live_local","live_deployed","external"];
function rung(value) {
  const r = RUNGS.includes(value) ? value : "?";   // a defaulted rung is a fabricated status
  return `<span class="rung" data-rung="${r}" title="spec · in_tree · live_local · live_deployed · external">${r}</span>`;
}

Three rules, each of which cost something to learn:

  1. Render it at build time, not in JavaScript. A chip painted by a script is

blank to anything that does not run scripts, and blank reads as "no status" rather than "unknown". GPSCoord's landing page ships its whole content with zero JavaScript — no inline script, no DOM content API, one deferred external file for the §8 animation only — and still shows every chip.

  1. `?` is a first-class state and should be visibly different — dashed border,

--warn. Do not make it look like a rung. GPSCoord has a genuine one: whether /convert/ resolves on the deployed domain is unknown until it is pushed, so that gate is pending and says so.

  1. The band must bound what the rung covers. live_local on a surface where

one component is live and eleven are specified is technically true and reads as a lie. The covers span is not decoration — it is the thing that keeps the chip honest, and the gate checks it is present.

The rung of a surface is the rung of its best-evidenced shipped artifact, and the band must name that artifact. That is the rule that makes a single chip survivable on a mixed surface.

2. The status block

<dl class="status">
  <div><dt>Status</dt><dd><strong>live_local</strong> — …</dd></div>
  <div><dt>Last verified</dt><dd>2026-08-16</dd></div>
  <div><dt>Source</dt><dd>…the command or file that produced the evidence…</dd></div>
  <div class="limit"><dt>Limit</dt><dd>…the strongest claim the evidence does NOT establish…</dd></div>
  <div><dt>Next rung</dt><dd><strong>live_deployed</strong> — …what would have to happen…</dd></div>
</dl>
.status{border:1px solid var(--line2);border-radius:var(--rl);background:var(--ink2);overflow:hidden;margin-top:26px}
.status>div{display:grid;grid-template-columns:150px 1fr;gap:16px;padding:15px 20px;border-top:1px solid var(--line);min-width:0}
.status>div:first-child{border-top:0}
.status dt{font-family:var(--mono);font-size:var(--xs);text-transform:uppercase;letter-spacing:.1em;color:var(--fg3);margin:0}
.status dd{margin:0;font-size:var(--sm);color:var(--fg2);line-height:1.6;min-width:0;overflow-wrap:anywhere}
.status .limit{background:rgba(245,196,81,.05)}
.status .limit dt{color:var(--warn)}
@media(max-width:640px){.status>div{grid-template-columns:1fr;gap:6px}}

`LIMIT` is the load-bearing row and the hardest to write. It is not a caveat and not a disclaimer. It is the strongest claim a reader would reasonably infer, which the evidence does not support. Test: if LIMIT could be deleted without changing what a reader believes, it is not doing its job. GPSCoord's:

This establishes that the arithmetic is correct. It does not establish that anyone has used it, that it behaves the same in a browser other than the one it was tested in, or that a single other thing described on this site exists.

`Next rung` is optional but strongly recommended. SITES.md asks every surface to answer "what would have to happen for this to advance one rung?" — this is the row where that answer lives, and having it on the page rather than in a planning document is what keeps it from going stale.

3. The CTA pattern, keyed to rung — and why it is code, not a convention

SITES.md §0.7 is mechanical, so implement it mechanically. The verb table lives in the build, and a page that asks for something its rung has not earned does not get emitted:

const VERBS = {
  spec:           ["Read", "Challenge", "Implement"],
  in_tree:        ["Inspect the source", "Run the tests"],
  live_local:     ["Use it", "Reproduce it locally"],
  live_deployed:  ["Use the deployed artifact"],
  external:       ["See independent evidence", "Contribute another result"],
};

function cta(groupRung, label, actions) {
  const allowed = VERBS[groupRung];
  if (!allowed) throw new Error(`CTA group declares an unknown rung: ${groupRung}`);
  for (const a of actions) if (!allowed.includes(a.verb)) {
    throw new Error(`BUILD REFUSED — CTA "${a.verb}" is not available at rung ${groupRung}.`);
  }
  …
}
<div class="ctagroup">
  <div class="tag ok">live_local &mdash; run and checked</div>
  <div class="cta">
    <a href="/convert/"><span class="verb">Use it</span><span class="what">…</span></a>
    <a href="…"><span class="verb">Reproduce it locally</span><span class="what">…</span></a>
  </div>
</div>
.cta{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:14px}
.cta a{display:block;background:var(--ink2);border:1px solid var(--line);border-radius:var(--r);
  padding:20px;text-decoration:none;transition:border-color .2s,background .2s,transform .2s;min-width:0}
.cta a:hover{border-color:var(--acc-line);background:var(--acc-soft);transform:translateY(-2px)}
.cta .verb{display:block;font-family:var(--mono);font-size:var(--xs);font-weight:600;
  text-transform:uppercase;letter-spacing:.1em;color:var(--acc);margin-bottom:7px}
.cta .what{display:block;font-size:var(--sm);line-height:1.55;color:var(--fg2)}
.ctagroup+.ctagroup{margin-top:34px}

A mixed surface gets one group per rung, not one blended group. GPSCoord ships a live_local group ("Use it" / "Reproduce it locally") above a spec group ("Read" / "Challenge" / "Implement"), each under its own tag. That is the whole answer to "the converter works but nothing else does" and it needs no prose.

The claim tag above each group is shared:

.tag{display:inline-block;font-family:var(--mono);font-size:var(--xs);text-transform:uppercase;
  letter-spacing:.06em;color:var(--warn);background:rgba(245,196,81,.06);
  border:1px dashed rgba(245,196,81,.35);border-radius:6px;padding:5px 11px;margin-bottom:16px}
.tag.ok{color:var(--data);border-color:rgba(90,209,200,.35);background:var(--data-soft)}

Dashed + --warn = unbacked. Solid + --data = has a witness. The same two states carry the pricing block, the "proposed" labels, and the rung chips.

4. The honesty machinery — the part that matters most

Adopted from `toolboxhvac.com`, which is the portfolio's reference for this. The band, the chip and the CTA table above are worth little on their own, because a person can type a wrong value under a correct-looking chip and nothing notices. That is not hypothetical: it is exactly how GPSCoord shipped a fabricated coordinate through a claim audit that reported every number had a witness.

4.1 Generate pages from frozen records. Never hand-copy a value.

records/*.json   →   build-site.mjs   →   index.html
                          ↑
                     recomputes every published value with the SAME code
                     the product runs, and exits non-zero on disagreement

toolboxhvac does this with 1,298 frozen PT rows and 24 verified fault codes; GPSCoord does it with 8 reference coordinates. The count does not matter — the direction of dependency does. A page cell must be computed, and the record is what it is checked against. If the page is the source, nothing can audit it.

4.2 A publication gate that reads the artifact, not the source

launch-gate.mjs reads the emitted HTML. Both projects check:

  • release identity — package.json == the record == the stamp printed on the

page. toolboxhvac adds release.json and per-asset SHA-256.

  • no unrendered `{{TOKEN}}` survives into the artifact.

  • the review ledger cannot lie — every gate is pending or approved, an

approved gate without its evidence / reviewer / date fields is refused, and while any gate is pending the artifact must carry the beta banner (toolboxhvac) or must not claim the higher rung (GPSCoord).

  • no page advertises a `mailto:`.

GPSCoord adds four that generalise and are worth stealing:

  • a retraction blocklist. Every string removed by the claim audit is listed,

and the gate refuses any page that reinstates one — except inside the retraction paragraph, because naming the wrong value is what a retraction is. This makes a retraction structural rather than a promise. It fired on its first run: the fabricated coordinate was still living in a source comment that gets inlined into the shipped page.

  • the §0.7 verb table, enforced (§3 above).

  • every rung on the artifact is a real rung, chip text equals stored rung, and

no data-rung="" / "undefined" / "null" anywhere.

  • every function the UI calls is exported by the library. The converter

reached a browser once calling GPS.parseInput when the module exports parseCoordinateInput. Every output box rendered empty; the HTML looked perfect; nothing in any test failed. A page whose script throws is indistinguishable from a page that is merely quiet.

4.3 Prove the gate can fail

A gate nobody has seen refuse is an opinion. Break each one deliberately, in a throwaway copy, and record the result. GPSCoord's, all confirmed 2026-08-16:

BreakResult
Alter a frozen reference valueREFUSED
Change the arithmetic without updating the recordsREFUSED
Reinstate an audited-away claimREFUSED
Add a mailto:REFUSED
Offer "Use it" at rung specREFUSED
Mark a review gate approved with no evidenceREFUSED
Break version identityREFUSED
Call a function the library does not exportREFUSED

4.4 The deploy command re-proves the artifact

From toolboxhvac.com/wrangler.toml, and the comment is the lesson:

# Build command:  npm run test:launch
# That command regenerates site/ and re-proves the artifact before Cloudflare is
# allowed to serve it. A plain `npm run build` would deploy an unproven artifact.
name = "toolboxhvac-com"
pages_build_output_dir = "site"

pages_build_output_dir is pinned in the repo so it cannot drift; the build command is only settable in the Cloudflare dashboard, so it must be written down somewhere a person will look. Set the Pages build command to the gated script, never to `npm run build`.

4.5 Contact is a hosted form, never a mailbox

No mailto: anywhere — Travis's call, 2026-08-11. toolboxhvac solves it properly: the Formspree endpoint is declared once in the build so no page can invent its own, the form carries _next (return URL), _gotcha (honeypot) and a hidden field stamping the release it was sent from, ?ref= prefills the subject with the route the reader was on, the privacy page discloses Formspree as a processor, and the CSP must include `form-action 'self' https://formspree.io` or the browser silently refuses the POST.

The endpoint `https://formspree.io/f/xgawkeej` is toolboxhvac's. Another surface needs its own, or an explicit ruling to share it. [TRAVIS]

Until a surface has one, a GitHub issues URL is an honest correction channel and is what GPSCoord uses. It is a live URL that works, which is the actual requirement.

5. Density, and the trap in copying a number

toolboxhvac's landing page is 13,401 bytes with ~5.6 KB of inline CSS and zero JavaScript, and that is the target to aim at. Small and considered beats large and animated.

GPSCoord's landing came out at 25,299 bytes — 8.9 KB shell CSS, 16.4 KB markup. Stated rather than rounded down, because the gap is the interesting part: toolboxhvac puts its evidence on separate routes (/review-status/, /methodology/), and GPSCoord's landing carries its evidence table, status block, zero counts and retraction inline. That is a real architectural choice with a real cost, not an accident. If a surface is going to have more than one or two evidence artifacts, put them on their own route.

Two things that cost nothing and are worth doing everywhere:

  • strip CSS comments and indentation at emit. Keep the source commented; ship

it dense.

  • no inline `style=` in generated fragments. A repeated 180-byte style string

across eight cells is 1.4 KB for nothing. It cost 2.9 KB here.

Split the landing from the application. toolboxhvac serves / and /app/; GPSCoord serves / and /convert/. The landing then legitimately ships zero JavaScript, which is both faster and a stronger claim.

Route hazard, and it applies to any domain in this portfolio with a catch-all. gpscoord.com currently answers every path with the same homepage. Whether /convert/ will resolve after a deploy is unknown and must be checked on the served domain before anyone claims the app is deployed. That unknown is why GPSCoord's deployed_route_check gate is pending and its rung is live_local.

6. Traps this shell already pays for

  • `IntersectionObserver` never fires in a non-compositing renderer. If a

surface uses scroll reveals, IO must never be the only trigger — add a setTimeout fallback and a done guard. The cleanest fix is the one used here: do not animate on scroll at all. The reveal machinery was written, then deleted, and nothing was lost.

  • The 1px-gap grid trick leaves a phantom lit cell whenever the last row is

short: gap:1px over a container background shows the background through the empty tail. Draw the separators with each cell's own shadow instead — .grid>div{box-shadow:0 0 0 1px var(--line)} with the container background matching the cells.

  • Wide content scrolls in its own box, never the document. Tables and diagrams

go inside overflow-x:auto; verify documentElement.scrollWidth === clientWidth at 390 / 700 / 790 / 800 / 815 / 1000 / 1280 / 1600.

  • Echo `innerWidth` back inside the same call as the measurement. A resize

command that silently fails produces eight identical "passes" at the default width. That happened here: agent-browser viewport is not a command — it is agent-browser set viewport <w> <h> — and the failure was invisible because the error had been redirected to /dev/null.

  • A control that does nothing is worse than no control. Copy buttons need a

real execCommand fallback for non-secure contexts, and every interactive element must be exercised in a browser before the page ships. An alert() or a silently-dropped form is the exact defect this portfolio keeps finding.

7. Adopting this on another surface

  1. Copy src/shell.css. Replace only the block between TOKENS-START and

TOKENS-END. Keep the roles of --acc, --data, --warn.

  1. Add a records/surface.json with surface, layer, parent, question,

surface_rung, surface_rung_covers, verified_at, status.{statement, source,limit}, advance.{next_rung,requires} and a gates ledger.

  1. Emit the band, the status block and the CTA groups from that record, at

build time.

  1. Copy launch-gate.mjs. Fill in your own retraction blocklist from whatever

your last claim audit removed.

  1. Break each gate once and record that it refused.

  2. Point the Pages build command at the gated script.

Reference implementations: ProjectAmp2/toolboxhvac.com/ (the original, and richer — PWA, per-asset hashes, route index policy, a public review-status page) and ~/Projects/GPSCoord/ (smaller, and the source of every fragment quoted here).

8. The identifying animation — required on every surface

Ruled by Travis 2026-08-16: every site carries an identifying animation, above the fold or close to it. This is the one piece of motion the shell requires rather than permits.

It exists because the portfolio's problem was never that the pages were ugly — it was that they were interchangeable. A visitor arriving on the fourth of ~33 surfaces should know within a second that it is a different place in the same world. A logo does not do that. A moving thing that depicts what the site is actually about does.

8.1 What is fixed

Only two things, and the second one is not negotiable.

  1. There is one, and it is above the fold or immediately below it.

  2. It renders no data and asserts nothing. It is decoration that identifies. It is not

an instrument, not a status display, and no number inside it may appear as text on the page.

8.2 Why rule 2 is written in blood

gpscoord.com shipped a canvas globe — SphereRouter, nodes and edges and little routing vehicles. Good animation; it is the reason Travis asked for this section. Beside it the page displayed:

12 Active Pathfinders

That 12 was for (let i = 0; i < 12; i++) — the loop that creates the decorative vehicles. A canvas animation's internal constant was published for months as a live user metric. Lane P found it and deleted it.

So: the animation may not be a source for any figure on the page. If a number appears next to it, that number comes from a frozen record and is recomputed at build time (§4.1), or it does not appear. The cheapest way to guarantee this is to make the animation take no inputs and expose no outputs — a closed decoration with no wire back into the DOM.

8.3 What is the site's own — and this is the whole point

What it depicts is unconstrained, and it should be the site's actual subject. The family resemblance comes from every surface having one, not from them looking alike.

SurfaceThe obvious subject
gpscoorda rotating globe with routes on it — restore SphereRouter
wrand.ccthe domain graph itself, drifting
deliberatican argument graph — attacks and supports settling
delegatica delegation chain, and one link refusing
agentromaticcontested work being allocated
opensentiencethe rung ladder, or laws being checked
computedriventhe road
webhost.systemsa machine booting somewhere that is not ours

Pick the thing the page is about. If the honest answer is "this is a specification and nothing runs," animate the specification — a structure assembling is truthful; a fake dashboard is not.

8.4 Constraints

  • Honour `prefers-reduced-motion: reduce` — render the first frame and stop. Not optional.

  • The page must remain readable with JavaScript off. The animation may require JS; the

content may not. If the animation fails to start, nothing else may be missing.

  • Never `IntersectionObserver` as the only trigger (§6). It does not fire in

non-compositing renderers, and an animation that never starts reads as a broken page.

  • It must not delay or obscure the `h1`. The question comes first; the motion is behind

or beside it.

  • Cheap. It runs on a phone. Cap the frame rate, stop when the tab is hidden

(document.hidden), and stop when it scrolls out of view.

  • CSS or inline SVG beats canvas where it can do the job — no JS, no context loss, and

prefers-reduced-motion is one media query. Reach for canvas when the subject genuinely needs it, as a rotating globe does.

8.5 The gate

Add to the publication gate (§4.2):

REFUSE if the landing page has no element marked data-identity-animation
REFUSE if any text node on the page equals a constant read from the animation source
REFUSE if the animation source contains a string that also appears in a frozen record

The middle check is the 12 Active Pathfinders defect, mechanised. It costs one pass over two files and it makes that class of error unshippable rather than merely known about.

Open in the interactive atlas