Handoff

Stage 13, the last row. What is being handed over, where to start reading it, how it behaves, what each screen is made of, and what is still open - each of them counted by an instrument rather than remembered.

Handed over on 2026-08-23, and brought level with the live product on 2026-09-30. The figures on this page are the handoff's and keep that corpus. The product built from this package is live at tendd.co; what it changed since, why, and what this repository did about it is the live product, since v1.0.

571 files 77 in the design system 0 references leaving it 1 dependency it cannot carry 21 re-runnable instruments 3522 links, 0 dead 30 rows open of 114

Start here: three doors and the entry they hang off

Everything below is reachable from these four addresses, and the same four are written in README.md in the same pass, because a README is the page a person opens on GitHub and sending them one click further for the address of the product would be the route into nowhere this stage exists to fix.

If you wantOpen
Every screen, in colour, with its statesdesign/overview.html
How the product behaves, flow by flowhandoff/docs/behaviour.md
What any one screen is assembled fromhandoff/docs/map.md
Where accessibility lives and how to re-check ithandoff/docs/a11y.md
What a stranger could not work out on their ownhandoff/docs/onboarding-gaps.md
The rules this repository is built underCLAUDE.md, loaded into every session
Why any single value is what it isdocs/decisions.md, newest on top, never edited
The visual language as a documentDESIGN.md, generated from the shipped code
Where every figure on a screen comes fromdocs/bank-connection.md
To lift the system into another projectdesign/system/ plus its own CLAUDE.md
To review this as an outside criticAGENTS.md

Eight markdown files have had no visible place at one point or another, and this section is what closed them. Four were closed in August: AGENTS.md and DESIGN-artifacts.md, the draft the design document grew out of, and research/docs/product-model.md and research/docs/quality-check.md, the last of which was named by nothing at all, not even by another document. The five documents of this stage joined them the day each was written. It is worth saying out loud rather than quietly enjoying: handoff13.cjs counts "md files named by at least one html", this is an html, and the naming is the sentence you are reading. An artefact with no visible place does not exist for the person who decides, and three of these four fell out of the count again the moment this page was rewritten, which is the whole argument for running the census rather than remembering it.

And the check that finds them was itself hiding one. It asked whether any page contains the file's name as a SUBSTRING, so handoff/docs/map.md counted as shown by eleven pages, every one of which was naming ia/docs/sitemap.md. Corrected on 2026-08-23 to a boundary match: a preceding slash is fine, a preceding letter is not. The instrument whose whole job is finding an artefact nobody can reach had been unable to see one for five days.

The package boundary: what this is, and what it is not

This is a clickable product, not an application. There is no data layer, no API, no router, no persistence and no session. Nothing here talks to a bank, charges a card or remembers anything between two page loads. A reviewer can walk every flow end to end and a developer can read every rule, and neither of those is running software.

Every STATE is its own file. 17 screens carry 39 states between them and that is 58 html files: home.html, home-loading.html, home-empty.html, home-error.html, home-few.html, home-one.html are one screen. A real build collapses those 57 files back into 17 screens with states, and nothing in this repository says who owns that translation - it is the first decision a receiver makes and it is named here because it was written down nowhere.

What is real and ships as it standsWhat is not here at all
The design system. css that is production code, lifted and measured. See the lift test belowA backend. No database, no auth, no Plaid, no Stripe, no telemetry. The stack in CLAUDE.md is a hypothesis and nothing here depends on it
The copy. Every string on every screen is final and owned by voice/docs/microcopy.md, which is where a new one comes fromState. A filter does not filter, a form does not submit, a switch does not persist. The one exception is the trend chart's cursor
The structure. One IA node per screen, one flow per journey, and the behaviour spec that joins themA router. Navigation is an anchor from one file to another. What that becomes is the receiver's decision
The behaviour. Steps, states, edge cases and validation, every row naming its sourceAnything out of MVP. Budgeting, investments, a native app, bill negotiation, bill pay, a household view. Named in CLAUDE.md

Two scripts exist and they are not the same kind of thing. design/system/behaviour.js belongs to the system, is the only javascript it owns, travels with it, and draws the trend chart's cursor: 7 of the 57 screens load it. design/story.js belonged to the landing and drove its scroll choreography: 1 screen loaded it. Everything else on every screen is html and css. Updated 2026-09-30: the landing is now Light 2 and its motion is the product's own, bundled into design/landing.js (three.js and Lenis, generated, with its rebuild command in its banner), plus design/auth.js for the sign-in dialog; story.js is gone. See the live product, since v1.0.

Which theme is the main one

Light is the main one, dark is complete, and since 2026-08-23 a person can choose. The light palette is the one written on :root; the dark palette is written on [data-theme="dark"] and overrides it role by role. Every semantic role in the system exists in both or it does not exist, which is a rule of stage 08 and is checked rather than promised.

The switch you see on a screen is not the one this is about. That one belongs to design/_screen.css, the reviewer's chrome, together with the panel that names the screen and its IA node. Chrome is not shipped, and the map of the product deliberately does not include it. The PRODUCT's control is a switch in a group of its own on Settings, How Tendd looks, above the rows that leave the screen.

