A worked example · how throughline was used · 17 to 20 September 2026

A Library on a Throughline

How throughline was used

Two graphs, nine registers chosen rather than inherited, 664 items all written by the AI and all signed by the person, 1,554 links between them, two standards borrowed by reference, and a gate that was green throughout while seven things were wrong.

Two pagesThis page is for a reader who wants the machinery. The account for a general reader tells what happened, who did what and what it cost.

The subject, and the contract

The work described here is a full statement of what an already-deployed browser editor does, written so that its engine could be taken out into a library. The editor is public at editor.iddn.uk; the repositories are not, so the figures on both pages come from the graphs, the git history and the session record, and a reader cannot check them. That is said plainly rather than glossed.

The contract was the estate's usual one. Every item is AI-origin and enters proposed. Nothing counts until a named human signs it. A signature covers an item's words, so amending the words after a signature raises the item as stale and it must be signed again. Links carry a stamp of the target's content, so rewording a target flags every link into it for re-reading.

Two graphs, and what they compose

The work is two graphs, not one. idd/shape/ records why the second graph is organised as it is; idd/library/ is the description itself. They are separate because they are ratified separately: a decision about which registers should exist is a different kind of statement from a guarantee the code must keep, and a person signing one should not be signing the other by accident.

the shape graph: registers and the links between them relates ×1 derives_from ×5 derives_from ×1 derives_from ×23 derives_from ×7 derives_from ×14 relates ×2 derives_from ×8 derives_from ×29 CON · 2 constraint DEC · 11 decision DOM · 1 domain NEED · 14 need NG · 19 non goal SRC · 8 source the shape graph: registers and the links between them
The shape graph: why the description is organised as it is. Eight sources read before anything was written, fourteen needs, eleven decisions each recording what was rejected, two inherited constraints and nineteen registers considered and not created. It borrows the library graph by path, so a decision here can point at the items it produced.

Almost everything in it is a root type, because a shape graph exists to be justified by nothing but itself. Three of its items are now rejected, and those three are the interesting ones: two of them are the non-goals in the seventh finding below, and the third is a pinned package that stopped existing.

the library graph: which registers link to which CAP · 178 capability CTL · 58 control HAZ · 24 hazard INT · 6 intent NG · 20 non goal PIN · 5 pin RSN · 118 reason SET · 15 setting TEST · 240 test CAP · 178 capability relates ×2 uses ×249 relates ×4 satisfies ×199 mitigates ×58 derives_from ×206 relates ×9 assumes ×43 raises ×272 reads ×62 CTL · 58 control relates ×1 mitigates ×9 derives_from ×64 reads ×15 relates ×2 RSN · 118 reason relates ×4 mitigates ×11 relates ×1 SET · 15 setting mitigates ×4 derives_from ×15 TEST · 240 test verifies ×202 verifies ×64 assumes ×37 verifies ×15 relates ×6 the library graph: which registers link to which · rows link to columns
scrolls →The library graph at the commit where the description was complete: nine registers, 664 local items and 1,554 links between them, with 363 ASVS clauses and 114 STRIDE items borrowed by reference. Rows are the linking register, columns the register or source linked to; a cell gives the link types and how many of each. Drawn as a matrix because 23 register pairs link, and arrows stop being legible well before that.

What the matrix shows at a glance is where the weight sits. 240 tests carry 281 verifies links, into 202 capabilities, 64 controls and all 15 settings. Every one of the 272 raises links comes from a capability, because a reason code is something a called operation returns and nothing else produces one. And the two borrowed columns are empty: not one local item cites a clause of either standard, which is the sixth finding below, drawn.

Nine registers, chosen rather than inherited

tl init lays down a default set — vision, requirements, nonfunctional, tests, risks, non-goals. That set was not used. The shape pass asked what this particular subject needs, and the answer was nine registers with names taken from the domain:

CAP · capability178 items. One behaviour the library offers, in the words of the interface a consumer calls.
CTL · control58 items. A guarantee the implementation must keep, with where it is enforced: at build, in the browser, or at the relay.
RSN · reason118 items. One named error the library can raise. A reason is a first-class item because a consumer's code branches on the name.
TEST · test240 items. How a capability or control is checked, with the harness that runs it.
HAZ · hazard24 items. A field condition that was actually observed — a network that filters archives, a host that rewrites an error.
SET · setting15 items. A value a consumer supplies at build time, and what reads it.
PIN · pin5 items. A pinned third-party version with its checksum.
INT · intent6 items. The roots everything else derives from.
NG · non_goal20 items. Scope deliberately excluded, so that a later proposal can be refused by pointing at one.

