From 212bbc930d9d8d48aeba6c981c818f57db4b0ca8 Mon Sep 17 00:00:00 2001 From: JCarlosHidalgo Date: Thu, 10 Sep 2026 18:09:09 -0400 Subject: [PATCH 1/4] A budget's reason says why its number is right today, and check:routes fails one that keeps the history instead The eighteen reasons had grown into raise logs: about 78,000 characters, "Raised" ninety-five times, and four of them closing on a note that every figure above it named a number the route no longer had. A contributor arguing the next raise read a history nothing held. Each reason now states what the route carries and what moves its number, and `reasonProblems` fails a reason that names a budget figure, says "raised" or "lowered", or runs past `REASON_MAX_CHARS`. Against the previous declarations it fails sixteen of the eighteen. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01TVMSZHMunk5kC54Voh5RsH --- scripts/check/arena/AGENTS.md | 2 +- scripts/check/arena/check-routes.test.ts | 21 +- scripts/check/arena/check-routes.ts | 951 +++++------------------ 3 files changed, 219 insertions(+), 755 deletions(-) diff --git a/scripts/check/arena/AGENTS.md b/scripts/check/arena/AGENTS.md index d29e0184d..304ec2444 100644 --- a/scripts/check/arena/AGENTS.md +++ b/scripts/check/arena/AGENTS.md @@ -29,7 +29,7 @@ The domain for a gate that reads two or more layers at once, or the repository r | `check-consumer.ts` | the `arena-to-prod` command a package ships stops doing what the npm page says it does, run against a real fixture project rather than reasoned about. It assembles each package, installs it into a temporary project, runs the CLI and reads what came out: the theme sheet's import chain, `"components": "auto"` resolving to the sheets a source actually needs, a component name the package does not ship being reported rather than silently dropped, and a source naming a symbol this package does not export resolving to nothing, since no alias exists to soften it. `DOCUMENTED_LIST` reads the sheet list back out of the shipped `README.md`, so the page and the command cannot drift apart. | | `check-architecture.ts` | a layer withdraws one of the architecture promises the consumer branch makes on its behalf. **Arena is adopted on a tree whose last nodes tell a project it may server-render, prerender or embed as a fragment, and which dependencies each answer costs**, and every one of those answers is a property of these sources rather than a policy anybody stated: a browser global read while a module evaluates throws during a server render at import time rather than at paint, `useLayoutEffect` warns there and is the classic tell, an import outside `ENVELOPES` is a framework decision the library made on a consumer's behalf and arrives as a dependency they did not choose, and `@angular/router` stops being optional the moment anything outside the metadata entry point imports it, which turns an install-time promise into a run-time break. Two escapes are accepted because the layers already use them and both are correct: a read inside a function, an effect or an after-render hook, and one behind a `typeof` guard. Reading goes through the same lexer `check-docs.ts` uses, twice over: the pass that reads an import specifier blanks comments and keeps strings, since a specifier IS a string, and every other pass blanks both, since a global named inside a string is prose. Suites, demos and build outputs are out of scope, because a suite spawns a browser on purpose. | | `check-vocabulary.ts` | a document states a naming convention no file in the tree answers to, cites a sibling by a bare FILE name nothing carries, which `BARE_FILENAME` reads as a name carrying an extension so a bare symbol or component name is nobody's claim here, tells a reader to run an npm script `package.json` does not declare, names a `README` on the branch whose one filename is `AGENTS.md`, or hands over a `find` whose pattern matches nothing where it looks. Each of the five had a live defect the day it landed, and every one had stood green: the claim is prose and the subject is the tree, so nothing was comparing them. A convention is the expensive one, since a reader hunting for the file it names cannot tell a stale document from their own mistake. `NAMED_BUT_NOT_HERE` carries what is named and correctly absent, the consumer's own files and what the assembled package ships, and `README_MEANS` the documents that mean the page npm shows; a deliberate absence is read from `check-citations.ts`'s own map rather than declared twice. Reach is the whole tree for the same reason `check-citations.ts`'s is: a gate names the shape it reads, never the shape it sits beside. | -| `check-routes.ts` | a declared reading route costs more than its budget, names a stop that reaches no document, or has fallen so far under its budget that the number stopped applying pressure. **A route is what a reader pays PAST the page it opens with.** `ENTRIES` declares each branch's router once, with a budget of its own, because a router that seven routes carried as their first stop was charged seven times and argued seven times, and moving a paragraph out of one reported seven stale allowances: neither number was ever about the reader. `ROUTES` is the registry of what is paid after that, and a stop is a pathspec charged for the **largest** file it reaches, so a route is measured at the worst case a reader can meet rather than at an average nobody experiences. **A route whose reader answers a question and walks one way declares `branches` instead of `stops`, and is charged its worst branch**, since a node only some answers reach is not a page every reader opens; the report names the branch that spends the budget. A route with `stops` alone is one unnamed branch, so both shapes take one measurement path. It is the only gate that measures a document by what reading it costs somebody else, which is why every budget carries its reason as a string: a route grows by a paragraph that reads as an improvement at the place it is written. | +| `check-routes.ts` | a declared reading route costs more than its budget, names a stop that reaches no document, has fallen so far under its budget that the number stopped applying pressure, or carries a reason that is a history rather than a reason. **A route is what a reader pays PAST the page it opens with.** `ENTRIES` declares each branch's router once, with a budget of its own, so a router shared by every route is argued for its reader and not once per route. `ROUTES` is the registry of what is paid after it, and a stop is a pathspec charged for the **largest** file it reaches, so a route is measured at the worst case a reader can meet. **A route whose reader answers a question and walks one way declares `branches` and is charged its worst branch.** Every budget carries its reason as a string, because a route grows by a paragraph that reads as an improvement where it is written; `reasonProblems` holds that string to why the number is right today, so it names no figure, says no "raised" and stays under `REASON_MAX_CHARS`. | | `check-community.ts` | the surface a stranger meets before any code stops holding: a declared outward-facing file nobody wrote, the governance policy stated in a second document or in none, a contact link naming a path the default branch does not carry, or a `context7.json` that has drifted from the router. `OUTWARD` names each file with the reason it exists, because none of them is reachable from the router and nothing else would notice one going. The two failures it was written for are both silent: a repository that reads as maintained and answers 404 to the reporter who followed its own contact link, and an indexed document the consumer branch never meant to hand over. Context7 has no router, so what it indexes is what it hands back, and every contributor document reaching the index is a contributor answer to a consumer question; an exclusion covering nothing fails as a stale allowance, in both directions. **Which names it guards is derived and never listed**: `contributorBasenames()` takes every document the walk finds, drops what a build emitted, what the config already excludes by folder, what `check-docs.ts:isConsumerDocument(repoRelativePath)` calls the other branch's, what `OUTWARD` sends to a stranger, and the one README that belongs to neither. The list it replaced named four while the tree carried six, and the two it was short of were a chart family's own tour and a page written the same session, both of them indexed and neither of them for a consumer. Every string in `rules` is required to be a span of `SKILL.md`, compared with runs of whitespace flattened, so the copy cannot go quietly false and rewrapping a paragraph is not a failure.. **It also holds the file to the schema it declares**: that schema sets `additionalProperties` to false, so a key outside its set is not one field ignored but a document that fails to validate whole, and what gets indexed is whatever the service falls back to. The set is written here because the schema is published rather than vendored, and a key it gains is a key this list has to gain with it | | `check-site.ts` | the published site stops holding to what it claims: a page pointing at an asset the output does not carry, a page the manifest declares and the build dropped, one published that nothing declares, a module one of its pages imports and the output does not carry, a sitemap missing an entry, or a CNAME for another domain. The first is load-bearing and `intro/AGENTS.md` says why nothing else catches it: a broken path fails silently in both directions, and an unstyled page that fits its box passes a look. The module half is that assertion one notation in: a `src` resolving says nothing about what the entry imports, and an import that 404s throws before the entry runs, so the page keeps every href and renders nothing. The shape a static host breaks and a dev server hides is a directory answered at request time with a listing. It SKIPS when `dist/site/` is absent, since a walk of nothing reports every link resolved; what makes a dropped playground visible is `site-pages.ts` asking the component map rather than the filesystem. | | `check-icons.ts` | a `ph-` class name in the tree is not a Phosphor glyph, or is a glyph the weight beside it does not carry. A contract takes an icon as a string, and a string nothing checks is a typo that renders an empty box, with no error and no failing gate. The truth is read out of the installed `@phosphor-icons/web` rather than typed here, because a list of 1500 names ages the first time the package does, and finding no stylesheet is an explicit failure rather than a pass over nothing. `SCANNED_ROOTS` is the scope and `EXEMPT` is empty on purpose: a name Phosphor does not have is a typo rather than a case. | diff --git a/scripts/check/arena/check-routes.test.ts b/scripts/check/arena/check-routes.test.ts index 6c7caf182..64f93387a 100644 --- a/scripts/check/arena/check-routes.test.ts +++ b/scripts/check/arena/check-routes.test.ts @@ -1,7 +1,7 @@ /* The gate reads the real tree, so these drive its pure functions over a temporary one with the * shapes a real mistake takes: a stop reaching nothing, a budget broken from either side, a branch - * that costs nothing, a route opening with an entry nobody declared, and a walk that found no - * document. ROUTES and ENTRIES are asserted by name, the declared reads are asserted equal to the + * that costs nothing, a route opening with an entry nobody declared, a walk that found no + * document, and a reason carrying the history of its number. ROUTES and ENTRIES are asserted by name, the declared reads are asserted equal to the * routers and stops, and the whole cost of every route is asserted to be its entry plus its worst * branch, which is the sum a reader actually pays and the one a split model could quietly lose. */ @@ -13,7 +13,7 @@ import { join } from 'node:path'; import { ROUTES, ENTRIES, BUDGET_SLACK, node, branchesOf, documents, largestReached, measure, measureEntry, entryProblems, unreachedProblems, overBudgetProblems, staleBudgetProblems, zeroScanProblems, - routeProblems, + routeProblems, reasonProblems, REASON_MAX_CHARS, } from './check-routes.ts'; function tree(files: Record) { @@ -156,6 +156,21 @@ test('the floor and the ceiling reach an entry too, so a shared router cannot bl assert.match(staleBudgetProblems(entry.name, 2_000, total)[0] ?? '', /An allowance is not an exemption/); }); +test('a reason states why its number is right today, never what it was, and stays short', () => { + assert.deepEqual(reasonProblems('test-route', 'the stops measure this with room for one section'), []); + assert.match(reasonProblems('test-route', 'x'.repeat(REASON_MAX_CHARS + 1))[0] ?? '', + /is a raise history read by every contributor/); + for (const figure of ['44,600', '44_600', '44600']) + assert.match(reasonProblems('test-route', `the route costs ${figure} at most`)[0] ?? '', + new RegExp(`names the figure ${figure}`)); + assert.deepEqual(reasonProblems('test-route', 'one component, three stops and a 430px viewport'), [], + 'a small count or a measurement is not a budget figure'); + assert.match(reasonProblems('test-route', 'Raised again when the page grew')[0] ?? '', + /says "Raised"/); + for (const one of [...ENTRIES, ...ROUTES]) + assert.deepEqual(reasonProblems(one.name, one.reason), [], `${one.name} carries a history`); +}); + test('a route opening with an entry nobody declared is reported rather than charged from its second page', () => { assert.deepEqual(entryProblems(route()), []); const problems = entryProblems(route({ entry: 'nobody' })); diff --git a/scripts/check/arena/check-routes.ts b/scripts/check/arena/check-routes.ts index 239b3ddbf..4bb926219 100644 --- a/scripts/check/arena/check-routes.ts +++ b/scripts/check/arena/check-routes.ts @@ -5,8 +5,8 @@ * reaches is what the route is charged, so the figure is the worst case a reader can meet rather * than an average nobody experiences. A budget the tree has fallen far under fails as a stale * allowance, the shape SIZE_ALLOWANCE has in check-docs.ts, so decomposing a document returns the - * pressure instead of ending it. The stops are this gate's declared reads, derived rather than - * written a second time. */ + * pressure instead of ending it. A reason says why its number is right today, so it names no + * figure, carries no raise and stays short: the history of a budget is the commit that moved it. */ import { readFileSync } from 'node:fs'; import { join } from 'node:path'; @@ -18,6 +18,12 @@ import { repoRoot as root } from '../../lib/arena/repo-root.ts'; export const BUDGET_SLACK = 0.85; +export const REASON_MAX_CHARS = 1_200; + +export const BUDGET_FIGURE = /\b\d{1,3}(?:[,_]\d{3})+\b|\b\d{4,}\b/; + +export const HISTORY_WORDS = /\b(?:raised|lowered)\b/i; + export type Entry = { name: string; router: string; @@ -46,53 +52,18 @@ export const ENTRIES: Entry[] = [ router: 'skills/design/SKILL.md', budget: 18_600, reason: - 'the page every consumer route opens with, and the reason it is declared here rather than as ' - + 'the first stop of each of them: a paragraph added to a router that seven routes shared was ' - + 'charged seven times and argued seven times, and moving one out reported seven stale ' - + 'allowances. Neither number was about the reader. What a route pays past this is its own, ' - + 'so the two move independently and each is argued where it is spent. The number is what the ' - + 'router costs today with room for one question, and a question earns its place here only if ' - + 'it is asked before a builder knows which situation they are in; everything asked after that ' - + 'belongs to the node that asks it. Raised from 15,700 when the cold start tree gained ' - + 'the two questions that decide an install rather than a screen, and the router had to ' - + 'say so: a row a reader matches on is how they learn a node exists at all, and a tree ' - + 'whose last nodes are unreachable from the page every route opens with is one only ' - + 'somebody who already knew about it can walk. Raised again to 16,300 when the table ' - + 'stopped asking one question twice. Two rows asked whether a component exists and both ' - + 'answered a no with the media register, so a builder whose component is merely missing ' - + 'was routed to a page about photo walls and feeds. One row asks it now and names the ' - + 'three shapes a no has: a different register, a decision the surface page states, and a ' - + 'component nobody has added, which is the other branch. Dropping the second row also ' - + 'puts the kernel row back beside the one that answers with the same document. Raised ' - + 'again to 17,100 for the two rows a consumer holding a defect needs. The accessibility ' - + 'row named the per-component behaviour file beside the pattern, and that file is in ' - + 'EXCLUDED_PATTERNS in scripts/lib/arena/package-assembly.ts, so the row sent a reader ' - + 'holding only the package to something the package does not carry; the pattern files ' - + 'themselves are exported by both builds and stay. The second row is the branch exit: ' - + 'CONTRIBUTING.md takes a document that sends a reader somewhere empty and a question the ' - + 'documentation did not answer, and no page on this branch named it, so a consumer who ' - + 'proved the defect was Arena\'s had nowhere to put it. Raised again to 17,400 for the ' - + 'row naming the repertoire. Arena is built with bun, React 18 and Angular 22, and every ' - + 'command on this branch was written in the runner Arena happens to use, so a reader with ' - + 'no row to match on learned a toolchain by copying one: what the row names is the page ' - + 'that separates what Arena supports from what Arena is made of, and it is asked before a ' - + 'builder knows which situation they are in, which is what earns a row here rather than ' - + 'in the node that would otherwise own it. Raised again to 18,000 when the rules list ' - + 'stopped being written by hand. Every statement of the rules is emitted from one ' - + 'declaration now, so each rule says whether a gate reads the reader\'s own sources for it, ' - + 'and the rule the page had stated only in its delivery paragraph joined the list it ' - + 'belongs to. What the number buys is a claim that cannot go stale: this page said no gate ' - + 'reads your application while the audit already did, and nothing failed, because the ' - + 'sentence and the check had no common source. Raised again to 18,600 when this page was ' - + 'rewritten into the register check:register holds, which is the first measurement of what ' - + 'that register costs: 661 characters on a page whose every sentence now carries one claim, ' - + 'runs under thirty words and names its own subject. The saving a rewrite was expected to ' - + 'return did not arrive here and the reason is worth the row: naming a subject spends ' - + 'characters, splitting a sentence spends a verb, and the subordination this page carried ' - + 'was holding claims rather than padding. What the number buys is measured on the gate ' - + 'rather than argued: thirty-six findings before and none after. A page whose prose is ' - + 'already flat has nothing left to spend, so the next raise here is a claim rather than a ' - + 'register.', + 'the page every consumer route opens with, declared once rather than as the first stop of ' + + 'each route, because a router charged to every route opening with it is argued once per route ' + + 'and never once for its reader. What a route pays past it is its own, so the two move ' + + 'independently and each is argued where it is spent. A question earns a row here only when it ' + + 'is asked before a builder knows which situation they are in: whether a component exists, how ' + + 'much of Arena a project takes, what it installs Arena with, where the appearance comes from, ' + + 'whether anybody outside the product has to find it, and where a defect goes once it is ' + + 'proved to be Arena\'s. Everything asked after that belongs to the node that asks it. The ' + + 'rules list is emitted from one declaration, so each rule says whether a gate reads the ' + + 'reader\'s own sources, and the prose holds the register check:register reads. A page whose ' + + 'prose is already flat has nothing left to spend, so what moves this number is a new question ' + + 'rather than a longer answer.', }, { name: 'contributor', @@ -100,29 +71,15 @@ export const ENTRIES: Entry[] = [ budget: 23_600, reason: 'the same argument on the other branch, and it is allowed to cost more because it is paid by ' - + 'whoever changes Arena and never by whoever uses it. It carries a routing table rather than ' - + 'a tour, so it grows by a row when a level appears and not by a paragraph when a level ' - + 'grows. Raised to 21,400 for a row that is not a level: what a consumer may build Arena ' - + 'into is a promise the sources keep rather than a directory anybody edits, so a ' - + 'contributor reaches it by the change they are making and never by the file they are ' - + 'opening. It is the one subject where the page a reader would look for does not exist, ' - + 'which is what the row buys. Raised to 22,400 for the two rows that are not indexed the ' - + 'way the rest of the table is. Every other row asks what you are changing, which a reader ' - + 'arriving with a symptom cannot answer yet: nine cold walks of this branch produced one ' - + 'that had two equally correct rows for a keyboard defect and picked by luck, and one that ' - + 'planned a component that already shipped in three layers, because no row asks whether it ' - + 'exists and the page that answers belongs to the other branch. A router indexed only by ' - + 'intent serves the reader who already has one, and that reader is not the expensive case. ' - + 'Raised to 23,000 for the fifth declared departure. The corpus a package carries is the ' - + 'reference tree shipped where the tree it is generated from already sits, so the ' - + 'departure above it reads as contradicted by the one below unless both are written ' - + 'down: a contributor who finds a prompt inside a tarball, and a page saying a copy ' - + 'inside the skill directory is refused, has two rules and no way to tell which one ' - + 'governs the change they are making. Raised again to 23,400 when the artefact list stopped ' - + 'saying three. It had named two npm packages since a third shipped, and a fourth landed ' - + 'beside it: a contributor reading which of them a change moves was reading a list that ' - + 'answered for half of them, and what a release moves is the one thing on this branch that ' - + 'fails in silence when it is forgotten.', + + 'whoever changes Arena and never by whoever uses it. It carries a routing table rather than a ' + + 'tour, so it grows by a row when a level appears and not by a paragraph when a level grows. ' + + 'The first rows are indexed by symptom rather than by what is being changed, because a reader ' + + 'holding a defect cannot answer that question yet, and a router indexed only by intent serves ' + + 'the reader who already has one. One row is a promise rather than a directory, what a ' + + 'consumer may build Arena into, since no page a contributor opens by path states it. The page ' + + 'also names every artefact a release moves and every departure from the convention it answers ' + + 'to, because a forgotten artefact fails in silence and an unwritten departure is one the next ' + + 'reader repairs.', }, ]; @@ -141,55 +98,17 @@ export const ROUTES: Route[] = [ budget: 32_800, reason: 'the route every build takes, and the only one paid per screen rather than per project. The ' - + 'layer-neutral index is deliberately NOT a stop: it answers whether a component exists at ' - + 'all, which is a question a builder who knows what they are reaching for never asks, and ' - + 'charging every build for it cost half again what the route costs now. The layer stop is a ' - + 'directory naming every component under its category rather than one index describing all ' - + 'fifty-nine, so a build is charged the one category it reaches into: splitting it took a ' - + 'quarter off this number, and naming the components in the directory is what keeps the ' - + 'extra stop from costing a guess. The number is what the stops measure today with room for ' - + 'one component to grow, and it is the ceiling a new rule on the consumer branch is argued ' - + 'against. Raised to 44,600 when the router gained the third decision a project takes before ' - + 'its first screen, whether anybody outside the product has to find it. The router is a stop ' - + 'on five consumer routes now, so a question added to it is charged five times and this is ' - + 'the one that pays per screen; what makes it worth the build route is that the answer is a ' - + 'peer dependency and a layer rather than a component, and a build that starts before it is ' - + 'settled is a build that has already chosen. Raised again to 45,400 when the router started ' - + 'naming the pieces a page is composed from. It said the shape of a screen is the project\'s ' - + 'and the silence about the shape had spread to the inventory: Arena ships css/page.css, ' - + 'css/rhythm.css and a fill-page role, documents all three on each layer\'s npm page, and ' - + 'named none of them where a builder decides how a screen goes together. What that costs is ' - + 'measured on a consumer bench, where a page carries a shell written by hand and a floor ' - + 'nobody paints, so a dark palette leaves the canvas the browser\'s own below the first ' - + 'viewport. This is the route that pays per screen and the one where it bites, because a ' - + 'screen is composed before a component is chosen, and a build that starts without the ' - + 'pieces writes them again. Raised again to 45,600 by three cold walks of the consumer ' - + 'branch, one per entry point. The two that reached this router painted the floor through ' - + 'the role and the one that never did took a palette step instead, so the row carries what ' - + 'separates them: a role is what a style plugin re-answers, and a page painted through one ' - + 'follows the skin it is handed. It stops there rather than forbidding a step, because the ' - + 'manifests spend both and a rule stricter than the tree is one the tree refutes. Raised ' - + 'again to 46,000 when the router gained the question that comes before the other three, ' - + 'what Arena ships at all and how much of it a project takes. This is the route that pays ' - + 'per screen and it is where not knowing bites: a builder who has never been told that ' - + 'rhythm-group, rhythm-component and rhythm-section exist writes a margin instead, and the ' - + 'branch named those three nowhere while spelling the classes that read them. The same ' - + 'silence covered the two density classes, which appeared once on the whole branch inside a ' - + 'rules bullet and never as something a product can offer. Two of the table\'s longest rows ' - + 'became pointers to pay part of it, which is the trade this budget is for: the router names ' - + 'the question and the document carries the answer. Raised again to 46,200 when the router ' - + 'started naming the contributor branch by its URL. A relative path to it resolves in a ' - + 'clone and nowhere else, and this router is served on the site and read inside a package, ' - + 'so the one sentence sending a reader to the other branch was a 404 for everybody who did ' - + 'not already have the tree. Raised again to 32,700 when a grid showing part of a list ' - + 'started saying how big the list is. ArenaTable is the largest prompt this route can ' - + 'reach and every screen that takes a table pays for it, which argues for keeping the ' - + 'rest of that page short rather than for leaving the member out: a paged table is ' - + 'already a subset of a grid, the source page its binding cites applies aria-rowcount ' - + 'and aria-rowindex to exactly that condition, and a consumer nobody tells carries the ' - + 'defect into every table they build. The arithmetic is the half that cannot live in a ' - + 'member description: the header row takes index 1, so an offset counted from 1 is off ' - + 'by one in a way no gate of ours and no gate of theirs reports. Raised again to 32,800 when the component descriptions were rewritten into the register check:register holds. A description is emitted into the layer-neutral index, into the category index this route charges, and into the type declarations both packages ship, so it is consumer prose that happens to live in a contract. The navigation category is where the raise lands, because the components whose descriptions carried the most subordination are its own: a nav item, a collapsible and a bottom-nav item each said what the parent settles, why it is not a member, and which other component has the same arrangement, in one sentence. Fifty-six findings over thirty descriptions before, and none after, for twenty-three characters on the charged stop.', + + 'layer-neutral index is deliberately not a stop: it answers whether a component exists at ' + + 'all, which a builder who knows what they are reaching for never asks. The layer stop is a ' + + 'directory naming every component under its category rather than one index describing all of ' + + 'them, so a build is charged the one category it reaches into, and naming the components ' + + 'there is what keeps the extra stop from costing a guess. The largest prompt this route ' + + 'reaches is the table\'s, and every screen taking a table pays for it, which argues for ' + + 'keeping that page short rather than for leaving a member out: a paged table has to say how ' + + 'big its list is and where the page starts in it, and the header row taking the first index ' + + 'is arithmetic no member description can carry. The number is what the stops measure with ' + + 'room for one component to grow, and it is the ceiling a new rule on the consumer branch is ' + + 'argued against.', }, { name: 'consumer-install', @@ -199,154 +118,16 @@ export const ROUTES: Route[] = [ budget: 51_700, reason: 'paid once per project rather than per screen, so it carries what the build route may not: ' - + 'the config file, the command, the theme surface and the two measurements. It shares the ' - + 'router with the build route, so the two move together whenever that page does. The number ' - + 'is argued up rather than held down when a correction lands on the npm page: a pre-paint ' - + 'script that answers the device as well as storage is longer than one that does not, and ' - + 'being right is what the page is for. It is what the stops measure once the appearance a ' - + 'project adopts stopped being picked from a page rather than written in a directory. ' - + 'Raised from 36,000 when the audit gained a scope: a consumer has to be told which rule ' - + 'relaxes inside a style plugin directory and which does not, and a report that is silent ' - + 'about where it stops applying is one nobody can act on. Raised again when the third ' - + 'generated file gained a name on the page: a file nobody is told to import is a plugin ' - + 'whose CSS reaches no page, and the failure is silent, so naming it is worth more than the ' - + 'lines it costs. Raised again when the page stated which register the library is for: what ' - + 'this route decides is whether to adopt Arena at all, and a consumer whose product is a ' - + 'media or a consumer one otherwise learns that the components run out by building the ' - + 'screen, which costs more than every stop on this route put together. Raised again when ' - + '--strict stopped being one switch: a project whose brand does not clear 4.5:1 has to be ' - + 'told which kinds it can still hold, and a flag whose kinds are not named on the page is ' - + 'one nobody can type. Raised again when the config gained a key for a brand whose mark is ' - + 'a gradient: what the key buys is that --strict=audit stops failing over the most ' - + 'recognisable thing about a product, and a key nobody is told about is one nobody ' - + 'declares, so the report it silences goes on being suppressed a line at a time. Raised ' - + 'again when the router\'s description named the style kernel: the description is the whole ' - + 'of what an agent reads before it decides whether to load Arena at all, and one that stops ' - + 'at the palette describes a system that cannot be reskinned. Raised again when the ' - + 'question table gained the three rows the kernel answers: this route and the build route ' - + 'share the router, so a project that has to decide its appearance before the first screen ' - + 'pays for those rows here too. Most of what they cost was bought back out of the paragraph ' - + 'that used to describe a style plugin on the router, which the kernel document owns now. ' - + 'Raised again when a cold agent walked this route and reported that the page taught it the ' - + 'skin is a palette and three fonts: it would have shipped somebody else\'s appearance with ' - + 'its own colours in it and believed that was the job, so the section says what a palette ' - + 'is not and names where the decision is made. Raised again when the same walk found what ' - + 'the page ships and never names: five projection markers whose slots fail in silence, and ' - + 'the sheets under css/ a reader picking a depth is not offered. A file a package ships and ' - + 'a page never mentions is one nobody imports, and this route is where a consumer meets ' - + 'every one of them. Raised again to 46,000 when check:exports landed and reported twelve ' - + 'symbols in that same shape, the modal contract among them: Arena tells a consumer a ' - + 'lightbox is theirs to write, ships the tested focus trap its own dialogs run on, and the ' - + 'page disclaimed it by omission, so a cold walk planned a second trap written from memory. ' - + 'Raised again to 46,600 when the main landmark and its skip link shipped as a pair joined ' - + 'by a constant id: the id is the one thing a consumer needs in order to write a second ' - + 'route into the content or an anchor of their own, and a pair whose id is undocumented is ' - + 'a pair a consumer coordinates by reading the source of a package they installed. ' - + 'Raised again to 49,400 when the metadata provider shipped behind a second entry point: ' - + 'the import path is not derivable, the router peer is optional and a consumer who does not ' - + 'know that installs it for nothing, and the whole value of the provider is a default a ' - + 'reader has to be told about, since a route born noindex is a surprise the first time it ' - + 'is right and a defect every time it is not. What the section deliberately does NOT carry ' - + 'is why a bundler resolves before it eliminates, which is the mechanism behind the second ' - + 'entry point and belongs to frameworks/PACKAGING.md, on the branch whose reader can act on it. ' - + 'What buys a raise here is always the same argument, that being right is what the page is ' - + 'for, and what makes this one binding is that a gate fails the page now rather than a ' - + 'reader discovering it. Raised again to 49,900 when the router started asking whether the ' - + 'product has to be found at all: this route is where the answer is acted on, since a yes ' - + 'names an entry point to import and a peer to install and a no leaves both alone, and the ' - + 'question reaching a project only through the page it is answered on is the shape that put ' - + 'the provider out of reach in the first place. Raised again to 52,300 when the page opened ' - + 'on what a project is adopting rather than on what to install: the components carry an API ' - + 'declared once for both layers, a behaviour pattern each of them binds, a kernel whose ' - + 'answers are the appearance, and a head a product that has to be found asks for and no ' - + 'other pays. A reader arriving from the registry has read nothing else, and one who learns ' - + 'those four by reaching the section that details each has already decided against three of ' - + 'them; the section is shared between both pages, so what it says is said once and charged ' - + 'to the larger. Raised again to 53,100 when the router started naming the pieces a page is ' - + 'composed from, which the npm page already documents sheet by sheet. The table was the one ' - + 'place css/page.css and css/rhythm.css were named, and a reader reaches a table by being ' - + 'sent to it: the router pointing at those two rows is what turns a stylesheet list a ' - + 'consumer finds into one they arrive at. Raised again to 53,700 by the same argument the ' - + 'pre-paint script bought the first time: it took two palette names as two literals, so a ' - + 'project declaring three or four could not state its first visit there at all, and the one ' - + 'name it did take was picked by hand where the theme surface takes the first palette of the ' - + 'matching polarity. The snippet takes the list and the default now, which are the two values ' - + 'that surface already takes, so the page costs more and the two cannot disagree. Raised ' - + 'again to 54,200 when the rhythm row started spelling its own classes. It named the three ' - + 'steps as bare modifiers, which reads as a custom property rather than as a class, and three ' - + 'cold walks read it that way: one wrote a gap variable that does not exist, one guessed the ' - + 'class and said so, and the one that got it right had it from a component prompt that ' - + 'happens to spell it. A row naming what a class does and never the class is a row a reader ' - + 'finishes and still has to guess from, so it spells all nine and says which belong to a ' - + 'stack and which to a row. Raised again to 54,600 when the router gained the inventory ' - + 'question. This page is where every part is documented in detail, sheet by sheet and export ' - + 'by export, and detail is not the same as knowing the part is there: a reader reaches a ' - + 'table by being sent to it, and the two tables on this page are long enough that what a ' - + 'reader does not already suspect exists is what they skip. The question sends them, and the ' - + 'inventory it links names the part in one line and leaves the detail here, so nothing on ' - + 'this page is said a second time. Raised again to 57,200 when the page gained the ' - + 'half of a page that is not a component. Three cold walks entered this library by three ' - + 'different doors, and only the two that reached the repository painted a floor: the page ' - + 'said the shell fills the window and never said what colour it takes, so the walk holding ' - + 'this page alone shipped a screen standing on the browser\'s own canvas, and painted its ' - + 'text out of the compatibility aliases in css/colors.css, because no role was named ' - + 'anywhere it was reading. The same walk could not learn that either density class exists ' - + 'at all. A class a consumer writes and a floor only a project can apply are both shipped ' - + 'things, and a shipped thing this page does not name is one a consumer replaces with a ' - + 'rule of their own and never learns they had. ' - + 'Raised again to 61,000 by six cold walks, two per entry point across both layers. This ' - + 'is the only route a reader arriving from the registry can take, and what it lacked is ' - + 'what they alone could not reach: how a level holds text back, which is why a bare ink- ' - + 'muted paints a caption at the strength of body copy; the column and the rhythm as names ' - + 'rather than as a stylesheet table four hundred lines down; the shape of the miss the ' - + 'group step replaces, which a walk reading only this page wrote by hand inside a table ' - + 'cell; that the band carries no block air, which every walk invented a padding for; and ' - + 'the behaviour contracts, which this package now ships because the sentence sending a ' - + 'builder to bind one pointed at a directory no tarball carried. Raised again to 62,500 ' - + 'when the page said what a TypeScript project declares before it can import a stylesheet. ' - + 'The three import lines it shows are a bundler\'s idiom and not the compiler\'s: a .css ' - + 'specifier resolves to no module, so a strict build stops at the file that wires Arena in, ' - + 'and three cold walks of this route wrote a declaration file of their own to get past it. ' - + 'It also carries what the shared column region gained, the row as anything laid across and ' - + 'the wrapper a growing child needs when that child is a component this package draws. ' - + 'Raised again to 47,400 when the install section named the three package managers rather ' - + 'than the one it showed. The layout that would break a package making flat-tree ' - + 'assumptions is the strict one, so the page says the peers are declared rather than ' - + 'hoisted and that the shipped command resolves the icon font through a symlinked store. ' - + 'It was verified by installing the packed tarball under that layout and running the ' - + 'command, which is the evidence a claim about somebody else\'s tool needs. Raised again to ' - + '48,000 when the icon sheet stopped being half a promise. The page says the sheet holds ' - + 'every glyph the project draws and every glyph Arena draws for it, and the glyph COUNT is ' - + 'what a project is told to cut a self-hosted font subset to, so a list padded with names ' - + 'that appear in a sentence about a member rather than in a render is the wrong list to cut ' - + 'to. Measured across eight twin pairs of consumer benches, where the Angular half carried ' - + 'between 8 and 18 rules its React twin did not for the same screens and the same config. ' - + 'The package ships the list its own renders draw now, and the page has to say so, because ' - + 'a reader who is told the two halves are answered the same way has no reason to trust ' - + 'either. Raised again to 49,000 when the config block stopped omitting the key that ' - + 'decides the appearance. The block is announced as the whole file and a reader copies it ' - + 'whole, so a project meaning to look like itself shipped the answers this package installs ' - + 'with and nothing on the way said so: the consequence was stated sixty lines below the ' - + 'block, which is past where somebody copying stops reading. The same raise pays for the ' - + 'flag table saying what --src names and what it does not. The command resolves a declared ' - + 'style plugin from the config and walks it wherever it lives, so the flag names the trees ' - + 'a project owns and nothing else, and the page says so because a reader who has to be told ' - + 'to pass a directory the config already declares is a reader being asked to hold the tool ' - + 'together. Raised again to 50,400 when the page stopped documenting the runner Arena ' - + 'happens to be built with as the one a consumer needs. The command it tells them to run ' - + 'was written in that runner and nowhere else, and the shipped file is a Node program ' - + 'reading three node modules, so a reader on this route learned a dependency Arena does ' - + 'not have by copying the only line that names one. The region carries the two axes a ' - + 'reader cannot act without, what runs the command and how the package is loaded, each ' - + 'with the evidence behind it, and leaves the rest of the repertoire to the page that ' - + 'owns it: a project that cannot load a module has to learn it here rather than at the ' - + 'first import, and one that can is spared the other four axes on the route it pays per ' - + 'project. Raised again to 51,400 for the rows naming where the language is. This is the ' - + 'only route a reader arriving from the registry can take, and the package they installed ' - + 'carries none of it: a page that never names the four ways to reach it ships a language ' - + 'nobody reaches. The rows also say that a corpus and the components it describes are two ' - + 'version numbers, since a served document that stops matching the package fails at ' - + 'nothing and is read by an agent anyway. Raised to 51,700 when both npm pages were rewritten into the register check:register holds. Two hundred and nine findings before and none after, for 177 characters on the charged stop. Thirty-seven of the sentences sit inside a @shared region, so they moved in generate-npm-pages.ts and landed on both pages at once; the rest are the half each layer writes for itself. The densest of them were the flag table and the audit paragraph, where a list of what a run reports ran as one sentence with four semicolons, which is the shape a reader scanning for one flag cannot scan.', + + 'the config file, the command, the theme surface and what each of them costs. It is the only ' + + 'route a reader arriving from the registry can take, and the package they installed carries ' + + 'none of the language, so a thing the package ships and this page never names is a thing ' + + 'nobody imports: an export, a stylesheet, a class, a constant id, a second entry point, a ' + + 'config key. Cold walks of this route rewrote each of those from memory when the page left it ' + + 'out. The config block is copied whole, so it states every key that decides the appearance; ' + + 'the install section gives the evidence behind each package manager and runtime it names; and ' + + 'a section shared by both npm pages is said once and charged to the larger. What moves this ' + + 'number is being right, since that is what the page is for, and what keeps it honest is that ' + + 'a gate fails the page rather than a reader discovering it.', }, { name: 'consumer-skin', @@ -356,57 +137,16 @@ export const ROUTES: Route[] = [ stops: ['skills/design/references/style-kernel.md'], budget: 17_700, reason: - 'paid once per project and never per screen, like the install route, which is what lets it ' - + 'carry the whole surface the kernel exposes rather than a pointer to it. What it buys is ' - + 'that a project stops learning the kernel from a normative contributor document that names ' - + 'gates and links a contributor roof, which is where four products learned it. The number is ' - + 'what the stops measure with room for the kernel to grow a section, and it moves when the ' - + 'kernel grows a question rather than when the document grows a paragraph: the differentiation ' - + 'table is a measurement over consumer benches, so it grows by remeasuring and not by ' - + 'writing. Raised from 24,000 by what a cold agent could not answer from it: which scale ' - + 'each role draws from, since the type does not say and one type covers four scales; that ' - + 'a reading floor refuses the build rather than reporting; and that the loudest two ' - + 'decisions a brand makes, a square corner and no shadow, have no step to alias. Every one ' - + 'of those was a guess it had to make, and a route that costs less by leaving them out ' - + 'costs more the first time somebody walks it. Raised to 29,500 when the router gained the ' - + 'ordering two cold walks proved was missing: the kernel was routed to from a question table ' - + 'and from a paragraph under a heading about delivery, while the one section shaped like a ' - + 'procedure began at step 1, pick a component. Both walks reached the kernel in time and ' - + 'both reported reaching it by reading the table top to bottom rather than by being sent, ' - + 'which is luck of format. The router is a stop on four consumer routes, so that fix is ' - + 'charged four times and this is one of them; the paragraph saying the same thing a second ' - + 'time is what was bought back first. Raised again to 30,500 when the router stated that how ' - + 'a screen is composed belongs to the project: Arena ships the pieces and no shape they must ' - + 'make, which is a decision about the product rather than a paragraph, and a skeleton handed ' - + 'down instead would have made the first impression of the kernel Arena\'s rather than the ' - + 'project\'s. Three buy-backs paid for it, the last of them an identity sentence restating ' - + 'two rules that have their own bullets. What the raise itself buys is that the route stopped ' - + 'sitting one character under its ceiling, where the next correction is refused by arithmetic ' - + 'rather than by judgement. Raised again to 31,300 when the router named the floor a page ' - + 'stands on. fill-page is a question the kernel asks and every answer to it reached a style ' - + 'plugin and never a page, because nothing on this route said the role exists or that Arena ' - + 'declares the polarity and paints no page of the project\'s: a skin decided without its ' - + 'floor is a skin decided down to the last surface and not the one under them. Raised again ' - + 'to 31,700 when the router gained the inventory question, which is worth its share here for ' - + 'a reason particular to this route: the kernel is the largest thing on the consumer branch ' - + 'and a project that meets it first meets it as the whole offer. Named as one part of eleven ' - + 'and the second of three steps, it becomes a decision a project can take or defer on ' - + 'purpose, and that deferring it is legitimate is something this route cannot say about ' - + 'itself. Raised again to 17,200 by the remeasurement this route\'s own reason reserves the ' - + 'raise for: the catalogue grew from four registers to eight, and three role groups that ' - + 'four products had answered identically stopped agreeing. Which surface is the page and ' - + 'which is the thing standing on it, whether a control and a field carry a drawn edge, and ' - + 'whether small text is the body face or the mono one each moved from the table of answers a ' - + 'project may inherit into the table of answers it has to make. A row in the second table is ' - + 'a decision this page is now obliged to name, and a page that kept the shorter version ' - + 'would be telling a project that a decision it has to make was already made for it. Raised ' - + 'again to 17,500 when the one exception to the scale rule gained its mechanism. The page ' - + 'named the type and page rhythm ladders as the case a plugin may move and then said nothing ' - + 'about how, so the only prose carrying the key shape sat inside a role description in the ' - + 'contracts, where the builder holding the question never reaches, and three readings of the ' - + 'same page were each defensible. An exception a page names and does not answer costs more ' - + 'than the paragraph answering it, because a reader either guesses or goes looking on the ' - + 'branch that is not theirs. Raised to 17,700 when this page was rewritten into the register check:register holds. Most of it paid for itself and one table did not: the column saying where the steps of a scale live answered eight of its twelve rows with the words "the same", meaning the file in the row above, so a reader who matched on one row held no other. The rows are grouped under the file each of them reads now, which costs about a hundred characters and makes every row answerable alone.', + 'paid once per project and never per screen, which is what lets it carry the whole surface ' + + 'the kernel exposes rather than a pointer to it, so a project stops learning the kernel from ' + + 'a normative contributor document. It states which scale each role draws from, that a reading ' + + 'floor refuses the build rather than reporting, which answers a project may inherit and which ' + + 'it has to make, and how the one exception to the scale rule is keyed. A cold walk without ' + + 'those had to guess each of them. The differentiation table is a measurement over the ' + + 'catalogue, so it grows by remeasuring and not by writing, and a row the catalogue moves from ' + + 'inherited to decided is a decision this page is obliged to name. The number is what the ' + + 'stops measure with room for the kernel to grow a section, and it moves when the kernel grows ' + + 'a question rather than when the document grows a paragraph.', }, { name: 'consumer-register', @@ -416,44 +156,16 @@ export const ROUTES: Route[] = [ stops: ['skills/design/references/media-register.md'], budget: 9_600, reason: - 'paid once per project like the skin route, and by a reader the router has just told to write ' - + 'their own markup. What it buys is that the honest sentence at the top of the router stops ' - + 'costing more than a dishonest one would: two cold walks were sent away from the component ' - + 'list correctly and then left to hand-roll a focus trap the package already ships, guess an ' - + 'ARIA contract that is written down in contracts/behaviour/, and rediscover that a photo ' - + 'wall is one line of CSS. Every part of the answer already existed and none of it was ' - + 'reachable from the sentence that created the need. The number is what the stops measure ' - + 'with room for one section, and it grows when the register gains a part to hand over rather ' - + 'than when the page gains a paragraph. Raised to 21,500 the first time that happened, on the ' - + 'walk that repeated against this page: it gained the component that reads the two media ' - + 'roles the page already told a reader to answer first, the split between an auto-fitting ' - + 'grid and a fixed-count wall, and the two component names that look like the answer and are ' - + 'not. It also lost a claim it should never have made: the page said Arena ships the hard ' - + 'parts of both patterns it names, and Arena ships the modal one and binds the feed one ' - + 'inside a component nobody can reuse, so the page says that instead. Raised again to 22,200 ' - + 'when the router started naming the pieces a page is composed from, which this reader needs ' - + 'more than any other on the branch: nothing they write is a component that arrives carrying ' - + 'its own floor, its own column and its own air, so a route that hands them the markup and ' - + 'not the pieces hands them a page to rebuild from nothing. Raised again to 22,600 when the ' - + 'router gained the inventory question and sent the pieces to a page of their own. This ' - + 'reader gets the most of both: the inventory is where they learn that the parts handed over ' - + 'are not only the two this page names, and the page document is where the floor, the column ' - + 'and the air stop being a sentence in a router and become the list their own markup is ' - + 'built from. ' - + 'Raised again to 24,300 when the register gained the viewer that opens a picture at full ' - + 'size. Six cold walks were asked for one and six wrote it, each inventing the same four ' - + 'answers: what dims the page behind it, whether the picture is contained or cropped, what ' - + 'a control laid over a picture nobody chose stands on, and whether the pattern still has ' - + 'to be bound when Arena ships no component to bind it. Every one of those is already a ' - + 'token or a contract, so the section costs a paragraph and replaces four guesses per ' - + 'project. Raised again to 25,300 when the section gained the box itself. It opened by ' - + 'saying every value a viewer needs is already a token and left the one value that is not ' - + 'unsaid, so walks sized the picture with a vh nobody chose. A fixed layer at inset 0 needs ' - + 'no length at all, and the unit that follows a phone\'s browser chrome is dvh, which is ' - + 'what the chosen number was compensating for.' - + ' The figures in this history predate the stop set being narrowed and the budget re-measured ' - + 'against what is left, so every one of them names a number this route does not have. The ' - + 'arguments travel and the numbers do not: what the budget is is the field beside this one.', + 'paid once per project, by a reader the router has just told to write their own markup. What ' + + 'it buys is that the honest sentence sending them away from the component list stops costing ' + + 'more than a dishonest one: without it a walk hand-rolls a focus trap the package already ' + + 'ships, guesses an ARIA contract that is written down, and rediscovers that a photo wall is ' + + 'one line of CSS. It names what Arena hands over instead of a component, the component names ' + + 'that look like the answer and are not, and the viewer that opens a picture at full size with ' + + 'the answers every walk otherwise invents: what dims the page, whether the picture is ' + + 'contained, what a control over it stands on, and which pattern it still binds. The number is ' + + 'what the stops measure with room for one section, and it grows when the register gains a ' + + 'part to hand over rather than when the page gains a paragraph.', }, { name: 'consumer-seo', @@ -463,44 +175,17 @@ export const ROUTES: Route[] = [ stops: ['skills/design/references/seo.md'], budget: 4_700, reason: - 'paid once per project like the skin and the register routes, and taken before the first ' - + 'screen for the same reason they are: the answer reaches the layer and the install rather ' - + 'than a component, so a project that settles it afterwards settles it against code it has ' - + 'already written. What it buys is that the metadata entry point stops being reachable only ' - + 'from the install route, four hundred lines into the longest page on the consumer branch, ' - + 'by a reader who has no reason to be looking for it. Every default it exists to surface is ' - + 'silent in the direction that costs: a route is born unindexed and says nothing about it, ' - + 'and an application with no origin publishes no canonical rather than a wrong one, so both ' - + 'are correct until a screen is missing from a result nobody thought to check. The page ' - + 'carries the question, the three properties and the asymmetry between the layers, and it ' - + 'carries no export table, because the layer\'s own page is where anything a package ships ' - + 'is documented. The number is what the stops measure with room for one section, and it ' - + 'grows when Arena gains something to write into the head rather than when the page gains a ' - + 'paragraph. Raised to 19,300 by a question this route does not ask: the router names the ' - + 'pieces a page is composed from now, and the router is a stop on five consumer routes, so ' - + 'this one is charged for it. That is what a shared stop costs and the reason it is still ' - + 'worth sharing, since the alternative is five routers going stale on five schedules. ' - + 'Raised again to 19,700 by the same arithmetic and a second question this route does not ' - + 'ask: the router asks what Arena ships and how much of it a project takes, and the head is ' - + 'one of the eleven parts that answer names. The shared stop is seven routes wide now, so a ' - + 'question added to the router is charged seven times, and what keeps that worth paying is ' - + 'that the head reaches a reader who came for something else and would never have opened ' - + 'this page on purpose.' - + ' The figures in this history predate the stop set being narrowed and the budget re-measured ' - + 'against what is left, so every one of them names a number this route does not have. The ' - + 'arguments travel and the numbers do not: what the budget is is the field beside this one. Raised to 4,200 when the section stopped ' - + 'reading as a refusal of the architecture. It opened on there being no server render, which ' - + 'is the one page a project consults while the layer choice is still cheap, and the tree ' - + 'answers each architecture with the evidence behind it: Arena renders no page and lists ' - + 'none, and which one produces it is the project\'s own answer.' - + ' Raised again to 4,600 when the page stopped picking the framework. It told a project ' - + 'whose framework was still open that Angular is the layer to build on, which is Arena\'s ' - + 'own gap read back as the project\'s decision: what differs is that Arena supplies the ' - + 'head on one layer and a React framework picked to be found supplies its own. The ' - + 'sentence also sent that reader at the layer whose server rendering is held by the ' - + 'weaker half of the evidence, and a product that has to be found is the one most ' - + 'exposed to it, so the pointer to the node carrying that asymmetry is at the decision ' - + 'rather than at the end of the page.' + 'paid once per project and taken before the first screen, because the answer reaches the ' + + 'layer and the install rather than a component, so a project that settles it afterwards ' + + 'settles it against code already written. What it buys is that the metadata entry point is ' + + 'reachable from the decision instead of from deep inside the install page. Every default it ' + + 'surfaces is silent in the direction that costs: a route is born unindexed, and an ' + + 'application with no origin publishes no canonical, so both are correct until a screen is ' + + 'missing from a result nobody checked. It states which layer supplies a head and that a ' + + 'framework picked to be found supplies its own, and it points at the node carrying the ' + + 'server-rendering evidence at the decision, without choosing an architecture for the project. ' + + 'The number is what the stops measure with room for one section, and it grows when Arena ' + + 'gains something to write into the head.', }, { name: 'consumer-surface', @@ -510,42 +195,16 @@ export const ROUTES: Route[] = [ stops: ['skills/design/references/surface.md'], budget: 10_800, reason: - 'paid once per project like the skin, the register and the seo routes, and taken before all ' - + 'three, because each of them asks how much of a thing nobody has shown the reader yet. What ' - + 'it buys is that a consumer stops learning the offer by running out of it. Arena ships a ' - + 'token layer, a set of colour aliases, the role tier, two densities, the components, ' - + 'five stylesheets for markup that is not one, a dozen exports carrying a compatibility ' - + 'promise, a head in one layer, a command that reports as well as writes, and a contract per ' - + 'pattern; the branch documented every one of them and named the whole of it nowhere, so ' - + 'which parts a project found came down to which page it happened to finish. The two ' - + 'measured cases are the rhythm tokens, which no consumer document named at all, and the ' - + 'density classes, which one rules bullet named once. The page is an index rather than a ' - + 'tutorial: every row points at the document that owns the part, which is what keeps it ' - + 'inside this budget and what keeps it from becoming a second router. It also carries the ' - + 'section nothing else could own, where Arena stops, because a limit stated in the document ' - + 'that would have taught you the feature is a limit nobody reads. The number is what the ' - + 'stops measure with room for one part to be added, and it grows when Arena ships something ' - + 'new rather than when the page explains something better: an explanation here is a row that ' - + 'should have been a pointer. ' - + 'Raised again to 25,800 when the density row stopped being two class names. Asked whether ' - + 'Arena answers twice the rows in the same height and a screen a thumb drives, six cold ' - + 'walks found both classes and none could say how one product offers both, which is the ' - + 'question a project actually has: the two are exclusive and neither is a prop, so the ' - + 'swap is a branch on the breakpoint helper rather than a media query, and that is decided ' - + 'here rather than per screen.' - + ' The figures in this history predate the stop set being narrowed and the budget re-measured ' - + 'against what is left, so every one of them names a number this route does not have. The ' - + 'arguments travel and the numbers do not: what the budget is is the field beside this ' - + 'one. Raised again to 10,500 for the one line this section was missing. The page states ' - + 'every line as a decision rather than a gap waiting to close, and the decision to ship ' - + 'no date picker and no time picker sat in one component prompt three stops away, where ' - + 'a project planning a form reaches it after it has planned one. A page promising the ' - + 'whole line is falsified by any part of it kept somewhere else. Raised again to 10,700 ' - + 'when the same section stopped answering a question that is the project\'s. It opened a ' - + 'line with no server render, where the tree supports one and the cold start tree ' - + 'answers each architecture with the evidence behind it: what Arena ships no tooling for ' - + 'is producing the pages, and a headline naming the architecture instead reads as a ' - + 'refusal on the one page a project consults before choosing. Raised to 10,800 when this page was rewritten into the register check:register holds. The page is one table whose second column answers a whole part of Arena in a paragraph, so every cell was a list joined by colons and semicolons, read by somebody deciding whether to take that part at all. Eighteen findings before and none after, for 73 characters. Raised to 4,700 when this page was rewritten into the register check:register holds. Thirteen findings before and none after, for 77 characters, most of them spent splitting the paragraph that decided one piece of the stack and named the asymmetry between the two layers in the same sentence.', + 'paid once per project and taken before the skin, register and seo routes, because each of ' + + 'them asks how much of a thing nobody has shown the reader yet. What it buys is that a ' + + 'consumer stops learning the offer by running out of it: every part Arena ships is named in ' + + 'one place, beside the document that owns it. The page is an index rather than a tutorial, ' + + 'which is what keeps it inside this budget and keeps it from becoming a second router. It ' + + 'also carries where Arena stops, each line a decision rather than a gap, because a limit kept ' + + 'in a component prompt is reached after the project has planned around it, and a page ' + + 'promising the whole line is falsified by any part of it kept elsewhere. The number is what ' + + 'the stops measure with room for one part, and it grows when Arena ships something new rather ' + + 'than when the page explains something better.', }, { name: 'consumer-stack', @@ -557,20 +216,15 @@ export const ROUTES: Route[] = [ budget: 10_300, reason: 'paid once per project like the surface, skin, register and seo routes, and taken beside the ' - + 'cold start tree rather than after it, because a toolchain settled after the first screen ' - + 'is settled against code already written. What it buys is that a reader stops inheriting ' - + 'Arena. Arena is built with bun, React 18 and Angular 22, and a branch that says nothing ' - + 'about the difference documents its own build as a requirement: every command here was ' - + 'written in one runner, no page named the peer ranges the manifests declare, and one ' - + 'package manager that installs this appeared nowhere at all, so the honest answer and the ' - + 'unasked question were indistinguishable. The evidence column is most of what the page ' - + 'costs and all of what makes it usable: an answer allowed by a manifest and never ' - + 'exercised is worth having and is not worth the same as one a suite runs, and a page that ' - + 'flattened the two would trade a silence a reader can notice for a confidence they ' - + 'cannot. What it deliberately does not carry is the layer decision and the render ' - + 'architecture, which are nodes eight and nine of the cold start tree: naming them here ' - + 'would give a project two places to answer one thing, and the tree is where the answer ' - + 'reaches a peer dependency rather than a preference.', + + 'cold start tree rather than after it, because a toolchain settled after the first screen is ' + + 'settled against code already written. What it buys is that a reader stops inheriting Arena: ' + + 'a branch saying nothing about the difference between what Arena supports and what Arena is ' + + 'built with documents its own build as a requirement. The evidence column is most of what the ' + + 'page costs and all of what makes it usable, since an answer allowed by a manifest and never ' + + 'exercised is worth having and is not worth the same as one a suite runs. What it ' + + 'deliberately does not carry is the layer decision and the render architecture, which are ' + + 'nodes of the cold start tree: naming them here would give a project two places to answer one ' + + 'thing, and the tree is where the answer reaches a peer dependency rather than a preference.', }, { name: 'consumer-page', @@ -581,47 +235,16 @@ export const ROUTES: Route[] = [ stops: ['skills/design/references/page.md'], budget: 14_200, reason: - 'the one consumer route past the router that is not paid per project or per screen but per ' - + 'element a builder draws themselves, which every screen has. What it buys is the half of a ' - + 'page that is never a component, and every part of it was reachable before only as a table ' - + 'row: the floor arrived as one row of the router, six hundred characters wide, and the air ' - + 'and the column as one row pointing at a stylesheet table four hundred lines into an npm ' - + 'page. Both of those rows are pointers here now, so a third of this route was bought back ' - + 'out of the stop it shares. What it adds beyond them is what no row had space for: which of ' - + 'the nineteen colour roles each thing a builder draws takes, that the three rhythm steps are ' - + 'custom properties as well as classes, that the alignment modifiers carry no length, and the ' - + 'cut that decides which side of a gap a builder owns, since air between two components comes ' - + 'from here and air inside one is a role answered in a style plugin. It closes on the same ' - + 'least-to-most the surface page opens with, because the answer for a first screen and the ' - + 'answer for a product with its own appearance are the same names read twice. The number is ' - + 'what the stops measure with room for one section, and it grows when Arena ships a piece for ' - + 'markup of somebody else\'s rather than when this page argues for one it already ' - + 'names. Raised again to 26,100 when the group step gained the shape of the miss it ' - + 'replaces. A cold walk that had read this page still wrote a column of its own carrying a ' - + 'display, a direction and a gap inside a table cell, which is what the group class already ' - + 'is, so the sentence telling a builder to reach for the class first is the one sentence ' - + 'here that was read and passed over. Naming the case costs a paragraph, and it is what ' - + 'separates a rule a builder agrees with from one they apply. ' - + 'Raised again to 27,700 when the colour section gained the level beside the role. A role ' - + 'says which colour text takes and a level says how far it is held back, and under the ' - + 'default style plugin ink-muted and ink-body resolve to the same colour, so a bare ink- ' - + 'muted paints a caption at full strength and no gate reports it. Five of six cold walks ' - + 'shipped exactly that, and the one that did not found the idiom in a compiled component ' - + 'sheet, which this branch tells a builder never to target. The block air the band leaves ' - + 'is here for the same reason: all six invented a padding for it because the column ' - + 'section said what the band carries and never what it leaves. Raised again to 29,900 by ' - + 'three more measured misses on this same page. Four of six walks wrote a hand-rolled flex ' - + 'strip for a short horizontal group inside an app bar, because the row was described as a ' - + 'wrapping line and a two element lock-up does not read as one, so the row is stated as the ' - + 'horizontal half of the three steps and the wrapping is a property it has rather than the ' - + 'test for whether it applies. One walk refused to put a class on an Arena element, which ' - + 'is right, and wrote no wrapper, which leaves the shell distributing its slack to nothing, ' - + 'so the page says what to do when the element that has to be laid out is a component. And ' - + 'the gutter is stated as a ceiling, since a length answered for a full width page leaves a ' - + '430px viewport spending two fifths of itself on margin.' - + ' The figures in this history predate the stop set being narrowed and the budget re-measured ' - + 'against what is left, so every one of them names a number this route does not have. The ' - + 'arguments travel and the numbers do not: what the budget is is the field beside this one.', + 'paid per element a builder draws themselves, which every screen has. What it buys is the ' + + 'half of a page that is never a component: which colour role each thing a builder draws takes ' + + 'and which level holds it back, since under the default style plugin a bare muted ink paints ' + + 'a caption at body strength and no gate reports it; the three rhythm steps as classes and as ' + + 'custom properties; the row as the horizontal half of those steps rather than a wrapping ' + + 'line; what to wrap when the element being laid out is a component; the block air the band ' + + 'leaves; and the gutter as a ceiling. Cold walks missed each of those when the page left it ' + + 'out. It closes on the same least-to-most the surface page opens with. The number is what the ' + + 'stops measure with room for one section, and it grows when Arena ships a piece for somebody ' + + 'else\'s markup rather than when this page argues again for one it already names.', }, { name: 'consumer-coldstart', @@ -640,51 +263,17 @@ export const ROUTES: Route[] = [ ], budget: 24_000, reason: - 'the first route on this branch that is declared as a tree, and the reason the shape exists. ' - + 'A reader here answers a question and walks one way: handed a document stating the palette ' - + 'and the type, they read the tree and go on; handed nothing and unwilling to be ' - + 'interviewed, they read the tree and one entry of the catalogue. Charging both branches ' - + 'would price a journey nobody takes and would make the catalogue look like a tax on every ' - + 'cold start rather than the answer to one branch of it. The worst branch is the catalogue ' - + 'one, and its second stop is a glob over the entries so the figure is the largest of them ' - + 'rather than the one that happens to be first. The number is what the two stops measure ' - + 'today with room for one entry to grow, and what it is argued against is that this route ' - + 'is paid once per project by a reader who would otherwise be guessing: the alternative to ' - + 'reading it is an agent inferring a palette from a screenshot, which costs nothing here ' - + 'and costs the project every screen. A branch is added when an answer sends a reader to a ' - + 'document the others never open, and never to describe a node they all read. Raised ' - + 'from 13,100 when the tree gained the two decisions that reach an install rather than a ' - + 'screen: how much of the product has to be found from outside it, which decides a layer ' - + 'and turns an optional peer into a required one, and what the application is assembled ' - + 'on, which is where the architecture claims are stated with what holds each of them up. ' - + 'The pair is what lets the last node derive the dependency list from the answers instead ' - + 'of asking about dependencies, and it is charged here rather than on the install route ' - + 'because a project reaching the install page has already chosen the layer this node chooses. It grew because the thing it describes grew, which is the one reason a budget should ' - + 'ever move. It grew again when node 4 gained what it matches a description against, which ' - + 'is the one line every ENTRY.md carries and not the entries themselves. That line is what ' - + 'keeps the second stop at a single entry as the catalogue grows: matching on the entries ' - + 'would make this route cost the whole catalogue, and the glob would stop being a fair ' - + 'charge the day a second register landed. The entry stop grew with it, because the ' - + 'catalogue now holds registers that depart from the answers the first ones converged ' - + 'on, and an entry that departs has to say so in its own table or a reader takes its ' - + 'roles for the shared answer and inherits a decision nobody made. A row is worth the ' - + 'characters when it names a role this register answers differently, and it is worth ' - + 'none when it restates the sheet sitting beside it. Raised again to 23,300 when the warm ' - + 'branch gained its exit. The node that reads what a project already carries handed the ' - + 'reader on to the nodes that decide an install, so a project with an approved config was ' - + 'routed into writing one again, and nothing in the tree said which of its nodes a project ' - + 'already carrying Arena has spent. The exit is what makes the first node\'s two answers ' - + 'reach two different ends rather than the same one, and it is charged here because the ' - + 'tree is where a reader is when they need it. Raised again to 23,600 when the catalogue ' - + 'stopped being reachable only by refusing the interview. Node 4 was titled a last ' - + 'resort and node 3 reached it from the row where the user will not answer, so a project ' - + 'that would rather pick a measured register than invent one had no row anywhere and was ' - + 'interviewed instead. Node 2 asks where the appearance comes from and a measured ' - + 'register is one of its answers, so the row belongs there, and the node it reaches says ' - + 'that two arrivals take it. It carries the verb as well: an entry is copied rather than ' - + 'cited, and the one page saying so was the catalogue\'s own AGENTS.md, on the branch this ' - + 'reader is told not to read, so a walk that reached the entry correctly then guessed at ' - + 'what to do with it. Raised again to 23,900 when the tree was rewritten into the register check:register holds. The tree is the page a reader walks one node at a time, so a node that carried its question, its cost and its exception in one sentence was the worst place on this branch for that shape: a reader who matches on one row reads that row alone. Thirty-five findings before and none after, for 199 characters. Raised again to 24,000 for the catalogue entry this route charges its worst branch. Fifty-eight findings over the eight cards before and none after, for 29 characters on the largest of them. A card opens on what the register is, in a list of four things after a colon, which is the one sentence a reader matching a description against their own product actually compares.', + 'the route declared as a tree, and the reason that shape exists: a reader answers a question ' + + 'and walks one way, so the route is charged its worst branch and never the sum. Handed a ' + + 'document stating the palette and the type, a reader reads the tree and goes on; choosing a ' + + 'measured register instead, they read the tree and one catalogue entry, and that stop is a ' + + 'glob so the charge is the largest entry rather than the first. The tree carries the ' + + 'decisions that reach an install rather than a screen, whether the product has to be found ' + + 'and what it is assembled on, so its last node derives the dependency list from answers ' + + 'instead of asking about dependencies. A description is matched against the one line every ' + + 'entry carries, which keeps the second stop at a single entry as the catalogue grows. The ' + + 'alternative to reading it is an agent inferring a palette from a screenshot, which costs ' + + 'nothing here and costs the project every screen.', }, { name: 'contributor-component', @@ -694,52 +283,17 @@ export const ROUTES: Route[] = [ stops: ['frameworks/AGENTS.md', 'frameworks/*/AGENTS.md'], budget: 71_000, reason: - 'the most-walked contributor route and the one carrying the most reasoning per stop. It is ' - + 'allowed to cost more than any consumer route because it is paid by whoever changes Arena ' - + 'and never by whoever uses it. What it may not carry is one category\'s own tour: the ' - + 'chart family is a fourth stop, frameworks/CHARTS.md, for whoever changes a chart, and ' - + 'charging every component change for it cost a fifth of this route. Raised from 83,000 ' - + 'when the branch named the convention it answers to and the two places it departs from ' - + 'one: a contributor who does not know the consumer branch is a skill adds an eighteenth ' - + 'file under the reserved name, and the cost of that is paid by every reader of the tag. ' - + 'Raised from 84,500 when the Angular stop gained the rule deciding which primitives are an ' - + 'attribute on a native element rather than an element of their own. That is not one ' - + 'family\'s tour: the question it answers -- would wrapping this element break what the ' - + 'element means -- is asked of every primitive, and the two consequences it carries, that an ' - + 'output named after a DOM event cannot be listened to from the host block and that the demo ' - + 'emitters read the declared selector, are ones a contributor hits by writing the component ' - + 'and not by reading about the table. Left out of the roof and out of a family page for the ' - + 'same reason: only one layer authors elements a consumer nests, so only that layer ' - + 'decides. Raised to 69,500 when the architecture envelope was written down on the three ' - + 'stops that own it. A consumer adopts Arena on a tree whose last nodes tell a project it ' - + 'may server-render, prerender or embed as a fragment, and what each answer costs in ' - + 'dependencies; every one of those is a property of the sources this route reaches, and ' - + 'the whole of it was unwritten. What that cost was measured on the consumer bench: a ' - + 'module-scope read of a browser global throws at import time during a server render, so ' - + 'the stack names the import and never the component that caused it, and a contributor ' - + 'fixing something else has no way to know the claim existed. check:architecture holds the ' - + 'mechanical half now, and this route is where its reader is told which escapes are ' - + 'correct rather than only that the gate failed. The roof carries what binds both layers ' - + 'and each layer carries its own envelope and its own peer, which is the split that keeps ' - + 'the pair from going stale in one of them. Raised to 70,000 when the Angular stop gained ' - + 'the fourth trap: the layer writes an optional member\'s default twice, as the initial ' - + 'value and again as the fallback of the transform that resolves an absence, and only one ' - + 'of the two runs for any given caller. It is charged here rather than to a family page ' - + 'because every optional member in the layer carries the shape, and it is charged at all ' - + 'because no gate holds the PAIR: the two halves are read by two gates that each know one ' - + 'of them and neither knows they are one decision. Measured on a consumer bench, where one ' - + 'grid drew six columns against the five the same config drew beside it. Raised to 71,000 ' - + 'when the ordered steps stopped omitting two things a new component cannot ship without. ' - + 'The step naming each layer shape said trio and quartet and stopped there, while the ' - + 'barrel chain that makes an Angular primitive typechecked at all sat two hundred lines ' - + 'downstream under a heading about verifying the layer, so a contributor reading the steps ' - + 'in order shipped a primitive check:angular never compiled and no adopter could import. ' - + 'And no step said to write suites, which left the one fixture whose walk is bidirectional ' - + 'outside the list entirely: a component owning a contentChildren query cannot ship ' - + 'without an entry there, and the steps that claim each one produces what the next reads ' - + 'never said so. Both belong to the route rather than to a layer, because the steps are ' - + 'what a contributor reads once and in order, and a layer page is what they return to ' - + 'after something has already gone wrong.', + 'the most-walked contributor route and the one carrying the most reasoning per stop, allowed ' + + 'to cost more than any consumer route because it is paid by whoever changes Arena. It may not ' + + 'carry one category\'s own tour: the chart family is a page of its own, frameworks/CHARTS.md, ' + + 'for whoever changes a chart. What it carries is what every component meets: the ordered ' + + 'steps a new component takes, including the barrel chain an Angular primitive needs to be ' + + 'compiled at all and the suites it owes; the architecture envelope, since a module-scope read ' + + 'of a browser global throws during a server render with a stack naming the import rather than ' + + 'the component; when a primitive is an attribute on a native element; and the Angular default ' + + 'written twice, as the initial value and as the fallback, a pair no gate holds. The roof ' + + 'carries what binds both layers and each layer carries its own envelope and peer, which is ' + + 'the split that keeps the pair from going stale in one of them.', }, { name: 'contributor-authoring', @@ -752,20 +306,13 @@ export const ROUTES: Route[] = [ 'paid once per contributor rather than per change, which is what lets it carry the whole ' + 'surface rather than a pointer to it. It is the shortest contributor route on purpose: what ' + 'it answers is asked before the first edit, by a reader who has not chosen a task yet, and a ' - + 'route that costs more than the change it precedes is one nobody walks. Three cold walks ' - + 'converged on it independently, one per other contributor route, and each had decided ' - + 'generated against authored by opening files rather than by being told: a component source ' - + 'carrying an emitted doc comment with no marker on it, a prompt read as wholly generated ' - + 'when it is mostly prose, and a manifest whose existence no stop on the route mentions. ' - + 'Every one of those mistakes ships green, which is the argument for a stop of its own ' - + 'rather than a paragraph on a page a task chooses. The number is what the stops measure ' - + 'with room for one section, and it grows when a generator gains a shape rather than when ' - + 'the page gains a paragraph, since the shapes are derived from what each node declares it ' - + 'writes. Raised to 7,900 when a generator gained the shape this page had no name for: ' - + 'a file written into a repository that is not this one. Nothing here can observe such a ' - + 'file, so the section says which instrument reads it and who runs that instrument, and ' - + 'it names the payload as generated too, since a prompt found inside a package is one a ' - + 'contributor would otherwise correct in the copy rather than in its source.', + + 'route that costs more than the change it precedes is one nobody walks. Cold walks of every ' + + 'other contributor route decided generated against authored by opening files rather than by ' + + 'being told: a source carrying an emitted doc comment with no marker, a prompt read as wholly ' + + 'generated when it is mostly prose, a manifest no stop mentions. Each of those ships green. ' + + 'It also names the file a generator writes into a package this tree never holds, since a ' + + 'prompt found inside a package is one a contributor would otherwise correct in the copy. It ' + + 'grows when a generator gains a shape, not when the page gains a paragraph.', }, { name: 'contributor-token', @@ -775,38 +322,16 @@ export const ROUTES: Route[] = [ stops: ['contracts/AGENTS.md', 'contracts/design/AGENTS.md', 'contracts/design/TokenTypes.md'], budget: 65_600, reason: - 'the normative half of the tree, where a stop is read for what a value MEANS rather than for ' + 'the normative half of the tree, where a stop is read for what a value means rather than for ' + 'how to write one, so it is bounded by what a person can hold rather than by what an agent ' - + 'can afford. Raised from 72,000 when the role tier went from 28 roles to 63 and grew a ' - + 'ninth token type: TokenTypes.md has to state what a keyword is and why it is the one ' - + 'departure from 2025.10, and the design specification has to say why a style plugin now ' - + 'reaches type, ink and internal air. Raised again to 77,000 when the muted percentages ' - + 'stopped being literals frozen into a hundred compiled sheets and became a level tier a ' - + 'palette moves: the specification has to say that those numbers are floors, that a level ' - + 'is raised and never lowered, and what raising one costs the hierarchy it belonged to. ' - + 'The route is longer because the thing it describes is bigger, which is the one reason a ' - + 'budget should ever move.' - + ' The figures in this history predate the stop set being narrowed and the budget re-measured ' - + 'against what is left, so every one of them names a number this route does not have. The ' - + 'arguments travel and the numbers do not: what the budget is is the field beside this one.' - + ' The last raise was bought by this level answering a second reader. contracts/design/ is ' - + 'what a platform target reads first, and a target that is not a browser cannot derive from ' - + 'a value and a unit what the reader asking for larger text does to it, so TokenTypes.md ' - + 'states that axis, what each of its three words obliges on each platform, and why the ' - + 'emission here does not follow it. It also states the three shapes in the type map that read ' - + 'one way on the web and another everywhere else, which is where a target would otherwise ' - + 'guess: a keyword inherits no transform, a font stack ends in generic families that name ' - + 'nothing off the web, and a unit hint on a bare number is an instruction for one platform. ' - + 'The level describes something bigger rather than the same thing at more length, which is ' - + 'the one reason this budget moves. It also states, where the values are decided rather ' - + 'than where a gate is registered, that target size is density\'s axis: what each density ' - + 'clears, and that compact clears neither floor and is therefore not offerable to a thumb. A ' - + 'reader picking a density for a phone needs that sentence in the specification and not in a ' - + 'gate they have no reason to open. And it names a fourth thing DTCG deliberately does not ' - + 'model, beside the colour derivations, the font loading and the device geometry: whether the ' - + 'reader asked for a stronger interface, which has no value until there is a device. Three ' - + 'cases the kernel answers and one it refuses, because an accent drawn as ink is a decision ' - + 'over a palette this level does not know.', + + 'can afford. The level answers two readers: a contributor deciding a value, and a platform ' + + 'target that reads contracts/design/ first and is not a browser. So TokenTypes.md states the ' + + 'one departure from DTCG and why, the axis a reader asking for larger text moves and what it ' + + 'obliges on each platform, and the shapes in the type map that read one way on the web and ' + + 'another elsewhere. The specification states the muted levels as floors a palette moves up ' + + 'and never down, target size as the axis density answers, and what DTCG deliberately does not ' + + 'model. The route moves when the level describes something bigger, never when it describes ' + + 'the same thing at more length.', }, { name: 'contributor-gate', @@ -816,48 +341,14 @@ export const ROUTES: Route[] = [ stops: ['scripts/AGENTS.md', 'scripts/check/AGENTS.md', 'scripts/check/*/AGENTS.md'], budget: 92_000, reason: - 'the route this repository asks a contributor to take most often after the component one, ' - + 'and the one whose last stop grows every time a gate lands, since a gate states its whole ' - + 'claim in one table row. The headroom is deliberately a few rows wide: a budget a single ' - + 'new row breaks reports the row rather than the growth it is there to report, and that ' - + 'has happened often enough to be the ordinary case rather than the exception. **Do not ' - + 'read a raise history here.** The one this string used to carry ended at a figure this ' - + 'route does not have, because the budgets on this branch were re-measured against the ' - + 'tree and clamped to it while the narratives were left behind, so a contributor arguing ' - + 'a raise read a number nothing held. Only the argument in such a history travels, and ' - + 'the argument is this: the measurement is worth more than the figure, so derive the ' - + 'median and the longest row with awk over the rows of the domain table before writing ' - + 'one, because the row that breaks a budget is the long one and the median is what says ' - + 'how often that happens. Raised to 83,500 when the gate checklist stopped under-reporting ' - + 'what a gate owes: it framed the registrations as a list plus two outside the file while ' - + 'the per-domain count in its own table is one more that fails on its own, and it told a ' - + 'contributor that nothing has to be written down for a node-less gate to run, which is ' - + 'true of the cache and false of check:graph. That sentence sent a reader to write a gate ' - + 'that goes red on the one rule the nearest page had just excused them from. Raised to ' - + '84,500 when check:workflow-scripts landed, which is one row and the ordinary case this ' - + 'budget is written around. The row runs longer than the median because its claim is a ' - + 'failure this tree has already paid for rather than a rule somebody thought of: a job ' - + 'that installs nothing resolved a pinned dependency to whatever the registry served, the ' - + 'throw was swallowed by a process substitution, and the guard that reads a list of paths ' - + 'read none. A contributor deciding whether their own gate owes that much needs the ' - + 'failure and not the rule. Raised to 87,000 when the contracts package landed with three ' - + 'gates at once, which is a case this budget has not had to hold before: a channel out of ' - + 'the repository is not one claim but three, and they arrive together or the artefact ships ' - + 'unheld. Measured the way this string says to, before the raise rather than after it: over ' - + '48 rows the median is 951 characters and the longest is 3,942, and the three new rows are ' - + '789, 856 and 677, so every one of them sits under the median and the raise pays for their ' - + 'number rather than for their length. Raised to 88,000 for the target-size row, measured ' - + 'the same way: over 49 rows the median is still 951 and the longest still 3,942, and the new ' - + 'row is 1,066. It runs a little past the median because its claim is a measurement rather ' - + 'than a rule, and what a contributor needs from it is what it measures and what it refuses ' - + 'to measure: the activation box rather than the painted one, only inside a named part, and ' - + 'in the density nothing else in this repository draws. Raised to 88,500 when check:citations widened from one surface to three, which is the case the paragraph above does not cover: no row landed and one row grew, from 1,927 characters to 2,840, over the same 49 rows whose median is 951 and whose longest is 3,942. A gate states its whole claim in one row, so a claim that widens is a row that grows, and the only alternative on offer is a row describing a third of what the gate reads. Raised to 89,600 for the check:support row, measured the way this string says to: over 50 rows the median is 972 characters and the longest is still 3,942, and the new row is 1,030. It sits just past the median because what a contributor needs from it is a failure this branch already shipped rather than a rule somebody thought of: bunx was written into five consumer pages while the command they name is a Node program, so the documentation carried a runtime dependency Arena does not have and no gate could see it. The raise pays for one row, which is the ordinary case this budget is written around. ' - + 'Raised to 90,700 for the check:mcp row, measured the same way: over 51 rows the median ' - + 'is 994 characters and the longest is still 3,942, and the new row is 1,017. It sits at ' - + 'the median, and what it buys is the one claim of that gate a contributor cannot infer ' - + 'from the package it judges: which of the four packages carries the language. A corpus ' - + 'in the server rather than beside the components reads as an oversight until a gate says ' - + 'where it belongs and why, and a comment claiming it would hold nothing.', + 'the route a contributor takes most often after the component one, and the one whose last ' + + 'stop grows every time a gate lands, since each gate has a row in its domain\'s table. The ' + + 'headroom is a few rows wide on purpose: a budget a single new row breaks reports the row ' + + 'rather than the growth it is there to report. A row states the claim and the failure the ' + + 'gate exists for, and the rest of its argument belongs to the gate\'s own reason strings, ' + + 'which its paired suite asserts by name. Before writing a row, measure the median and the ' + + 'longest row of the domain table, because the row that breaks a budget is the long one and ' + + 'the median is what says how often that happens.', }, { name: 'contributor-tailwind', @@ -867,13 +358,12 @@ export const ROUTES: Route[] = [ stops: ['frameworks/tailwind/AGENTS.md'], budget: 44_000, reason: - 'the row a contributor takes when a shape, a colour or a state is wrong and the API is not, ' - + 'and it went unbudgeted for as long as the routing table has carried it. What that cost is ' - + 'was measured on a cold walk: the file a corner radius actually moves in is a style plugin ' - + 'under plugin-style-store, four stops from the router, because the appearance Arena ' - + 'installs with IS a plugin and no page on the way said so. One stop, and it is the largest ' - + 'single page on the contributor branch, so what this budget holds down is the document most ' - + 'likely to grow by a hazard nobody routes to and everybody meets.', + 'the row a contributor takes when a shape, a colour or a state is wrong and the API is not. ' + + 'The file a corner radius actually moves in is a style plugin under plugin-style-store, ' + + 'because the appearance Arena installs with is itself a plugin, and a cold walk found no page ' + + 'on the way saying so. One stop, and it is the largest single page on the contributor branch, ' + + 'so what this budget holds down is the document most likely to grow by a hazard nobody routes ' + + 'to and everybody meets.', }, { name: 'contributor-behaviour', @@ -883,20 +373,15 @@ export const ROUTES: Route[] = [ stops: ['contracts/AGENTS.md', 'contracts/behaviour/AGENTS.md'], budget: 38_600, reason: - 'the route a bug report takes, which is the arrival the routing table had no row for until ' - + 'the symptom rows landed, and the one whose answer is most often that nothing in the code ' - + 'is broken: a requirement no suite pins is unfalsifiable rather than unverified, and that ' - + 'paragraph is what a reader comes here for. It is budgeted apart from the component route ' - + 'because the two share no stop past the roof, and because this one is paid by whoever is ' - + 'holding a defect rather than by whoever is adding a component. The level answers a second ' - + 'reader now, which is what the last raise bought: a pattern names the role it requires as a ' - + 'field rather than only inside prose, and the rule about native semantics states the half ' - + 'that applies where there is no browser to supply them. Both are the level describing ' - + 'something bigger rather than describing the same thing at more length, which is the one ' - + 'reason a budget moves. The roof above it grew for the same kind of reason: contracts/ now ' - + 'holds a page written for somebody who is not changing Arena at all, the one npm shows for ' - + 'the contracts package, and the roof is where a reader is told that page exists and who it ' - + 'is for.', + 'the route a bug report takes, and the one whose answer is most often that nothing in the ' + + 'code is broken: a requirement no suite pins is unfalsifiable rather than unverified, and ' + + 'that paragraph is what a reader comes here for. It is budgeted apart from the component ' + + 'route because the two share no stop past the roof, and because this one is paid by whoever ' + + 'holds a defect rather than by whoever adds a component. The level answers a reader outside ' + + 'the browser as well: a pattern names the role it requires as a field and not only inside ' + + 'prose, and the rule about native semantics states the half that applies where no browser ' + + 'supplies them. The roof names the one page on that level written for somebody who is not ' + + 'changing Arena, the npm page of the contracts package, and says who it is for.', }, { name: 'contributor-release', @@ -907,71 +392,16 @@ export const ROUTES: Route[] = [ budget: 69_600, reason: 'the least frequent route on the branch and the most expensive one to get wrong, because ' - + 'every one of its failures publishes nothing and errors nowhere. It is budgeted now ' - + 'because the document carrying the sequence was a .txt until this route existed, so every ' - + 'documentation gate walked past it: its own citations went unchecked, so did the two pages ' - + 'naming it, and one of its commands pushed the tag to whatever branch was checked out ' - + 'while the publish workflows listen on one. A cold walk reached the procedure at all only ' - + 'by reading to the middle of a CI page. The sequence is the first stop because it is what ' - + 'a reader needs first, and the other two are what it sends them to rather than what they ' - + 'open instead. Raised to 55,100 when the operating system matrix moved off the pull ' - + 'request and onto the merge into develop, because that move puts a precondition on this ' - + 'route: needs does not cross workflows, so no required check on a merge request to main ' - + 'reads a platform result, and what carries it instead is that the merge request is opened ' - + 'on a green develop. A reader cutting a release who does not know that reads a green ' - + 'required check as an answer about three operating systems, and it is an answer about ' - + 'one. Raised to 57,200 when the publish guard was made to fail closed. A reader cutting a ' - + 'release has to know that "nothing this package carries has moved" is a real answer and ' - + 'not a fault, and that the guard reaches it by asking the assembler which of a directory ' - + 'ships rather than by keeping a second list of globs in step. The version of it that ' - + 'could not read its own list of paths republished both packages from a diff of the whole ' - + 'tree at 10.0.1 and 10.1.0 and wrote publish into the run summary while doing it, which ' - + "is this route's failure mode exactly: nothing errored, and nobody was told." - + ' Raised to 58,300 when a third package landed. The packaging page opened by saying there ' - + 'were two, which stopped being true, and a reader cutting a release needs to know at that ' - + 'point that the third is not a layer, that its consumer has no Node, and that the CSS ' - + 'chain, the CLI and the peer dependencies the rest of that page is about apply to neither ' - + 'of its files. Raised to 60,400 when that package gained a publisher of its own. A reader ' - + 'cutting a release now has a third workflow, and two things about it are true of neither of ' - + 'the others: its publish step is npm and not Bun, because bun publish does not speak OIDC ' - + 'and a tarball leaving through it would be unattested, and configuring a trusted publisher ' - + 'for a name that has never been published needs an explicitly selected allowed action since ' - + 'May 2026 and may not be possible at all before a first version exists. Both are things this ' - + 'route otherwise discovers at the worst moment, and the sequence itself carries them ' - + 'rather than sending a reader to a CI page mid-release. Raised to 62,900 when the release ' - + 'page stopped following the push of the tag and started following a green Arena main. That ' - + 'moves a step of this sequence: the tag now has to exist before that run finishes, which is ' - + 'what --follow-tags buys, and a page that never appeared is written by dispatching the ' - + 'workflow by hand and by nothing else. A reader who does not know it waits for a page that ' - + 'no event is coming for, which is this route\'s failure mode again: nothing errored, and ' - + 'nobody was told. Raised to 63,300 for the one artefact of a release that no gate here ' - + 'can observe. The corpus ships in one package and the components in another, so a ' - + 'project installs two version numbers and nothing here installs either: the pair is ' - + 'checked by installing both packed packages and calling arena_start or it is checked by ' - + 'nobody, and a corpus that did not travel answers every question with silence. This ' - + 'route\'s failure mode once more, and the sequence carries the step rather than sending ' - + 'a reader to find it. Raised to 64,000 when a fourth ' - + 'package landed, which is the case the third already argued and this route pays again: a ' - + 'reader cutting a release has one more workflow, and what is true of this one is true of ' - + 'none of the others. It is the only package here declaring a runtime dependency, and it ' - + 'carries no document at all, so the two things a reader would otherwise check by opening ' - + 'the tarball are the two a gate asserts instead. Raised to 67,700 for two preconditions ' - + 'this route reaches nowhere else. The branch takes no push: main requires a pull request ' - + 'and a status check and names no bypass, so the tag rides develop and the release lands ' - + 'as a merge, and a sequence that spells a push to main spells a command the branch ' - + 'refuses. And the benches are a step of the release rather than an afterthought of it: ' - + 'they install Arena from the registry, so they move to the published version after the ' - + 'publish workflows finish and are packed only then, and the workflow serving them builds ' - + 'their default branch at the moment it runs, so the domain shows that move on a hand ' - + 'dispatch and on nothing else. Both are this route\'s failure mode again, where the wrong ' - + 'order errors nowhere. Raised to 69,600 when a release gained a version surface no gate on ' - + 'this route reads and a publish job gained a second registry. server.json states the ' - + 'version twice and check-release.ts never opens it, so a reader who bumps by that page ' - + 'alone passes step 1 and fails step 2, and the sequence has to say which gate catches it. ' - + 'And what the publish job sends is no longer only a tarball: the MCP registry entry goes ' - + 'out of the same job under the same OIDC identity, so a reader cutting a release owes it ' - + 'the same attention as the npm half and reaches that fact nowhere else. This route\'s ' - + 'failure mode both times, where what is not known errors nowhere.', + + 'every one of its failures publishes nothing and errors nowhere. The sequence is the first ' + + 'stop because it is what a reader needs first; the other two are where it sends them. It ' + + 'carries each precondition this route reaches nowhere else: main takes no push, so the tag ' + + 'rides develop and the release lands as a merge; the operating system matrix runs on develop, ' + + 'so the merge request opens on a green develop; the tag exists before Arena main finishes or ' + + 'the release page needs a hand dispatch; server.json is a version surface check-release.ts ' + + 'never reads; each package has a publisher of its own, one of them npm for the attestation ' + + 'Bun cannot give; a first publish needs a trusted publisher configured by hand; and the ' + + 'corpus and the components travel apart, so both are installed to check them. Each is this ' + + 'route\'s failure mode: nothing errors, and nobody is told.', }, ]; @@ -1076,6 +506,23 @@ export function staleBudgetProblems(name: string, budget: number, total: number) ]; } +export function reasonProblems(name: string, reason: string) { + const problems = []; + if (reason.length > REASON_MAX_CHARS) + problems.push(`${name}: its reason runs ${reason.length} characters against ${REASON_MAX_CHARS}. ` + + 'A reason says why the number is right today, and one that keeps growing is a raise history ' + + 'read by every contributor arguing the next one.'); + const figure = BUDGET_FIGURE.exec(reason)?.[0]; + if (figure) + problems.push(`${name}: its reason names the figure ${figure}. The budget is the field beside ` + + 'the reason, and a figure written into the prose is one nothing holds when the field moves.'); + const history = HISTORY_WORDS.exec(reason)?.[0]; + if (history) + problems.push(`${name}: its reason says "${history}", which is what the budget was rather than ` + + 'why it is right. The commit that moved it carries that, dated.'); + return problems; +} + export function entryProblems(route: Route, entries = ENTRIES) { if (entries.some((entry) => entry.name === route.entry)) return []; return [ @@ -1121,6 +568,7 @@ export function routeProblems(base = root, routes = ROUTES, entries = ENTRIES) { + 'reports a cost that starts at the second page'), ...overBudgetProblems(entry.name, entry.reason, entry.budget, one.charged, one.total, 'entry'), ...staleBudgetProblems(entry.name, entry.budget, one.total), + ...reasonProblems(entry.name, entry.reason), ); } @@ -1132,6 +580,7 @@ export function routeProblems(base = root, routes = ROUTES, entries = ENTRIES) { measured.push({ route, branches, worst, charged, total, opening, whole: opening + total }); problems.push( ...unreachedProblems(route, unreached), + ...reasonProblems(route.name, route.reason), ...(named.length > 0 ? [] : [ ...overBudgetProblems(route.name, route.reason, route.budget, charged, total), ...staleBudgetProblems(route.name, route.budget, total), From d2c219bffb6840b87d60246ed7fa95601034a487 Mon Sep 17 00:00:00 2001 From: JCarlosHidalgo Date: Thu, 10 Sep 2026 18:15:30 -0400 Subject: [PATCH 2/4] A table cell stays under 2,000 characters, because a document cap does not cap a row scripts/check/arena/AGENTS.md sat 153 characters under the 60,000 limit with single gate rows near 4,000, so the cap had become the target and the next row would have been refused by arithmetic. `cellSizeProblems` in check:docs fails a table cell over `MAX_CELL_CHARS`, fenced tables excluded and escaped pipes kept inside their cell. The rows that broke it lose their histories rather than their claims: that page drops to about 50,400 characters, and the three cells elsewhere (`arena-to-prod/`, `chromium.ts`, `typecheck.ts`) come under the limit. The gate route's budget follows the shorter stop down to a few rows of headroom. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01TVMSZHMunk5kC54Voh5RsH --- AGENTS.md | 3 +- scripts/check/arena/AGENTS.md | 42 +++++++++---------- scripts/check/arena/check-docs.test.ts | 20 ++++++++- scripts/check/arena/check-docs.ts | 57 ++++++++++++++++++++------ scripts/check/arena/check-routes.ts | 5 ++- scripts/generate/core/AGENTS.md | 2 +- scripts/lib/arena/AGENTS.md | 4 +- 7 files changed, 92 insertions(+), 41 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index f67616fd4..3964385f6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -237,7 +237,8 @@ two copies and never reads either for meaning. Verify with are allowed, in a gate's own reason string, or in the component's `.prompt.md`. **Somewhere a stale copy of it fails something.** -`bun run check:docs` holds the size rule, the punctuation rule and the comment rule, and +`bun run check:docs` holds the size rule, for a document and for a table cell, the punctuation +rule and the comment rule, and `bun run check:citations` holds every path that prose names to existing. **The present-tense rule is the one no gate holds**, because nothing mechanical can judge it. diff --git a/scripts/check/arena/AGENTS.md b/scripts/check/arena/AGENTS.md index 304ec2444..422b7b15b 100644 --- a/scripts/check/arena/AGENTS.md +++ b/scripts/check/arena/AGENTS.md @@ -5,32 +5,32 @@ The domain for a gate that reads two or more layers at once, or the repository r | gate | fails when | | --- | --- | | `check-all.ts` | not a gate, but the runner: `GATES` is the registry, and `testStep()` is the **single authority** for how the test suite is invoked. Read the invocation there rather than reconstructing one; a narrowed run matching fewer files is indistinguishable from one matching all of them. | -| `check-docs.ts` | a `.md` exceeds its limit, 60,000 characters unless `SIZE_ALLOWANCE` raises it, a `.md` punctuates its prose with an em dash, or a hand-written source carries a comment it is not allowed: `scripts/` and tests may carry one header of at most 10 lines, everything else none. `SIZE_EXEMPT` names what is exempt from the limit by charter, `SIZE_ALLOWANCE` what holds to a higher one instead, and `PROSE_EXEMPT` the one tree exempt from the punctuation rule, each with its reason. **An allowance is not an exemption**: the document is still measured against the raised number, and one that falls back inside the shared limit fails as a **stale allowance**, as does one whose document has moved or gone. That is what keeps a raised ceiling from quietly becoming permanent. The measure is characters and never bytes, so reading a file's length by hand means `node -e "console.log(require('fs').readFileSync('X','utf8').length)"` and never `wc -m`, which reports a document of multi-byte characters hundreds over a limit it is comfortably under. Both rules read by lexing, so a `//` inside a string, a regex or a template literal is never mistaken for a comment, a `@ts-` or `eslint-` directive is a directive rather than the file's one allowance, an em dash inside a fence or a code span is the document quoting code, and a fence closes only on a run of its own character at least as long as the one that opened it; a `.generated.` file is skipped unread. It also holds the branch boundary, over every consumer document: a `.prompt.md` anywhere, an `INDEX.md` under `frameworks/`, and an `ENTRY.md` under `plugin-style-store/catalogue/`, which is a consumer document although it sits outside the skill tree because it is what a project reads to choose the appearance it starts from. That classification is also what keeps it out of `check-community.ts`'s contributor set, so Context7 hands it to a consumer asking what they can start from instead of hiding it as repository furniture. Citing a path under `scripts/`, a `AGENTS.md` under `contracts/` or `frameworks/`, or `frameworks/PACKAGING.md` sends a reader somewhere the router promised they would not have to go. `CONTRIBUTOR_PATHS` carries a reason per shape, and `BRANCH_SWITCH` carries the single carve-out: the root `SKILL.md`, which names the contributor branch because naming it is how it sends a contributor away. Nothing below it has anybody left to redirect, so a document there needing an exception is a rule stated on the wrong branch. **`RULE_OWNERS` is the same boundary run the other way, on CONCEPTS rather than paths**: a rule is assigned to one branch with its reason, and the gate fails the phrase appearing on the other. It is a register rather than a mechanism, so it covers what is declared and nothing else, and what is not declared is unheld and said to be; an entry no document on its own branch states any more fails as a stale registration. It exists because a `R4 violation` written into a contract's `description` reached a published `.d.ts`, where a consumer read a rule number with no way to learn what it meant. **`FOREIGN_CODE` runs the same boundary over what a document OFFERS rather than what it says**: a fenced example writing another design system's markup fails, and prose naming one passes, because a Don't saying what Arena is not teaches nobody to install it while an example is the half a reader copies. The two halves are the two the Markdown lexer already separates, so a code span is prose's neighbour and a fence is not. **It also holds the one count a consumer document carries**: each layer's `PACKAGE.md` states how many components the package ships, and `COMPONENT_COUNT_CLAIM` compares that number against `frameworks/Components.json`. The count stays because the npm page is where a reader meets it first, and it is held because the two pages had drifted four and nine components behind the tree with every gate green. | +| `check-docs.ts` | a `.md` exceeds its limit or holds a table cell over `MAX_CELL_CHARS`, prose carries an em dash, a hand-written source carries a comment it is not allowed, or a consumer document crosses the branch boundary. **Size**: 60,000 characters unless `SIZE_ALLOWANCE` raises it; `SIZE_EXEMPT` names what is exempt by charter and `PROSE_EXEMPT` the one tree exempt from the punctuation rule, each with its reason. **An allowance is not an exemption**: the document is still measured against the raised number, and one that falls back inside the shared limit, or has moved or gone, fails as a **stale allowance**. The cell cap exists because a document cap does not cap a row. The measure is characters, so read one by hand with `node -e "console.log(require('fs').readFileSync('X','utf8').length)"` and never `wc -m`. **Comments**: `scripts/` and tests may carry one header of at most 10 lines, everything else none. Every rule here reads by lexing, so a `//` inside a string is not a comment, a `@ts-` or `eslint-` directive is not the allowance, and an em dash in a fence or a code span is quoted code. **The branch boundary**, over every consumer document (a `.prompt.md`, an `INDEX.md` under `frameworks/`, an `ENTRY.md` under `plugin-style-store/catalogue/`): `CONTRIBUTOR_PATHS` fails a citation of a path under `scripts/`, an `AGENTS.md` under `contracts/` or `frameworks/`, or `frameworks/PACKAGING.md`, and `BRANCH_SWITCH` is the carve-out for the root `SKILL.md`; `RULE_OWNERS` runs the boundary on concepts, failing a registered rule's phrase on the branch that does not own it; `FOREIGN_CODE` fails a fenced example writing another design system's markup, while prose naming one passes. `COMPONENT_COUNT_CLAIM` holds the one count a consumer page carries, each `PACKAGE.md`'s, against `frameworks/Components.json`. | | `check-graph.ts` | the declared script graph disagrees with itself: a script under `build/`, `generate/` or `check/` that neither declares a node nor is named in one of the two lists that say it is out, an entry in either list that has since subscribed or gone, a node whose name is no npm script, a `feeds` naming no node or naming itself, a cycle, an artifact two nodes write, a node reading nothing that is there, or a spec whose directory holds no file at all. **The rule it exists for is the unsubscribed reader**: node B reads what node A writes and A does not list B in `feeds`, so a run would keep B's last answer over an artifact A has just rewritten. Both halves are held, so a `feeds` entry no artifact carries fails too, an edge nobody maintains being the same defect read backwards. A spec matching no file today whose directory IS there is reported and allowed: refusing it would mean a declaration can only describe the tree as it stands, and the first file of a new kind would be read by nobody. What it asserts lives in `scripts/graph/graph-problems.ts`, with the rest of the graph; this file is a print and an exit. | -| `check-portability.ts` | a script assumes one operating system. Every rule is a **ban with a named owner**, never a judgement about whether a construct is correct, and that is what keeps its false-positive rate zero by construction: it does not ask whether spawning a bare name is wise, it asks where that may live. `process.platform` belongs to `lib/arena/platform.ts`, an absolute path to the browser candidate table, a bare-name spawn to `host-binary.ts`, a `node_modules/.bin` path to `node-bin.ts`, a process-group signal to `chromium.ts`, a raw `symlinkSync` to `platform.ts`, and a separator spelling or a prefix containment to `utils/posix-path.ts`. Locale ordering is owned by **nowhere**, and that empty list is the claim. **A stale owner fails too**, so an exception cannot outlive the thing it was written for. The subject is scripts and **not suites**: a suite naming `win32` and a `C:` path is doing its job, and the win32 branches are covered from a Linux runner precisely because it does. Matches are filtered through the same lexer `check:docs` uses, so a construct named in a comment is described rather than performed; the two rules whose construct **is** a string say so in their own declaration, since skipping literals for those would skip the rule, and its suite plants one violation per rule because a gate only ever seen to pass is a gate seen to do nothing -- that fixture is what caught the `.bin` rule not firing at all. It also holds `pr.yml`'s matrix equal to `ci/arena/supported-os.ts` -- **names and blocking flags both**, so a leg cannot quietly stop counting nor a supported platform quietly stop gating -- and holds `pr-gate`'s `needs` equal to **every other job in that file**, stated as the complement rather than as a list somebody keeps in step: a job the one required check does not name is a job whose failure branch protection never hears about, which is exactly what a matrix living in a workflow of its own bought, since `needs` cannot cross workflows. It fails unless **exactly one** workflow names `CHROME_PATH`, exporting it everywhere being what made the candidate list unreachable. A separate set of rules is about the tracked **path** rather than the file, which is why no gate over content could see them: a **reserved device name** (`con`, `nul`, `com1`, whatever the extension or case), a character NTFS refuses, a segment ending in a dot or a space that Windows silently rewrites, a **case-only collision** that a case-insensitive filesystem resolves by overwriting one with the other, and a path past `MAX_PATH` once `CLONE_ROOT_BUDGET` is spent. None of those exist today, and the emptiness is the claim; the run states the longest tracked path against the budget so the margin is a number rather than a hope. | -| `check-workflow-scripts.ts` | a script a job runs cannot run in it. A job with no `bun install` still runs scripts, and Bun answers a bare specifier there by fetching it at whatever major the registry serves rather than the one `bun.lock` pins. That is not a hypothetical: the publish guard reached `typescript` through the assembler, resolved 7.x against a tree pinned to 6.x, and threw inside `< <(...)`, where `set -e` cannot see it -- so it read no paths, **git reads no path as every path**, and two packages were republished from a diff of the whole tree. Either half satisfies the rule: install first, or import nothing outside `node:` and this tree. It reads a **job** and not a workflow, since installing in one says nothing about another; it follows `bun run` through `package.json` so a name cannot hide a path, and stops rather than hangs on two names that run each other; and it takes specifiers off the **parse**, because `export const MANIFEST_IMPORT = /import manifest from '...'/ ` is a regular expression whose source is an import statement, and every scan short of a parser -- this repository's own and typescript's `preProcessFile` both -- reports its character class as a package. | +| `check-portability.ts` | a script assumes one operating system. Every rule is a **ban with a named owner**, never a judgement about whether a construct is correct, which keeps its false-positive rate zero by construction: `process.platform` belongs to `lib/arena/platform.ts`, an absolute path to the browser candidate table, a bare-name spawn to `host-binary.ts`, a `node_modules/.bin` path to `node-bin.ts`, a process-group signal to `chromium.ts`, a raw `symlinkSync` to `platform.ts`, and a separator spelling or a prefix containment to `utils/posix-path.ts`. Locale ordering is owned by **nowhere**, and that empty list is the claim; **a stale owner fails too**. The subject is scripts and **not suites**, since a suite naming `win32` and a `C:` path is how the win32 branches are covered from a Linux runner. Matches go through the lexer `check:docs` uses, so a construct named in a comment is described rather than performed, and the suite plants one violation per rule, because a gate only ever seen to pass is a gate seen to do nothing. It also holds `pr.yml`'s matrix equal to `ci/arena/supported-os.ts`, names and blocking flags both; holds `pr-gate`'s `needs` equal to every other job in that file, stated as the complement, since a job the one required check does not name is a failure branch protection never hears about; and fails unless exactly one workflow names `CHROME_PATH`. **The tracked path** has rules of its own: a reserved device name, a character NTFS refuses, a segment ending in a dot or a space, a case-only collision, and a path past `MAX_PATH` once `CLONE_ROOT_BUDGET` is spent. None exists, the emptiness is the claim, and the run prints the longest tracked path against the budget. | +| `check-workflow-scripts.ts` | a script a job runs cannot run in it. A job with no `bun install` still runs scripts, and Bun answers a bare specifier there by fetching whatever major the registry serves rather than the one `bun.lock` pins; inside `< <(...)`, where `set -e` cannot see it, the throw reads as a list of no paths, and **git reads no path as every path**. Either half satisfies the rule: install first, or import nothing outside `node:` and this tree. It reads a **job** and not a workflow, since installing in one says nothing about another; it follows `bun run` through `package.json` so a name cannot hide a path, and stops rather than hangs on two names that run each other; and it takes specifiers off the **parse**, because a regular expression whose source is an import statement reads as an import to every scan short of a parser. | | `check-generated.ts` | a file whose header says a script writes it does not say so in its name, or a `.generated.` file is neither tracked nor covered by a reason-carrying ignore entry. Both directions fail silently otherwise: an unnamed output is read as hand-written, and an ignored payload file ships a tag whose `intro/styles.css` `@import`s resolve to nothing. | -| `check-skill-spec.ts` | the consumer branch stops answering the Agent Skills specification: a second tracked file under the reserved name, a skill carrying no frontmatter, a `name` that is not lowercase alphanumeric separated by single hyphens or does not match the directory holding it, an empty or over-long `description`, a frontmatter key neither the standard nor `HARNESS_KEYS` declares, a value that does not parse as YAML because an unquoted scalar carries a colon and a space, or a body over the ceiling the standard recommends. **The count is the assertion rather than a tidiness rule**: a file carrying that name and no frontmatter is not a lesser skill but a broken one to anything that globs for the name, which is what the npm convention does, so seventeen index documents under it were seventeen skills that fail to parse. The subject is what git tracks, since the plugin is served from the tag where nothing runs a build and a file outside the index reaches no reader. **`HARNESS_KEYS` is empty, and the emptiness is the claim**: a key the standard refuses costs every client that validates the frontmatter, and the one this skill carried set `user-invocable` to the value it already defaults to. An entry the skill stops carrying fails as a stale departure. The YAML rule is the one the standard's own reference validator found and this gate did not: the description had read `not the skin: it ships`, which ends a plain scalar and turns the rest into a mapping, so the frontmatter had never parsed. A run finding nothing in the index fails rather than passing over a branch that reaches no clone. | +| `check-skill-spec.ts` | the consumer branch stops answering the Agent Skills specification: a second tracked file under the reserved name, a skill carrying no frontmatter, a `name` that is not lowercase alphanumeric separated by single hyphens or does not match the directory holding it, an empty or over-long `description`, a frontmatter key neither the standard nor `HARNESS_KEYS` declares, a value that does not parse as YAML because an unquoted scalar carries a colon and a space, or a body over the ceiling the standard recommends. **The count is the assertion rather than a tidiness rule**: a file carrying that name and no frontmatter is a broken skill to anything that globs for the name, which is how the npm convention finds one. The subject is what git tracks, since the plugin is served from the tag where nothing runs a build. **`HARNESS_KEYS` is empty, and the emptiness is the claim**: a key the standard refuses costs every client that validates the frontmatter, and an entry the skill stops carrying fails as a stale departure. `skills-ref validate` is the standard's own second opinion and worth running by hand. A run finding nothing in the index fails rather than passing over a branch that reaches no clone. | | `check-skills.ts` | `frameworks/INDEX.md` or a layer's own `INDEX.md` stops matching a fresh emit, or one falls out of the git index. The index tree is tracked rather than built, because the plugin is served from the git tag where nothing runs a build, so a stale copy is a wrong answer handed to every reader of that tag with every other gate green. Tracking is this gate's to assert because `check:generated` scans no `.md`. Fails on an empty declaration too, rather than comparing an index of nothing against an index of nothing. | -| `check-support.ts` | a consumer page stops agreeing with what the packages declare, or hands a reader one runner and no other. Three claims: the `@support` regions of the repertoire page match a fresh emit, so a range moves in `support-matrix.ts` and lands everywhere it is stated rather than in the one copy somebody remembered; no consumer page names a peer at a version the manifests do not declare, nor another node engine, which is how a hand-typed range goes stale with every other gate green; and no consumer page writes `bunx` without an alternative beside it. **The third is why this gate exists.** The shipped command is a Node program reading three `node:` modules, and `bunx` had been written into five consumer pages, so the branch documented a runtime dependency Arena does not have and nothing on the board could see it. The evidence level on every answer is asserted rather than assumed, because an answer allowed by a manifest and never exercised is worth having and is not worth the same as one a suite runs. | +| `check-support.ts` | a consumer page stops agreeing with what the packages declare, or hands a reader one runner and no other. Three claims: the `@support` regions of the repertoire page match a fresh emit, so a range moves in `support-matrix.ts` and lands everywhere it is stated; no consumer page names a peer at a version the manifests do not declare, nor another node engine; and no consumer page writes `bunx` without an alternative beside it, because the shipped command is a Node program reading `node:` modules, and a runner written alone reads as a runtime dependency Arena does not have. The evidence level on every answer is asserted rather than assumed, because an answer allowed by a manifest and never exercised is worth having and is not worth the same as one a suite runs. | | `check-prompts.ts` | a `.prompt.md` carries no `@api` region, or one that does not match a fresh emit from `contracts/api/components/.json`. A prompt is where a consumer stops reading, so the members have to be stated there; a hand-typed table would be a second copy of the contract that nothing holds, and the fix for a wrong cell is always the contract. Whether a component is contracted at all is `check:api`'s question, so an uncontracted one is counted rather than failed. | -| `check-rules.ts` | a surface that states the rules of the language stops matching a fresh emit, or a rule claims an enforcement the shipped audit does not carry. Four surfaces stated them and none was emitted from another, so the router could say no gate reads your application while `arena-to-prod --audit` already did, with every gate green. A rule names the audit tag that reports it, out of the `RULE_TAGS` the shipped module declares, or says why a source text cannot show it. | -| `check-register.ts` | a consumer document runs a sentence past 30 words, hangs more than one subordinate clause off it, or opens one on a pronoun that names nothing. The claims on that branch were true and one sentence carried four of them at once, which a reader holding the whole page resolves and a model holding a half written screen does not. `NOT_YET_REWRITTEN` is the map that empties: a pathspec awaiting the rewrite with its reason, and an entry whose every document already reads in this register fails as a stale allowance. | -| `check-api.ts` | a layer's implementation diverges from `contracts/api/components/.json`. **No map excuses a divergence**, deliberately: a contract forbids one, so it has nowhere for a second opinion to live. `DERIVED_DEFAULT` is not that and is the only map here: it names the members whose default is a token reached through a constant, which is a value a contract cannot state without copying the token and which the literal reader therefore cannot compare. It fails both ways, on an entry whose initialiser became a literal and on a non-literal initialiser nobody named, so the set of members compared against nothing is declared rather than silently skipped. Both layers run through one comparison: reading a default in one layer alone is what let `ArenaGrid.min` ship. | +| `check-rules.ts` | a surface that states the rules of the language stops matching a fresh emit, or a rule claims an enforcement the shipped audit does not carry. Every surface stating the rules is emitted from one declaration, so what a page says reads a consumer's application and what `arena-to-prod --audit` actually reads cannot disagree. A rule names the audit tag that reports it, out of the `RULE_TAGS` the shipped module declares, or says why a source text cannot show it. | +| `check-register.ts` | a consumer document runs a sentence past 30 words, hangs more than one subordinate clause off it, or opens one on a pronoun that names nothing. A reader holding the whole page resolves a sentence carrying four claims, and a model holding a half written screen does not. `NOT_YET_REWRITTEN` is the map that empties: a pathspec awaiting the rewrite with its reason, and an entry whose every document already reads in this register fails as a stale allowance. | +| `check-api.ts` | a layer's implementation diverges from `contracts/api/components/.json`. **No map excuses a divergence**, deliberately: a contract forbids one, so it has nowhere for a second opinion to live. `DERIVED_DEFAULT` is not that and is the only map here: it names the members whose default is a token reached through a constant, which is a value a contract cannot state without copying the token and which the literal reader therefore cannot compare. It fails both ways, on an entry whose initialiser became a literal and on a non-literal initialiser nobody named, so the set of members compared against nothing is declared rather than silently skipped. Both layers run through one comparison, since a default read in one layer alone is a divergence the other layer never reports. | | `check-playgrounds.ts` | a fixture under `frameworks/demos/` stops holding against the contract it seeds: a seed the contract does not declare, a value of the wrong type, an enum value outside its declared set, a required member or slot the fixture leaves empty, a bind naming something that is not an event or writing a payload into a member that cannot hold it, or a host that does not mark exactly one place for the component under test. Type-checking a seed is the whole point: a component drawn from a bad seed still draws, so the page renders and lies while every other gate stays green. It also holds the half of the citation rule only this gate can make: a cited `.demo.generated.html` is one a contract actually **emits**, and a bare page filename names something under `frameworks/`, which is the one shape a rename rewrites in every import and in no sentence. Whether a cited path merely exists is `check-citations.ts`'s, which reads every surface rather than a layer's. It opens none of them: every check here compares emitted SOURCE, so a page that compiles and then mounts nothing is invisible to it, which DOUBTS.md carries. | | `check-kitchen-sink.ts` | an arrangement under `frameworks/kitchen-sink/` stops pointing `check-pixel-parity.ts` at the whole library: a component the registry names that no arrangement places, an arrangement naming a component that does not exist, a component placed twice, a `frameworks/demos/` fixture that stops pinning a member its component would otherwise read off the clock or the host's own zone, or an emitted page that is not what a fresh run of the generator writes. Every one of those shrinks the surface the pixel comparison runs over while leaving it green, which is the failure that gate cannot see about itself: it reports zero differences over a page holding nothing exactly as it does over a page holding everything. | -| `check-citations.ts` | prose anywhere in the tree names a repository file that is not there, or cites `path:member()` naming a file that declares no such member. **Prose is three surfaces**: a line of a `.md`, a prose value in a `.json` read through `check-contracts-neutrality.ts:strands(rel, tree)`, which is where the prose keys are declared, and every comment in a file `check-docs.ts:allowsHeader(repoRelativePath)` admits, which is the one header a script or a suite may carry. The fourth candidate, a member doc under a component directory, is deliberately not read: `check:api` keeps it equal to the `description` this gate already reads at the contract, so reading the copy judges one fact twice. A value is addressed by its key path rather than by a line, for the reason a document cites a member rather than a line, and a `.json` that does not parse is reported, since a file the walk cannot open contributes no passage and reads like one with nothing to say. `BARE_EXEMPT` is the map the wider reach needed: a scale step is spelled the way a document extension is. Both defects it was written for had stood with every gate green: a document citing a spec the charter deletes once executed, and a build chain naming a step that had been renamed. A match is judged only when it names a **file**, because prose shortens a path freely and a shortened one is not a claim, and a leading slash is a URL from a site root rather than a repo path. The roots are read from the tree, so a new top-level directory is covered the day it lands, and a run finding none of them fails rather than reporting every absolute path missing, which is the vacuous pass in reverse. `EXEMPT` names what is absent on purpose, with a reason each, and an entry no document cites any more fails as a stale allowance. **A root git ignores leaves that list**, asked of git rather than guessed, because `docs/` exists on the machine a spec was written on and on no clone, so the gate would otherwise scan a different alternation depending on where it ran -- and a gate whose subject varies by environment is the defect this whole rule set is about. Closing that opens the other half, which it also holds: **a document citing a file under an ignored root fails**, since such a citation passed here for the wrong reason, the root not being in the alternation at all, while no clone could follow it. **The walk skips those roots too**, and that half was found by filing a real plan under `docs/` and watching the gate judge it: excluding a root from the alternation and still walking INTO it fixes which paths are recognised and leaves which documents are read varying by machine, which is the same defect with a different subject. `basenames()` skips them for the same reason, or a bare citation would resolve against a scratch file that exists on one machine. | -| `check-agents.ts` | a level on the contributor branch is reached by no chain of links from the router, or a `README.md` is back on this branch. The levels are what the walk finds rather than what a list declares: the list this gate used to carry had gone thirty-five short while reporting itself complete, which is the shape a hand-maintained subject always ends in. **A level nobody wrote is deliberately not a failure**, because the convention hands a reader the nearest level above it and that is a correct answer; a level nobody LINKS is, since proximity is automatic for an agent and unavailable to a reader, so the same tree answers the two of them differently. `emitted()` drops a copy a build left behind, and it is scoped so `scripts/build/` stays in the walk: widening it by one word took five real levels out of the subject with the gate still printing a pass. `SURVIVORS` names every `README.md` kept and why each is not a contributor document, and an entry whose file has gone fails as a stale allowance. | -| `check-exports.ts` | a symbol a barrel carries to the root of a package has no home on that package's npm page. The router already states the rule and nothing held it, so a helper written for one component reached a consumer beside the components while `PACKAGE.md` said a symbol it does not name carries no compatibility promise. That pair is worse than either half: the tested focus trap behind a dialog ships, reaches a consumer who has been told a lightbox is theirs to write, and is disclaimed by omission, so they write a second one. **The subject is what a consumer CALLS**: a type is reached through the value whose signature carries it, so naming the hook names its options and a page listing both says one thing twice. `INTERNAL` declares what reaches the root and is nobody's to lean on, with the reason, and fails both ways. Derived from the barrel rather than listed, because a list goes short the first time a module gains an export. | -| `check-classes.ts` | a class a consumer writes ships inside a stylesheet and has no home on that package's npm page. It is the other half of the rule above: a symbol reaches a consumer through the barrel, a class reaches them through the tarball, and the tarball announces nothing. The two density classes ship inside the spacing sheet and a consumer reading either npm page cannot learn that `.arena-compact` exists at all, which is the shape this gate is written against. **The subject is what a consumer puts on their own markup**, derived from `scripts/lib/arena/package-assembly.ts:CSS_CHAIN` and `scripts/lib/arena/package-assembly.ts:CONSUMER_SHEETS` rather than listed here, so a class added to a shipped sheet is caught on the day it ships. `NOT_WRITTEN` declares the classes that are Arena's own name for something rather than a consumer's to write, with the reason, and fails both ways. | +| `check-citations.ts` | prose anywhere in the tree names a repository file that is not there, or cites `path:member()` naming a file that declares no such member. **Prose is three surfaces**: a line of a `.md`, a prose value in a `.json` read through `check-contracts-neutrality.ts:strands(rel, tree)`, which is where the prose keys are declared, and every comment in a file `check-docs.ts:allowsHeader(repoRelativePath)` admits. A member doc under a component directory is not read, because `check:api` keeps it equal to the `description` this gate already reads at the contract. A value is addressed by its key path rather than by a line, and a `.json` that does not parse is reported, since a file the walk cannot open reads like one with nothing to say. A match is judged only when it names a **file**, because prose shortens a path freely, and a leading slash is a URL from a site root. The roots are read from the tree, so a new top-level directory is covered the day it lands, and a run finding none of them fails. `EXEMPT` names what is absent on purpose and `BARE_EXEMPT` a bare name that is not a document, each with a reason, and an entry nothing cites any more fails as stale. **A root git ignores leaves the alternation and the walk alike**, asked of git rather than guessed, so the gate reads the same subject on every machine, and a document citing a file under an ignored root fails, since no clone can follow it. | +| `check-agents.ts` | a level on the contributor branch is reached by no chain of links from the router, or a `README.md` is back on this branch. The levels are what the walk finds rather than what a list declares, because a hand-maintained subject goes short while reporting itself complete. **A level nobody wrote is deliberately not a failure**, because the convention hands a reader the nearest level above it; a level nobody LINKS is, since proximity is automatic for an agent and unavailable to a person. `emitted()` drops a copy a build left behind, and it is scoped so `scripts/build/` stays in the walk. `SURVIVORS` names every `README.md` kept and why each is not a contributor document, and an entry whose file has gone fails as a stale allowance. | +| `check-exports.ts` | a symbol a barrel carries to the root of a package has no home on that package's npm page. A symbol reaching a consumer undocumented is disclaimed by omission: a consumer told a lightbox is theirs to write writes a second focus trap beside the tested one the package already ships. **The subject is what a consumer CALLS**: a type is reached through the value whose signature carries it, so naming the hook names its options and a page listing both says one thing twice. `INTERNAL` declares what reaches the root and is nobody's to lean on, with the reason, and fails both ways. Derived from the barrel rather than listed, because a list goes short the first time a module gains an export. | +| `check-classes.ts` | a class a consumer writes ships inside a stylesheet and has no home on that package's npm page. It is the other half of the rule above: a symbol reaches a consumer through the barrel, a class reaches them through the tarball, and the tarball announces nothing. **The subject is what a consumer puts on their own markup**, derived from `scripts/lib/arena/package-assembly.ts:CSS_CHAIN` and `scripts/lib/arena/package-assembly.ts:CONSUMER_SHEETS` rather than listed here, so a class added to a shipped sheet is caught on the day it ships. `NOT_WRITTEN` declares the classes that are Arena's own name for something rather than a consumer's to write, with the reason, and fails both ways. | | `check-agents-spec.ts` | the contributor branch stops answering [the convention published for its filename](https://agents.md), which fixes a file at the root, that name exactly, plain Markdown with no required field, a level resolved by proximity, and a command an agent runs because a page listed it. The convention publishes no validator and no schema, so this gate is the whole of what holds Arena to it and `npx agents-lint` is a third party's second opinion rather than the standard's. **The commands are derived and never listed**: a script whose name carries no colon is an entry point and one with a colon narrows a phase, so the root page has to name each entry point and gains the obligation the day a new one lands. Whether a name a document spells is a script at all belongs to `check-vocabulary.ts:BUN_RUN` and is not asked twice. `DEPARTURES` carries what Arena does differently with the reason, and it fails both ways: a departure the tree makes and nobody declared, and a declared one the tree has stopped making. | | `check-duplication.ts` | two documents state more of the same sentences than their limit allows: three anywhere in the tree, or `FAMILY_LIMIT` between two components filed under one category. The unit is a normalized sentence of at least `MIN_WORDS` words, compared through an index over every document at once rather than pair by pair, and a **generated region is cut out before anything is compared**, because it is one source rendered into many files and leaving it in made every prompt overlap every other one through the same emitted line. Two shapes are legitimate: `sameSubject`, two documents with one file name, which is a document rendered per layer and a generator question rather than a duplication one; and `sameFamily`, two components stating the rule they both answer to, which is `FAMILY_DEBT`, a real duplication that is bounded rather than paid because a reader opens exactly one prompt and moving the rule up a level would charge every build for a family it is not using. | | `check-deadlines.ts` | a bare span sits in a wait position, or a suite's budget does not name every deadline its import closure declares. A literal in a case timeout, a `withTimeout` bound or a `sleep` is a number read off whichever machine wrote it, so it is right where it was written and silently wrong everywhere else: it expires early and reports its subject absent, sending a reader into the component that has no defect. A budget under one of the deadlines it can spend abandons the callback with its subject still running, and every file loaded afterwards then reports an error naming neither. The closure is read as **text** by `scripts/graph/script-closure.ts` and never imported, because importing a script to read it runs it. Both maps carry a reason per entry, and the paired suite asserts on them by name. | | `check-consumer.ts` | the `arena-to-prod` command a package ships stops doing what the npm page says it does, run against a real fixture project rather than reasoned about. It assembles each package, installs it into a temporary project, runs the CLI and reads what came out: the theme sheet's import chain, `"components": "auto"` resolving to the sheets a source actually needs, a component name the package does not ship being reported rather than silently dropped, and a source naming a symbol this package does not export resolving to nothing, since no alias exists to soften it. `DOCUMENTED_LIST` reads the sheet list back out of the shipped `README.md`, so the page and the command cannot drift apart. | | `check-architecture.ts` | a layer withdraws one of the architecture promises the consumer branch makes on its behalf. **Arena is adopted on a tree whose last nodes tell a project it may server-render, prerender or embed as a fragment, and which dependencies each answer costs**, and every one of those answers is a property of these sources rather than a policy anybody stated: a browser global read while a module evaluates throws during a server render at import time rather than at paint, `useLayoutEffect` warns there and is the classic tell, an import outside `ENVELOPES` is a framework decision the library made on a consumer's behalf and arrives as a dependency they did not choose, and `@angular/router` stops being optional the moment anything outside the metadata entry point imports it, which turns an install-time promise into a run-time break. Two escapes are accepted because the layers already use them and both are correct: a read inside a function, an effect or an after-render hook, and one behind a `typeof` guard. Reading goes through the same lexer `check-docs.ts` uses, twice over: the pass that reads an import specifier blanks comments and keeps strings, since a specifier IS a string, and every other pass blanks both, since a global named inside a string is prose. Suites, demos and build outputs are out of scope, because a suite spawns a browser on purpose. | -| `check-vocabulary.ts` | a document states a naming convention no file in the tree answers to, cites a sibling by a bare FILE name nothing carries, which `BARE_FILENAME` reads as a name carrying an extension so a bare symbol or component name is nobody's claim here, tells a reader to run an npm script `package.json` does not declare, names a `README` on the branch whose one filename is `AGENTS.md`, or hands over a `find` whose pattern matches nothing where it looks. Each of the five had a live defect the day it landed, and every one had stood green: the claim is prose and the subject is the tree, so nothing was comparing them. A convention is the expensive one, since a reader hunting for the file it names cannot tell a stale document from their own mistake. `NAMED_BUT_NOT_HERE` carries what is named and correctly absent, the consumer's own files and what the assembled package ships, and `README_MEANS` the documents that mean the page npm shows; a deliberate absence is read from `check-citations.ts`'s own map rather than declared twice. Reach is the whole tree for the same reason `check-citations.ts`'s is: a gate names the shape it reads, never the shape it sits beside. | -| `check-routes.ts` | a declared reading route costs more than its budget, names a stop that reaches no document, has fallen so far under its budget that the number stopped applying pressure, or carries a reason that is a history rather than a reason. **A route is what a reader pays PAST the page it opens with.** `ENTRIES` declares each branch's router once, with a budget of its own, so a router shared by every route is argued for its reader and not once per route. `ROUTES` is the registry of what is paid after it, and a stop is a pathspec charged for the **largest** file it reaches, so a route is measured at the worst case a reader can meet. **A route whose reader answers a question and walks one way declares `branches` and is charged its worst branch.** Every budget carries its reason as a string, because a route grows by a paragraph that reads as an improvement where it is written; `reasonProblems` holds that string to why the number is right today, so it names no figure, says no "raised" and stays under `REASON_MAX_CHARS`. | -| `check-community.ts` | the surface a stranger meets before any code stops holding: a declared outward-facing file nobody wrote, the governance policy stated in a second document or in none, a contact link naming a path the default branch does not carry, or a `context7.json` that has drifted from the router. `OUTWARD` names each file with the reason it exists, because none of them is reachable from the router and nothing else would notice one going. The two failures it was written for are both silent: a repository that reads as maintained and answers 404 to the reporter who followed its own contact link, and an indexed document the consumer branch never meant to hand over. Context7 has no router, so what it indexes is what it hands back, and every contributor document reaching the index is a contributor answer to a consumer question; an exclusion covering nothing fails as a stale allowance, in both directions. **Which names it guards is derived and never listed**: `contributorBasenames()` takes every document the walk finds, drops what a build emitted, what the config already excludes by folder, what `check-docs.ts:isConsumerDocument(repoRelativePath)` calls the other branch's, what `OUTWARD` sends to a stranger, and the one README that belongs to neither. The list it replaced named four while the tree carried six, and the two it was short of were a chart family's own tour and a page written the same session, both of them indexed and neither of them for a consumer. Every string in `rules` is required to be a span of `SKILL.md`, compared with runs of whitespace flattened, so the copy cannot go quietly false and rewrapping a paragraph is not a failure.. **It also holds the file to the schema it declares**: that schema sets `additionalProperties` to false, so a key outside its set is not one field ignored but a document that fails to validate whole, and what gets indexed is whatever the service falls back to. The set is written here because the schema is published rather than vendored, and a key it gains is a key this list has to gain with it | +| `check-vocabulary.ts` | a document states a naming convention no file in the tree answers to, cites a sibling by a bare FILE name nothing carries, which `BARE_FILENAME` reads as a name carrying an extension so a bare symbol or component name is nobody's claim here, tells a reader to run an npm script `package.json` does not declare, names a `README` on the branch whose one filename is `AGENTS.md`, or hands over a `find` whose pattern matches nothing where it looks. The claim is prose and the subject is the tree, so nothing else compares them. A convention is the expensive one, since a reader hunting for the file it names cannot tell a stale document from their own mistake. `NAMED_BUT_NOT_HERE` carries what is named and correctly absent, the consumer's own files and what the assembled package ships, and `README_MEANS` the documents that mean the page npm shows; a deliberate absence is read from `check-citations.ts`'s own map rather than declared twice. Reach is the whole tree for the same reason `check-citations.ts`'s is: a gate names the shape it reads, never the shape it sits beside. | +| `check-routes.ts` | a declared reading route costs more than its budget, names a stop that reaches no document, has fallen so far under its budget that the number stopped applying pressure, or carries a history where its reason should be. **A route is what a reader pays PAST the page it opens with.** `ENTRIES` declares each branch's router once, with a budget of its own, so a router every route shares is argued for its reader and not once per route. `ROUTES` is the registry of what is paid after it, and a stop is a pathspec charged for the **largest** file it reaches, so a route is measured at the worst case a reader can meet. **A route whose reader answers a question and walks one way declares `branches` and is charged its worst branch.** Every budget carries its reason as a string, because a route grows by a paragraph that reads as an improvement where it is written, and `reasonProblems` holds that string to why the number is right today: no figure, no "raised", and no more than `REASON_MAX_CHARS`. | +| `check-community.ts` | the surface a stranger meets before any code stops holding: a declared outward-facing file nobody wrote, the governance policy stated in a second document or in none, a contact link naming a path the default branch does not carry, or a `context7.json` that has drifted from the router. `OUTWARD` names each file with the reason it exists, because none of them is reachable from the router and nothing else would notice one going. Context7 has no router, so what it indexes is what it hands back, and a contributor document in the index is a contributor answer to a consumer question; an exclusion covering nothing fails as a stale allowance, in both directions. **Which names it guards is derived and never listed**: `contributorBasenames()` takes every document the walk finds, drops what a build emitted, what the config excludes by folder, what `check-docs.ts:isConsumerDocument(repoRelativePath)` calls the other branch's, what `OUTWARD` sends to a stranger, and the one README that belongs to neither. Every string in `rules` is required to be a span of `SKILL.md`, compared with runs of whitespace flattened, so the copy cannot go quietly false and rewrapping a paragraph is not a failure. **It also holds the file to the schema it declares**: that schema sets `additionalProperties` to false, so a key outside its set invalidates the whole document, and the set is written here because the schema is published rather than vendored. | | `check-site.ts` | the published site stops holding to what it claims: a page pointing at an asset the output does not carry, a page the manifest declares and the build dropped, one published that nothing declares, a module one of its pages imports and the output does not carry, a sitemap missing an entry, or a CNAME for another domain. The first is load-bearing and `intro/AGENTS.md` says why nothing else catches it: a broken path fails silently in both directions, and an unstyled page that fits its box passes a look. The module half is that assertion one notation in: a `src` resolving says nothing about what the entry imports, and an import that 404s throws before the entry runs, so the page keeps every href and renders nothing. The shape a static host breaks and a dev server hides is a directory answered at request time with a listing. It SKIPS when `dist/site/` is absent, since a walk of nothing reports every link resolved; what makes a dropped playground visible is `site-pages.ts` asking the component map rather than the filesystem. | | `check-icons.ts` | a `ph-` class name in the tree is not a Phosphor glyph, or is a glyph the weight beside it does not carry. A contract takes an icon as a string, and a string nothing checks is a typo that renders an empty box, with no error and no failing gate. The truth is read out of the installed `@phosphor-icons/web` rather than typed here, because a list of 1500 names ages the first time the package does, and finding no stylesheet is an explicit failure rather than a pass over nothing. `SCANNED_ROOTS` is the scope and `EXEMPT` is empty on purpose: a name Phosphor does not have is a typo rather than a case. | | `check-behaviour.ts` | a component declares no `.behaviour.json`, names a pattern or requirement that does not exist, or the two layers disagree without saying why. A green run is a **coverage** claim, never an accessibility one. | @@ -42,9 +42,9 @@ The domain for a gate that reads two or more layers at once, or the repository r | `check-contracts-resolve.ts` | a design token in the carried set stops resolving inside that set alone. Not a second `check-dtcg.ts`: that one asks whether a value is well formed here, where the whole tree is present, and this asks whether a reference still points at something once the CSS chain and the generated emission are gone. A reference resolving in this tree only through a file the package does not carry is the defect that lands in somebody else's generator and that nobody here would ever see. A cycle is reported rather than walked, and a run finding no reference at all fails, since the rhythm steps and the chart paddings are authored as aliases on purpose. | | `check-structure.ts` | a component is declared in two categories, sits in a category `frameworks/Components.json` assigns elsewhere, is named by no declaration, or is present in no layer. It says nothing about whether the category is the **right** one. | | `check-script-tokens.ts` | a `Tokens.generated.*` module disagrees with the DTCG source or the CSS, a `script: true` flag is orphaned, or `ArenaCatSlot` stops being exactly `1..catSlots` in order. | -| `check-intro-generated.ts` | a page bundle under `intro/` does not match its source, or a page loads a module entry that is not a bundle. Those bundles are **tracked**, because the Claude Code plugin is served from the git tag and nothing there runs a build, which is the same reason `contracts/design-generated/` is tracked; a tracked build product is a promise, and this is what keeps it. The second half is the load-bearing one: a page whose `