This was the sharpest question a stranger asked, and for six hours the honest answer was that nobody had decided. The system had carried both themes since stage 08, and the product had no control, no string for one, and no prefers-color-scheme anywhere: a capability that was fully paid for and a feature that had never been specified. Three readings were available and the founder chose the third: follow the machine, stay light-only and say so, or give a person the choice. The two new lines went through Voice before they went on a screen, the block went into IA node 6.16, and the structure went into the frozen grey twin with the decision written into that file, which is the only way this project allows the structure contract to change after Voice.

And the product still does not follow the machine. prefers-color-scheme stays at zero occurrences in base.css, in tokens.css and in both chrome stylesheets, deliberately: a person who has chosen a theme has said what they want, and a money app that changes appearance because the sun went down is doing something its user did not ask for.

What a real build still owes is one line in a data model, and it is written in behaviour.md rather than guessed here: where the choice lives. On the account, so it follows a person to a second device, or in the browser, so it is per-device. The switch in this package draws the state and does not keep it, exactly like the four alert switches above it, because this is a clickable product with no runtime and no persistence.

The route through the repository

Thirteen stages, one folder each, and every one of them opens on a page rather than on a folder listing. The order below is the order the work happened in, which is also the order that makes it readable: each stage's material is the previous stage's answer.

FolderWhat is in itWhere to start
index.htmlThe entry page. It renders the roadmap from _nav.js, which is the single registry of the project: a stage is switched on in one place and nowhere elseindex.html
research/Who this is for and what they are trying to do: the market, four personas, the jobs, and the journey before and afterresearch.html
ia/The structure: a sitemap, the flows, one page per node with the block bank, the seven global elements and one accessibility contractstructure.html
wireframes/The grey structure contract, frozen since Voice, and the answer to why every screen exists twice. One grey page per state, real copy, no colour. It is read-only and it is not a source of truth: after the rollout the coloured screen is the product, and this folder is process historyoverview.html
voice/The voice, and the line inventory that owns every interface string in the productvoice.html
design/The product in colour. The folder opens on the product home; the map of all 58 pages is the hub beside itindex.html, overview.html
design/system/The code of the design system, liftable whole and carrying its own CLAUDE.md: tokens at two levels, the base, the components, the patternsCLAUDE.md
design/kit/The stand that shows it: a page per component, the foundations, the architecture and the backlogwhy.html
handoff/This page and its five documents. It adds what no other page answers and it repeats nothingyou are here
at the rootCLAUDE.md the rules, docs/decisions.md the log, DESIGN.md the language read out of the code, AGENTS.md the entry for an outside critic, README.md the indexREADME.md

The route is checked and not asserted. design/kit/screens/route13.cjs resolves every href and src in all 245 pages against the file that writes it, every markdown link in all 72 documents, and then walks the project in a real browser from the entry page: 3522 links, 0 dead; 55 markdown links, 0 dead; 0 pages carrying a markdown link pasted into html. A browser is not optional there, because the roadmap on every page is drawn by _nav.js at runtime and a static read of the entry page sees none of it.

How it behaves

A screen shows a state. What it does not show is what leads to that state, what happens at the edges, and what a field will refuse. That is handoff/docs/behaviour.md, and the thing that makes it a specification rather than a plausible description is the source column: every row names the file it was read from, and a row with no source is not written into the spec at all. It goes to a list addressed to a person.

5flows, all five described
0deliberately not described
5kinds of source, each declared
7rows that had no source, all 7 now decided
9states no click can reach

The roll-call closes at five. Flows in ia/docs/flows.md = described 5 + deliberately not 0. The five sources are the screen file, the flow, the IA node, the figures document - approved as a source on 2026-08-23, having been used and marked until then - and the grey wireframe - and the last of them is used zero times in all five flows, which is the honest version of "the grey is not a source of truth": it was available and it was never needed.

Nine states cannot be reached by clicking, and every one of them is right. The click graph reports them, and the spec answers each with the real trigger a build will fire: a bank connection that goes stale, a merchant with no cancellation guide, a person who has never had an account, a renewal that fails on a card that was working. The largest single reason is that the canonical person in this package is on Free, so the two screens that only a paying account ever sees have nothing to link them.

What each screen is made of

handoff/docs/map.md is read off the code by design/kit/screens/map13.cjs, which serves the repository to a browser, walks all 57 screens, and writes its raw output beside itself. It is in three parts and the third is the working one.

PartRowsWhat it answers
A. Screen to zones and components57What is on this screen, how many regions its markup declares, and how many of them the line inventory addresses
B. Component to the tokens it reads73Every var() inside that component's own css, and its reach in screens
C. Token to the screens it stands on163If I change this value, what moves. Derived by INVERTING A and B rather than by walking the code a second time

The inversion is two knees deep and that is the whole of why it works. A component reads colour only through a semantic role, so a flat inversion would name no primitive at all and the entire primitive level would report as unused. The chain followed is component to semantic role to primitive, in both themes, since a dark role points at a dark primitive.

The roll-call closes and the idle control is a number rather than an impression. Screens named by the registry 57 = mapped 57, with nothing on disk that the registry does not name and nothing named that is not on disk. Zero components stand on no screen, asked here for the third time and against the whole product rather than a sample. Three tokens are reached by no chain, and all three are accounted for: two are the width points, which a container query cannot read through a var(), so the token holds the decision and the literal in the query is its application; the third was declared idle and kept, with the date, in the file that declares it.