The distinction that did the most work is capability against control. A capability says what a consumer can do; a control says what must hold however it is done. Splitting them is why a security clause has somewhere to attach that is not a feature, and why the seven findings below have addresses.

The distinction that cost the most is hazard. A hazard is a measured field condition, not a hypothesis. That rule is sound and it is exactly why two of the seven findings had nowhere to live: a threat nobody has observed is not a hazard, and until a threat register was borrowed there was no register for it.

Two standards, borrowed by reference

[[sources]] namespace = "asvs" ref = "v5.0.0" [[sources]] namespace = "stride" ref = "v2026-09"

363 ASVS clauses and 114 STRIDE items are composed into the graph, so a local item can cite a clause by its UID and the gate resolves it against a pinned edition rather than against whatever the standard says today. The URLs are left off above because one of the two graphs is public and the other is not; the ASVS one is in the catalogue.

Neither source was declared when the description was first written. Both arrived after the findings below forced the question, and one of them — the threat-modelling source — arrived only because a non-goal saying it was unnecessary turned out to be wrong.

Item before code, and a challenge pass before a signature

The order was: shape the graph, declare its schema, write the items, challenge them, then hand them to a human. No code was written until the last of those had finished.

Declaring the schema before the first item matters more than it looks. A fingerprint covers an item's normative attributes, so adding a normative attribute later would change every fingerprint and invalidate every signature. Declaring attributes, link types, endpoint rules and coverage rules first means a signature given on day one still stands on day three.

The challenge pass was ten challengers against every family of items, each answered by a sceptic whose job was to refute the challenge rather than agree with it. 118 findings survived and were applied. 29 questions could not be settled by argument and were written down for the ratifier, each naming the items it touches and which option the graph currently holds.

The seven findings, with their addresses

Each of these is a defect in something that already existed. None was found by running anything.

ItemWhat was wrongWhat found itNow
CTL-0006Policy says base-uri 'self'; ASVS V3.4.3 at level 2 requires object-src 'none' and base-uri 'none'. The library emits no <base> element, so 'none' costs nothing.Reading asvs:SR-0055 beside the controlopen — flagged ambiguous inside the signed item
CTL-0057The relay called GitHub with default redirect handling, so a 307 or 308 would forward the client secret to whatever set Location. The browser is bounded by connect-src; a Worker has no policy.Reading the relay's capabilities against the trust boundary. "Redirect" appeared in 1 of 658 items, in another sense.closed, ratified
CTL-0058CTL-0043's own rationale concedes the Origin header is the only control. Anyone could exhaust the app's device-flow allowance; CTL-0044 keeps nothing that would show it.Reading CTL-0043's rationaleclosed, ratified, then amended again on 20 September because the first version could refuse an honest sign-in
CON-0002Titled "one requirement per behaviour and one test per requirement" while 26 items were verified by two tests and 2 by three.Reading the constraint's wording; the count came afterwardsclosed — the rule was wrong and was rewritten as a floor
CAP-0062Nine items said the worker installs four pinned wheels. From throughline 3.11.0 composition lives in the Tool, so the fourth had ceased to exist.Rejecting PIN-0003, then two sweepsclosed — the count is removed, not corrected
—The editor's graph carries 63 satisfies claims into ASVS across 21 requirements. 20 of the 21 were carried over, but the new graph declared no sources, so 61 of 63 claims became unstated. Nothing recorded it.Counting the claims in both graphsopen — the source is composed again; 0 clause citations are reattached
NG-0008Rationale "the operator chose controls only for ASVS" is the reason for NG-0009, not for this. Its text, "a threat model can only be closed against a deployment", was disproved by CTL-0057 and CTL-0058 hours earlier.The ratifier, immediately after signing itclosed — signature withdrawn, both non-goals rejected, a threat source composed

The sixth has no single UID, which is itself the point: a claim that stops being made leaves no item behind to carry the loss. The three editor requirements whose clauses vanished — SR-0116, SR-0117, SR-0129 — had themselves been written on 16 August 2026 as the output of an audit into false citations. SR-0116 exists only because asvs:SR-0255 (V12.2.1) had been cited by six requirements and delivered by none. They survived the extraction as CTL-0015, CTL-0028 and CTL-0014. The clauses that forced them did not.