Accessibility

handoff/docs/a11y.md introduces nothing. Accessibility was built at stages 08 to 11; what was missing was not the work but the PROOF. So every row carries four things and the third is the one that matters: item, where it is in the code, how to check it, and a status of exactly two words. "Confirmed" means a run on 2026-08-23 with the instrument named in the row, never a memory of stage 08 doing it, and a row for which no instrument can be named never gets the word.

3100text nodes on screens, 0 below threshold
908tab stops, 0 with no indicator
572control borders and marks, 0 below 3:1
17elements loop when motion is allowed, 0 under reduce
41624text nodes on the pages around them, 6 left and all specimens
13rows confirmed of 15, 0 debts left

Two rows were real failures when this stage measured them, and both closed the same day. Nine screens scrolled sideways at twice the default text size, all on a phone and none at desktop width; and the trend chart, made focusable at runtime and not a native control, wore the browser's own focus ring. They went to the backlog as debts, because the product was accepted at stage 12 and an edit would have voided every pixel comparison it stands on - and then the founder lifted the freeze. Nine became 0 of 114 and the ring is the system's, with zero boxes moved at 360 or at 1440.

The zoom failure was the width sweep's own lesson arriving one axis later, and the fix was the same stage answering itself. Stage 10 swept 58 widths by moving the viewport and holding the text; a person with low vision does the opposite. Nobody had asked the question in that direction. What closed it is the reason stage 10 refused to write its two width points in pixels: a container query in rem reads the viewport in units of the reader's text, so one threshold releases a label that may not fold, and it releases it under zoom and nowhere in the product's own sweep. It is the first time that refusal has paid for itself.
The blind spot this stage could not close all day was that no instrument read a page which is not a screen, and it is closed. The accessibility run took the 57 coloured screens, the semantics run took those and their grey twins, and both stopped - while a receiver meets a stage page BEFORE any product screen, because the roadmap is how they arrive. It was two holes and not one, which is why it lasted: the corpus never included those pages, AND the contrast walker rooted itself at the product and returned nothing when it found none. Check 5 reads 129 pages and 41624 text nodes, and what it found was a shape rather than a tail: 2330 nodes below the floor, of which eight colour literals caused 2203, copied page to page across a documentation half written before the design system. Ten literals darkened took it to 6, and those six are text that DEMONSTRATES a colour on a page about colour. 87 of 129 pages had no main landmark and 0 do now.

And the instrument had to be made deterministic before any of its numbers could be believed. Its first two full runs reported catastrophic contrast failures, sixteen in one and twenty-eight in the next, on different screens each time, and none of them reproduced in isolation. The cause is in the system: every interactive element transitions its colours over the state duration, so asserting a theme starts a cross-fade across the whole page and a sample taken inside it compares one theme's ink against a surface halfway to the other. The contrast and focus passes now run with reduced motion emulated, which makes the product's own reduce block zero every duration; the motion pass needs a context of its own, since measuring the baseline inside a reduced one would report the reduce block as the product's motion, which is a check that always passes and proves nothing. Two consecutive runs now return byte-identical output, and that determinism is part of what "confirmed" means.

How to add a feature

There is a ready prompt for it: handoff/docs/one-shot.md. Copy it, put your feature in the one blank it leaves, and hand it to whoever is building - a person, or a model in a fresh session. It leads to all three documents above, carries the growth rules word for word from the files that own them, and repeats no value from any of them.

It was written to be tested, and it was tested twice on 2026-08-23 by two readers with no memory of this project, on two different features. Each was given the prompt and nothing else, and each was sized before the run rather than after it - four states and three - because a reader who does not stumble on a feature of two states has proved nothing. Both built something the instruments accepted: zero style violations, zero contrast or focus failures across the grown corpus in both themes, zero prose past the reading measure at fifty-eight widths. Neither screen is in the product, and the reason is in "deliberately not done" below.

2readers, two features
0questions either could ask
13confident readings the first could defend and might have got wrong
12the second
13paragraphs of the prompt written by those two runs
2findings about the product, both in the backlog

The second list is not shorter, and saying so is the point of running the exam twice. Thirteen readings became twelve, which is no improvement worth claiming. What did change is WHICH readings: the second reader named five paragraphs of the prompt that did real work and said what each one saved it, and only one item on its list repeated a difficulty from the first. That one is the paragraph the first run's own fix created - the test for whether a feature is a new screen or a state of an existing one - and the second reader had to break the tie itself because the rule and the cautionary tale beside it pointed at two different tables. It is a three-row table now instead of a sentence. A prompt that is measurably better and still produces a list that long is a prompt that is working and is not finished, and the honest form of that is a number rather than a paragraph of comfort.

What the exams found about the product, rather than about the writing, is two rows and both are in the backlog: a rewrite that the line inventory's own log records as done and that was applied by halves, still carrying the word two documents agreed to drop; and a shipped button label that breaks the voice's own rule about how a button addresses a person, in three places at once. Neither had been seen before, because no instrument compares a line in the inventory against the rules of the voice, and nobody had read the two side by side.

And one thing the exam found about an instrument. The click graph printed "still unreachable, and every one is a state no product screen should link to" - a verdict rather than a measurement, true of the corpus it was written on. The second reader's new screen landed in that list as a base screen and was told by the instrument that it belonged there. It reads the registry now and says which of them are base screens, because a base screen nothing opens is a finding and a state nothing opens is expected, and an instrument that cannot tell them apart is answering a question nobody asked.

The census: what is on disk

Counted by group rather than by folder, because a receiver's first question is not "how many files" but "how much of this is the product, how much is the argument for it, and how much is scaffolding I can ignore". Every figure comes from design/kit/screens/handoff13.cjs, whose corpus is git ls-files - what a receiver actually gets - with a walk of the tree kept as the fallback for a copy that is not a checkout. Neither is a list somebody typed.

GroupFilesLinesWhat it is
Product screens, colour588225What ships. 17 screens, 39 states, every one with a grey twin
Product screens, grey585951The structure contract, frozen since Voice
The design system772037176 css and 1 js. The thing that is meant to travel
The stand8720329design/kit/: a page per component, per pattern, per foundation
Stage pages3618422One account per stage, this page included - which makes this the one row that moves when the page does, so it is the census's checked-in run and not a live figure
Markdown72261722 loaded into every session, 70 read on demand
Instruments214424.cjs in design/kit/screens/, each re-runnable
Everything else162-Assets, the registries, the fingerprint json a stage left behind

Two numbers are worth reading together. The product is 8225 lines of html and the argument for it is 26172 lines of markdown, more than three times as much. That ratio is the pipeline's shape rather than an accident: every value in the system carries the decision that put it there, and a decision with no ground is the thing this repository spends its words refusing.

And the corpus is what git carries, which it was not until the clean clone of step 6. The census had been walking the working directory, so it counted 648 files where a fresh clone receives 568: the 80 it was adding are one local tool's screenshot logs, the course notes and a macOS artefact, every one of them named in .gitignore and none of them ever handed to anybody. A census of a receiver's package that counts files the receiver never gets is wrong in the one direction that flatters it, and only a clone could have found it. The corpus is now git ls-files, with the tree walk kept as the fallback for a copy that is not a checkout.

These figures were each one line per file too high until the critique of this stage. The census counted lines with a split on the newline, which returns an extra element for the empty string after a file's final newline, so every group total was inflated by the number of files in it. The read-only pass found it from the outside, by flagging a line count on this page against the census's own and calling the page wrong; the page was right. A census that is off by one on every file is worse than no census, because the error is invisible at any single row.

The lift test

The design system's central claim is that design/system/ is liftable whole. It had been asserted in three places and measured in none, so on 2026-08-18 it was measured: the folder was copied into an empty directory and one screen the system had never seen was assembled there out of its classes alone - the shell, the app bar, the brand lockup, the big total, the interactive trend chart, the segmented range, a prose card, a divided list, the trust block and the tab bar.

0failed requests
0console errors
0elements left unstyled
0sideways scroll, phone and desktop
2themes, both correct
1dependency it cannot carry

Every url() inside the folder resolves inside it, assets/ included - which is why those four washes are part of the lift rather than decoration. The chart's cursor was built by behaviour.js, the only javascript the system owns, and it works in the empty folder exactly as it works in the product.

And one dependency it cannot provide

base.css asks for Inter by name and nothing in the folder provides it: no @font-face, no file, no import. The lift test passed without noticing, because the machine it ran on has Inter installed, which is exactly the shape of failure a handoff exists to prevent. It is written down now in the two places a person looks - design/system/CLAUDE.md under "What this folder does NOT carry", and at the declaration in base.css, which is the line somebody greps for.

Five weights are actually used: a subset that drops the heaviest loses the wordmark. The fallback chain in base.css is deliberate and safe, so a page with no Inter is readable and not broken - it is simply not Tendd.

The run surface

The census asked what a receiver needs before they can open anything, and in its first run the answer was almost nothing existed: no package.json, one file in the whole repository telling a person how to run something, and every instrument resolving playwright from a global npm root - one of them from an absolute path inside one machine's npx cache, which was the least liftable line here.

python3 -m http.server 8080 # nothing is built; every page is static html npm install # playwright, the only dependency there is npm run check # the rollout ledger and the quality sweep npm run check:route # every link, and the two-click promise of this page npm run census # this page's numbers, recomputed

Every instrument resolves playwright local first and global after, so it runs for a receiver who typed npm install and still runs on a machine that never had one. node_modules/ is ignored; nothing in this repository is built from it and nothing is published.