The gate, and what it does not prove

tl check · library Items 1144 live system_requirement 345 · test 241 · capability 179 · reason 119 · control 59 · criterion 50 · non_goal 26 · hazard 24 · mitigation 19 · user_requirement 17 · threat 17 · setting 15 · intent 9 · step 8 · category 6 · pin 5 · element 5 Status ratified 664 · approved 363 · proposed 113 · rejected 4 Links 2051 implements 412 · derives_from 323 · verifies 283 · raises 273 · uses 249 · satisfies 199 · mitigates 102 · assumes 80 · reads 78 · relates 35 · targets 17 Grounding 494/494 local non-root items trace to a root · 30/30 local delivery roots served Local 668 of 1144 local · 476 borrowed 0 error(s), 108 warning(s) — composed graph is sound

This gate ran on every commit and was green on every one of them. It was green while a constraint was broken twenty-eight times, while nine items disagreed with each other about a count, and while 61 conformance claims evaporated.

That is not a defect in the gate. It proves what it says it proves: that every non-root item reaches a root, that every delivery root is served, that a cited clause resolves in the composed union, that a status move is legal, and that a signature still covers the words it was given for. It does not ask whether an item is true, whether two items agree, or whether a requirement actually delivers the clause it cites.

The 108 warnings are the interesting number. They are suspect links: relationships whose target was reworded after the link was last confirmed, waiting to be read again. Under --strict they are errors, which is why this work does not currently meet the assessment gate and why no assessment record is filed for it.

Signatures, stamps and the cost of changing a word

A fingerprint covers an item's type, text, normative flag and normative attributes. It deliberately excludes title, status, links and timestamps, so that reordering or a workflow change never raises a false suspect. Two consequences ran through this work.

The first is the one the tool is built for: rewording a signed item's text invalidates the signature, and it says so at the moment of the amend. That is how five of the ratifier's signatures were caught going stale when the AI amended six items at 13:17, in the middle of his signing run. Nothing else would have noticed.

The second is the gap. Because links are outside the fingerprint, a satisfies claim can be added to or removed from a ratified item without disturbing the signature. That is what made the sixth finding silent, and it is the same gap an audit on this estate had already measured: 175 borrowed-clause citations across 38 requirements were read pairwise against their clause text, and 44 were false. A second pass the same day read the 26 ASVS citations the first had left out and found 15 false. Every one of them resolved; the gate had never had anything to say.

MeasureValueTaken from
Items, local664graph, at the commit the description was complete
Items in the record it replaced198the editor's graph — so 466 statements are newly written, describing behaviour that already ran
Live · rejected660 · 4graph
Accepted by a human660 (ratio 1.00)graph
AI-origin664 (all of them)graph
Links, local1,554graph
Clauses composed363 ASVS · 114 STRIDEgraph
Clause citations made0graph — the sixth finding, still open
Commits · span56 · 2.39 daysgit
Commits citing an item17 (30%)git
Items amended after ratification81, in 176 amending commitsgit
AI working time · wall clock7.7 h · 57.6 hsession record, 10-minute idle gap
Messages from the person101session record

What the tooling taught

Declare the schema before the first item. A normative attribute added later changes every fingerprint. Declaring attributes, link rules and coverage rules first is what let a day-one signature survive to day three.
A coverage rule that asks "is there a test?" cannot see "how many?". Twenty-eight items broke a stated rule under a green gate because the rule was expressed as the existence of an incoming link.
Reject rather than reword, when a decision changes. A fingerprint covers content and not status, so rejecting an item leaves every stamp intact and makes nothing suspect. Rewording it would have cascaded.
Drop a count from a title; do not correct it. A number in normative text buys a fresh signature every time the number moves. Naming the set instead costs nothing when the set changes.
A register with a strict definition will refuse a real finding. Hazards are measured field conditions by rule, so two genuine threats had nowhere to go until a threat register was borrowed. The rule was right and the gap was real at the same time.
Links are outside the signature, so a conformance claim is free to be wrong. Measured twice on this estate now: 44 false of 175, then 15 false of 26. A citation is an assertion until a person has read the clause beside the requirement.

The account. What happened, who did what, what it cost and what went wrong is on the account for a general reader.