One instrument still carried the absolute path, and it was found at this step. The census named that defect in one file on 2026-08-18 and the critique found it again in a second file the same day; both were fixed and a third was not, because both sweeps were looking at the files they had been pointed at. The route check reads every instrument rather than the ones a stage remembers. Fixed 2026-08-23.
InstrumentWhat it answersClean?
rollout12.cjsPairing, the ban on a screen carrying a style of its own, links, the registry against the code, and the template a new screen is copied from0 violations
quality13.cjsSix checks over every screen the registry names, by two widths by two themes: overflow, a control's label, a tap target, a flush distance, an empty boxclean
route13.cjsEvery link in every page and every document, a markdown link pasted into html, the click depth from the entry page measured in a browser, and the three doors agreeing across their two placesthe route holds
map13.cjsScreen to zone to component to token, taken off the rendered product, with the reverse list produced by inversionroll-call closed
a11y13.cjsContrast in both themes, a real tab pass, motion under reduce, text at twice its size, and the 129 pages around the product that nothing else readsclean
aria13.cjsSix semantics questions over every page in both corpora: a group with no name, a label ARIA discards, a wait that does not declare itself, a control with no accessible name, a heading level skipped0 of 6
walk13.cjsThe click graph: every link inside the app across the 57 screens. Which screens leave a reviewer no way forward, which states cannot be reached by clicking, and what the chrome declares against what it stands on0 unanswered
width12.cjsThe width sweep: every page across 58 widthson demand
edges13.cjsEvery right edge on every app screen at the widest stop, and the check behind the usage rule that says one screen keeps one measure across its states0 split
pages13.cjsEvery page that is not a product screen, at the three narrowest stops0 scroll sideways
design13.cjsDESIGN.md against tokens.css: every type step the document names, resolved and compared11 of 11 agree
plans13.cjsWhere the product parts company by plan, and the four ways the "one product, not two" decision could stop being true4 checks clean
fp12.cjsThe box fingerprint, before and after any change that should move nothingon demand
inert12.cjs"Checked rather than assumed", re-run on the whole producton demand
handoff13.cjsThis page's censuson demand

The remaining six are the ledgers and pixel proofs of stages 08 to 12 and are kept for the same reason their stage pages are: a claim with no instrument behind it is a claim, and the instrument is how the next person disagrees with it.

Every one of the fifteen is clean, and it was not when this page was first written. Two were not: the ledger reported two screens carrying a class the system does not define, which the README had been calling "the two that must stay clean" while one of them was not; and the accessibility run reported nine screens failing at 200% text. Both were backlog rows for as long as the product was frozen and both closed on 2026-08-23. The ledger also has a check it did not have: number 8 reads the template a new screen is copied from against the product it claims to start, because nothing in this repository had ever read that file and it had rotted in three ways.

What is open, counted rather than remembered

Read off design/kit/docs/backlog.md by a script that classifies each row by what it says about itself, so the number cannot drift from the file.

114rows in the backlog
94say closed
30open by that reading
24say both, and a row with anything unfinished is open
0addressed to this stage
0addressed to a stage that has ended

The one row addressed to this stage is the largest structural debt in the repository: a per-component coloured footprint is written by hand in forty places and nothing recomputes it, which is why twenty-one component pages went stale the day the corpus grew. It has an instrument, counts.cjs, and what it does not have is the discipline of being run - which is the honest state of it and is why it is still open.

The count of the founder's own rows moved from twenty to five between two runs, and the reason is not drift. The founder took twenty-one decisions on 2026-08-20 and the rows that record them still carry the word in their prose. The census now prints two readings rather than one - rows where the founder is the OWNER, and rows that mention the founder anywhere - because a single number would have made a closed decision look like an open one.

Who decides after the handover

Two lists in this package are addressed to a person and not to a builder, and without an addressee the first row of either stops the work. They are not defects. Each one is a place where the code is consistent, the documentation is silent, and somebody has to say which way it goes.

Four things that were so on purpose and written down nowhere, and the day they stopped being

From the onboarding log, where a clean-context reader was sent through the repository twice and every finding was verified line by line against the files before it was believed. All four were put to the founder on 2026-08-23 and all four were answered the same day, which is why this table has a third column that says what happened rather than what is owed.

The questionWhat was actually trueDecided
Which theme is the main one, and is dark a product feature?Light is the main one, dark was complete, and the product had no control, no string and no prefers-color-scheme anywhereA control in Settings. Two lines through Voice, a block in IA node 6.16, a switch in a group of its own, and the structure mirrored into the frozen grey with the decision written into that file. The product still does not follow the machine, deliberately
How many destinations does the tab bar have?Four, since the Save tab was retired. Seventeen surfaces outside the decision log still said five or named SaveDated, not corrected. They keep the corpus they were written on, which is what the live-count rule provides for a claim that is not recounted, and docs/decisions.md is the naming. Two were corrected because this stage owned them
Two screens carry a class the system does not defineThe ledger had reported it since those screens landed, and no backlog row had ever named itNeither option in the row was the answer. The class was already declared, under the name the renaming map gives it in colour: the two newest screens had copied the grey markup and skipped the rename, so that line had been rendering unstyled on both
Is the stand's shell really the file a new screen is copied from?The registry said so, and the file named three destinations against the product's four, put a back control on a destination against U18, and closed a div with a </main>Fixed, and given an instrument. The ledger has a check 8 now that reads the template against the product, because the reason it rotted is that nothing here had ever read it

Seven behaviours no file stated, and the day they were stated

From the behaviour spec, which refuses to write a row it cannot source. All seven were put to the founder on 2026-08-23 and all seven were answered, and the list is kept exactly as it was written rather than deleted: a spec that quietly removes its own open list is one that cannot be trusted with the next.

What was undecidedDecided
What happens to a subscription after the person reports it cancelled, which changes the count on the main screen and is therefore what the product ISIt stays in the list, marked, and leaves the monthly total at once, then disappears on its own once the next charge date passes with no charge, silently. Not gone immediately, because Tendd knows a person SAID so and not that it worked; not still in the total, because the total is what a person is choosing to pay for. State 2.6.7, four lines through Voice, one screen in both editions, and nothing added to the design system
When the alerts dot appears and when it clearsIt appears on an alert that was not there last time and clears when the Alerts screen is opened, read or not. No number, ever. The node had refused the whole family without mentioning the one that shipped, and now says why: a count is a demand and a dot is an offer
What an empty or malformed email does, what is valid in the four Add Subscription fields, and whether asking for another sign-in link has a limitOne minimum contract for all three forms, in the spec and on no screen. The three rules about WHEN matter more than the field list: the primary action is inactive until the form can be submitted, an error appears after a person has tried and never while they are typing, and it is one line under its own field. The resend is answered rather than dropped
Whether the figures document is an approved sourceYes. It is the only file that states triggers, and refusing it would have stripped the source from ten cells to keep a rule about which files count
A third exit the flows do not drawThe drawing was behind the product. "Do this later" into the empty list is in Flow A now, with the reason the screen already carried
Who owns what, so a receiver does not have to guess. A new product STRING is Voice's before it is anybody's, and the founder's after that. A rule that would have to bend is the founder's alone. A defect with an instrument behind it is a builder's and belongs in the backlog. Nothing in either list was decided by the person who found it, and that is the whole reason both lists exist: every one of them went to the founder, and every one came back with an answer that is written down where the next person will meet it rather than here.

What is deliberately not done

So that a decision is not read as a hole. Everything here was available and was refused, and each refusal has a reason that survives being written down.

Not doneWhy
Nothing in the product was fixed WHILE THIS STAGE RANThe product was accepted at stage 12 against a pixel comparison of the whole corpus, so every hole this stage found became a row in the backlog with its measurement rather than an edit. The founder lifted the freeze at the close and eleven of those rows were paid in one round, each verified by the same fingerprint the freeze existed to protect: zero boxes moved at 360, and the only two screens that moved at 1440 are the two whose unstyled line was corrected
No skip linkIt is a new product string, and a string belongs to Voice before it belongs to accessibility. Named at stage 12 as one of three things left, and still true. What it would skip TO now exists: seven stage pages had no main landmark and have one since 2026-08-23
No focus trap in a dialogEvery state in this package is its own file, so a dialog is a page and there is nothing to trap focus inside. A real build owes a trap, a restore on close and an escape key. That is a boundary rather than a gap, and it is counted as one
No screen reader journey testThe semantics instrument proves that names and roles exist; it cannot prove that a journey reads well. That needs a person with a screen reader, and its absence is named rather than assumed away
No deploy in this stageGitHub Pages has served this repository since stage 01. What this stage owes is a version tag, three live addresses, and the proof that a fresh clone works with nothing explained out loud
No new screen, no new component, no new tokenA handoff documents what was built. The single exception the stage allows is an examination screen built by somebody else from the documentation alone, and its fate is decided out loud rather than left in the product

The critique, on two instruments

Run 2026-08-18 the way CLAUDE.md requires it: Codex read-only on the source and Claude in a browser on the render, taken independently and merged afterwards, with a column saying who found what. 22 findings, 22 closed, none dropped at verification - and the two most valuable ones were each produced by checking the other instrument's work rather than by either instrument alone. The closing round of this stage adds its own and lands in this section.

WasBecameFound by
Six places in the navigation node still described a different number of tabs, including a decision line calling the extra one "exactly the density and alarm this segment avoids" and an accessibility contract promising a landmark with a count that no longer matchedAll six agree with the code. The refusal is kept and dated rather than deleted, with the condition that removed it and the measurement that replaced itCodex
DESIGN.md described a tab bar the product no longer had, and its Do-block still forbade petrol inside a screen's content after a decision in the same document had granted it a fourth jobBoth corrected. A normative rule that contradicts its own document is worse than a stale figureCodex
tokens.css justified the plotted line with a count of one element on one screenTwo on two. The sentence was measured correctly in the morning and the founder gave a second screen its own plot in the afternoon: the count moved under a claim that was never re-run. The ARGUMENT survivesCodex
An instrument carried an absolute path into one machine's home, on the same day the census had named exactly that defect in another oneResolved from the file's own location, like every other instrument. A third file with the same defect survived both sweeps and was found at step 5 of this stageCodex
This page said "four markdown files are named by no page" while being an html page that names all fourThe census reads zero, and the paragraph says why out loud: naming them here is what closed itCodex
Two figures in README.md had drifted, and a sentence credited every instrument with serving the repository to a browser when two of them never open oneRecounted, and the two that never touch a browser are namedCodex
The file that reports "zero em dashes found in any file" contained one, inside the sentence naming the character it had searched forNamed by code point and never by drawing it. The result it reports is true againCodex
A component file said petrol has three jobs, written the same day a decision made it fourFour - and the finding was a thread: pulling it found 24 more places saying three, in nine system files, twelve kit pages, DESIGN.md and a concept page. All 25 correctedCodex found one, Claude swept the rest
A stand page asserted "the line is not petrol, and that is the decision worth keeping" two sections above the one explaining that it now isThe refusal is quoted as what it was, with a link to the day it was lifted. A page arguing with itself is the worst kind of staleClaude
Ten of the pages that are not product screens scrolled sideways at the narrowest stop - and nothing had ever swept them. The width sweep covers the product screens and reports zero, truthfully0 of 127. Four causes, each with a solved form already in the product. pages13.cjs is checked in beside the othersClaude
A brand page logged three console errors from an svg attribute that cannot take the value it was givenThe attribute is gone and the aspect ratio says what it was for. The drawing is unchangedClaude
THE ONE THAT CAME FROM DISAGREEING. Codex flagged a line count on this page against the census's own and called the page wrong. The page was rightThe census counted lines with a split on the newline, which returns an extra element after a file's final newline: every line figure it had ever printed was inflated by one per file. Fixed at the function, every figure on this page recounted. A census off by one on every file is worse than no census, because the error is invisible at any single rowCodex found the disagreement, Claude found which side was wrong

Nothing was dropped at verification, which is unusual and is worth one sentence rather than a celebration: the read-only pass was given a scope of files changed on one day, and a narrow scope is what makes a critic precise. The one finding that could have been dropped turned out to be the most valuable in the run, because the instrument was the wrong one.

Round 2, 2026-08-23: the closing pass over this stage's own material

The same two instruments, the scope narrowed to what stage 13 produced. Eleven findings, ten closed, one filed, and four dropped at verification inside a single row - every one re-read in the file it names before it was believed. Seven came from the read-only pass, three from the browser and the design detector, and the split is exactly what the two instruments are for: the sharpest source finding is a rule this package could not obey, and the sharpest render finding is a contrast failure no source read could ever have produced.

WasBecameFound by
THE ONE THAT MATTERS MOST: this package broke its own rule, and the rule was the thing that was wrong. CLAUDE.md said a css fragment or a pixel value anywhere inside handoff/ is a defect. This page carries a style block full of them, because a stage account has a look, exactly as the twelve accounts before it doThe rule now binds handoff/docs/, where it was always aimed, and names the two things that are not copies: a MEASURED figure, and a stage page's own chrome, which reaches no screen. A rule that the file it governs cannot obey is not a strict rule, it is a wrong oneCodex
The prompt for adding a feature said "no pixel number anywhere above" forty lines after printing oneThe number is a measurement and says so: the band of sideways scroll that survived two accepted stages, which is the whole argument for sweeping a width rather than sampling its ends. Caught by the same pass, in the same class it exists to warn aboutCodex
Four instruments took their product corpus from a typed list of what to leave out, and one of them promised in its own header that nothing in it is typed by handA screen is a screen if design/_nav.js names it. All four read the registry now, and the map prints what is on disk and unregistered rather than assuming it. Every figure they produce is byte-identical, which is the proof this corrected the method and not the measurementCodex
Three route figures on this page contradicted the output checked in beside the instrument, because the corpus grew after they were writtenRead off the run. The page had been carrying the shape of the very defect it spends a paragraph describingCodex
This page said the census derives its corpus from the tree, five days after the clean clone changed it to what git carriesNamed correctly, with the tree walk described as what it now is: the fallback for a copy that is not a checkoutCodex
The page said "four documents" twice and the stage has five since the prompt was writtenFive, in both placesCodex
Two headers still described a corpus of 55 screens and 110 pages, inside the two instruments whose whole argument is that a claim counted off a corpus is recounted when the corpus growsNeither carries a number any more: both name the registry they read. A comment that cannot go stale is better than a comment that was corrected twiceCodex
The hamburger on this page said "All screens" and opened the roadmap. The label is generated by the coloured screens' chrome, where it is exactly right, and it reached the two stage accounts that load the same stylesheetCorrected on this page in its own style block, because one line in the shared stylesheet would move the render of all 57 accepted screens. design/rollout.html carries the same wrong word, is frozen html, and is a backlog rowClaude
THE ONE THE SOURCE COULD NOT SEE: the project's own sidebar fails WCAG AA, on every page that shows section links. `.nav-section` in _nav.css is drawn in the muted grey the registry reserves for a stage that has not started, at 13px. Measured against the panel: 2.65:1, where 4.5:1 is the floor. Only the CURRENT link escapes it, because that one is petrolNot fixed, and filed. It is one token and it moves no box, but it repaints the sidebar of twenty pages, and a colour change at the closing ritual belongs to a founder's walk rather than to a builder's last commit. What made it invisible for the whole project is that no instrument has ever swept a stage page: the accessibility run and the semantics run both take the product and its grey twin and stopClaude
This page had a nav, a footer and no main, because the eleven stage accounts before it have none either. A page with landmarks and no main gives a screen reader nothing to skip TOA real <main> here. The other accounts are frozen and carry a rowClaude
Five type and radius values on this page sit off the documented ramp, reported by the design detectorDropped at verification, four of five, with the reason. Every one of them is character-identical to design/rollout.html, which is the account this page answers to: a stage account is chrome, it reads the semantic colour roles and owns its own geometry, and eleven accounts before it do the same. The fifth was mine and real - one invented half-pixel step that existed nowhere else in the house set - and it is gone. The typeface flag is a locked decision from Conceptdetector, Claude verified
The pattern across both rounds is one sentence. Every finding in round 2 is a claim that was true when it was written and stopped being true while nobody was looking: a count, a corpus, a rule, a label. Not one of them is a mistake anybody made; all eight are the cost of writing something down beside something that moves. That is also the argument for every instrument in this repository, and it is why the answer to six of the eight was to delete a number rather than to correct it.

Was unclear, became

This stage produced no screen and no component. What it produced is a package that answers questions it could not answer before, and the honest way to show that is the pair: what a stranger actually asked, and where the answer lives now. Every row on the left was said out loud by a reader with no memory of this project, in one of the three clean-context runs, and none of them was imagined by the person who built it.

Was unclearBecame
"What IS this? Seventeen screens exist as fifty-eight files and nothing says what a real build does with that."The package boundary, on this page: no data layer, no API, no router, no persistence; every state is its own file; and the translation from 57 files back to 17 screens with states is named as the first decision a receiver makes rather than left as a surprise
"Which theme is the main one, and is dark a feature or a capability?"A section of its own, and the answer is measured: light is the main one, every role is paired, the switch is reviewer chrome, the product has no control and no string, and prefers-color-scheme appears zero times anywhere. Recorded in docs/decisions.md as a state with the founder's call still open
"How does the product behave? A screen shows a state and never what led to it."behaviour.md: five flows, five described, steps, states, edge cases and validation, every row naming the file it was read from, and seven rows that had no source moved to a list addressed to a person rather than guessed
"If I change this value, what moves?"map.md, taken off the rendered product: 57 screens to 73 components to 163 tokens, with the reverse list produced by inverting the same data two knees deep rather than by walking the code twice
"Is accessibility done, or is it claimed?"a11y.md: fifteen rows, each with how to check it, and a status of two words where "confirmed" means a run today with a named instrument. Eleven confirmed, two debts written to the backlog, one boundary. Nothing was fixed here
"How do I add something without breaking it?"one-shot.md, a prompt rather than a checklist, tested twice by strangers on two features and rewritten thirteen times by what they got wrong
"Who do I ask? Everything open seems to be addressed to nobody."Two lists with an addressee: four things true on purpose and written down nowhere, and seven behaviours no file decides. Plus the rule for who owns what: a string is Voice's, a bending rule is the founder's, a defect with an instrument behind it is a builder's
"Where does the route start, and does it go anywhere?"Four addresses in two places written in one pass, a folder-by-folder table, and route13.cjs: every link in every page and every document, resolved, plus the click depth from the entry page measured in a real browser
"How much of this do I actually receive?"A census whose corpus is what git carries, because the clean clone found it counting 80 files nobody is ever handed

The contract of this stage, checked

The list a handoff has to satisfy, as a table rather than as a paragraph, so that a disagreement lands on a row. Every "yes" here is a run or a file, and where the answer is partly no it says so.

The contract asksAnswerWhere it is proved
The audit was done by a stranger, not by the builder imagining oneyesThree clean-context runs, each with the forbidden paths listed BY PATH, each returning a reading journal checked line by line against that list. onboarding-gaps.md
Both of the stranger's lists verified row by rowyesFour verdicts per row, and the rows that came back "so intentionally and nowhere written" became entries in docs/decisions.md rather than disappearing quietly
Every behaviour row names its source, and a row with none goes to a list for a personyesFive source kinds declared, seven NOT DECIDED rows, roll-call closed at five flows
The map is read off the code, and its reverse list is produced by inversionyesmap13.cjs, and the idle control closes with numbers: 0 components on no screen, 3 tokens no chain reaches and all three accounted for
Every accessibility row has a way to check it, and no hole was fixed hereyes15 rows, 11 confirmed by a run today, 2 debts and 1 boundary. Two rows went to the backlog with their measurements
The README leads rather than reports, and there is exactly one status tableyesRewritten as an index; the other owner of the same truth is /_nav.js and the census checks the two against each other
Two clicks from the entry page to any artefact of the handoffyesroute13.cjs check 4, walked in a browser because the roadmap is drawn at runtime: 6 of 6 artefacts, none further than two
The clean clone works with nothing explained out loudyesCloned into an empty directory, instruments run from inside it: 0 absolute paths, playwright resolved globally, 239 pages opened, 0 dead links, 0 of 129 pages scrolling sideways
The exam was passed twice, by different readers on different featuresyesTwo builds, both accepted by every instrument, both deleted
The second list is shorter than the firstnoThirteen became twelve, and that is not an improvement. What changed is which readings, and one item repeated a difficulty the first run's own fix had created. Said here rather than smoothed: a prompt this much better that still produces a list this long is working and is not finished
The fate of the exam screens was decided out loudyesBranch B for both, reverted by git, with the reason in docs/decisions.md
The product outside that fork is unchanged while the stage ranyes57 paired with 57, 0 orphans either way, 0 style violations. The founder then lifted the freeze and eleven backlog rows were paid, which is a decision taken out loud at the close and not a stage that edited what it was documenting. The round is in docs/decisions.md and every change carries the fingerprint that proves what moved
The critique ran on two instruments, with a column saying who found whatyesTwo rounds, 22 findings and 8, all closed. The read-only pass owns the source, the browser pass owns the render, and round 2's single browser finding is a generated label that no source read could see
Nothing in the documents copies a value instead of naming ityesChecked mechanically: 0 hex, 0 css fragments, 0 product strings in handoff/docs/. The rule itself was corrected in round 2, because as first written this page could not obey it
Every file of the package has a named reader among the threeyesThe new developer reads the prompt and the boundary; you-in-a-year reads the decisions and the open lists; a model in a new session reads the map and the behaviour spec, which are the two written to be read by something that cannot see the screen