How throughline was used · a worked example · 8 to 11 September 2026

A Parking Register on a Throughline

The technical account

Two graphs, seven borrowed standards and law sources, twenty tagged editions, and one gate that ran on every commit. What each graph held, how they composed, and what the tooling caught.

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.
2graphs in two repositories: a specification and an implementation that pins it
187items in the specification, 185 ratified, 2 retired; 86 in the implementation
7sources composed by tag, 150 citations, every clause read first
20tagged editions of the specification, v0.1.0 to v0.7.1
142tests in the built service, one file per specified test

The tool, and the contract

Throughline is a Git-native requirements tool. Every requirement is one small YAML file with a permanent UID. Every non-root item must ground upward, through a typed link, to a root that justifies it: an intent, a legal constraint, a risk. A validator, tl check, fails the build on an orphan, a dangling link, an illegal status move, or a document that no longer matches the graph. A companion, tl-compose, lets one graph borrow items from others by tag, so a project cites WCAG or the UK GDPR as items rather than restating them.

What made it a foundation for two parties rather than a file format was a small contract, followed without exception across the three days:

  • The AI authors every item with origin: ai. Such an item is born proposed and counts for nothing until a human ratifies it. Ratification writes the human's name and a fingerprint of the content into the file; change the content and the gate reports the signature stale.
  • The human never edits an item by hand. They ratify, reject, or ask for a change. The AI never runs tl ratify.
  • Every commit names the item it serves. Every document that residents or regulators read is generated from the graph and gated: an item changes, the document is stale until regenerated.
  • A claim about the law or a standard is a satisfies link to a clause that was read. The gate proves the link resolves; only reading proves it is true.
proposedborn from the AI, counts for nothing
ratifieda human's name and a fingerprint of the words
stalethe words moved after the signature; re-ratify or revert

Two graphs, and what they compose

the parking register's two graphs, and what they compose implements ×99, relates ×148 satisfies ×13 satisfies ×21 satisfies ×57 satisfies ×10 satisfies ×13 satisfies ×6 satisfies ×32 derives_from ×6, relates ×3 composed, uncited specification · 188 constraints, risks, journeys, implementation · 86 requirements, decisions, tests WCAG 2.2 public · v2.2.3 OWASP ASVS public · v5.0.0 SOLID public · v2026-09 plain language public · v2026-07 audience: general public · v2026-07 practical GDPR public · v2026-09 UK GDPR public · v26.06.19.0 EU GDPR public · v25.09.04.0 local CI/CD public · v0.1.0 the parking register's two graphs, and what they compose
The two graphs and what they compose: the specification borrows seven public standards and law sources by tag; the implementation pins the specification at an edition and borrows two more. Arrows carry the links that cross between graphs, with counts. Generated from the graphs' own configuration and dumps.

The work ran on two graphs in two repositories. The specification holds the intents, constraints, risks, assumptions, non-goals, requirements, journeys, pages and tests, and composes seven public sources by tag. The implementation, in the service's own repository, pins the specification at a tagged edition and borrows two more sources of its own. Every link that crosses from one graph into another is stamped with a fingerprint of its target, so an amended clause or a moved source marks its dependants suspect in the graph that cites it.

The specification graph

specification: which registers link to which ASM · 5 assumption CON · 19 constraint INT · 2 intent JNY · 12 journey NFR · 5 nfr NG · 7 non goal PAGE · 20 page RISK · 8 risk SR · 44 system requirement UR · 13 user requirement asvs (source) 60 links in audience (source) 6 links in plain (source) 13 links in practical (source) 32 links in solid (source) 10 links in ukgdpr (source) 9 links in wcag (source) 21 links in ASM · 5 assumption relates ×1 relates ×1 CON · 19 constraint relates ×1 relates ×1 relates ×1 relates ×1 relates ×1 relates ×3 relates ×2 JNY · 12 journey relates ×3 derives_from ×17 NFR · 5 nfr derives_from ×1 derives_from ×4 relates ×1 relates ×11 relates ×2 satisfies ×6 satisfies ×13 satisfies ×10 satisfies ×10 NG · 7 non goal relates ×2 PAGE · 20 page derives_from ×34 relates ×78 SR · 44 system requirement assumes ×5 derives_from ×59 derives_from ×16 relates ×2 relates ×2 relates ×2 relates ×1 mitigates ×26 refines ×1 relates ×52 implements ×56 relates ×1 relates ×1 satisfies ×57 satisfies ×32 derives_from ×5 relates ×1 satisfies ×11 TEST · 53 test relates ×12 verifies ×6 relates ×1 relates ×6 verifies ×46 verifies ×13 UR · 13 user requirement derives_from ×13 derives_from ×9 derives_from ×1 specification: which registers link to which · rows link to columns
Scrolls sideways.The specification's eleven registers and seven sources as a matrix: rows link to columns, cells carry the link types and counts. 45 kinds of link do not fit as arrows, so this is drawn as a matrix rather than a diagram.
INT · intent2 intents, the roots the whole graph serves
CON · constraint21 legal constraints, 19 live; each carries its citation and the date it was read
RISK · risk8 risks, each mitigated by at least one requirement
ASM · assumption5 assumptions, one rewritten when the hosting disproved it
NG · non_goal7 non-goals: what the register deliberately does not do
UR · requirement13 user requirements, one set per journey
SR · requirement43 system requirements, each verified by a test
JNY · journey12 journeys, each an ordered list of steps over pages
PAGE · page19 pages, each with a route and an access level
NFR · nfr5 non-functional requirements: reading age, encryption, availability
TEST · test52 acceptance tests, one file each in the built service

Seven sources were composed over the three days, each pinned by tag: WCAG 2.2 cited at A and AA only; OWASP ASVS 5.0.0 at Levels 1 and 2 only; the SOLID principles as the bar for how the code is written; plain-language and general-reader writing rules for everything a resident reads; the UK GDPR as a law source; and a pattern source for erasure and anonymisation written that week. Every one of the 150 satisfies links names a clause whose text was read first.

Two things happened that only a composed graph allows. When the law source gained the erasure and time-limit articles, two local constraints that restated them were retired and the items beneath them re-grounded on the source's own obligations. And when the pattern source's vault rule turned out not to fit a resident with several notices, the fix went into the source first, the source was re-tagged, and the register re-pinned and cited the new rule. The AI also learned it had been wrong about a tool limit: transitive sources are handled by re-export from the source that composes them, and the correction went into its memory the same hour.

tl-compose check · 8 source(s) composed: asvs@v5.0.0, audience@v2026-07, eugdpr (re-exported from 'practical'), plain@v2026-07, practical@v2026-09, solid@v2026-09, ukgdpr@v26.06.19.0, wcag@v2.2.3 Local 185 of 860 local · 675 borrowed 0 error(s), 0 warning(s) — composed graph is sound (strict)

Registers rebuilt: journeys and pages as items

The graph began with one requirements register. When the human asked for user and system layers, journeys and a site map, nothing was yet ratified, so the registers were rebuilt rather than tombstoned: ten user requirements grouped by journey, the system requirements keeping their numbers, then a JNY register whose items carry an ordered list of steps, and a PAGE register whose items carry a route and an access level. A journey refines the user requirement it serves and each step names the page it lands on.

The journeys immediately found two holes the system requirements alone had passed every check with. A journey promised a fresh link that no requirement offered. A journey and a page walled the controller area behind sign-in that no requirement required. Both became system requirements with tests. A graph of behaviours is checked by walking it; a graph of requirements is only checked by reading it.

The constraint register shows the other kind of rebuild. Twenty-one constraints were authored across the three days; two were retired, not deleted, when the composed law source absorbed what they restated. Their UIDs are never reused, and the items that had derived from them were re-grounded on the source's own clauses in the same commit.

The implementation graph

implementation: which registers link to which DEC · 16 decision INT · 1 intent REQ · 34 requirement TEST · 34 test cicd (source) 14 links in spec (source) 247 links in DEC · 16 decision derives_from ×3 relates ×9 derives_from ×16 satisfies ×13 implements ×30 relates ×37 INT · 1 intent relates ×12 REQ · 34 requirement derives_from ×71 relates ×3 relates ×1 relates ×1 implements ×69 relates ×57 TEST · 34 test verifies ×34 relates ×1 relates ×42 implementation: which registers link to which · rows link to columns
The implementation graph: requirements implement the specification's clauses, decisions derive from them, and every requirement is verified by a test.

The implementation lives with the service's code and is the smaller graph: requirements that implements a clause of the pinned specification, architecture decisions that derives_from those requirements, and a test for every requirement, one test file per specified test, 142 of them at v0.7.1. Its throughline.toml pins the specification by tag and borrows two sources of its own for how the code is written.

Its gate is stricter than the specification's in one respect: it stamps every link into the specification and refuses to build when the controller's sign-off dates on the generated documents fall behind the pinned commit. A specification that moves without the implementation cannot be built against, by construction.

One instruction from the human, all data encrypted at rest and in transit, produced three implementation requirements with tests and decided the database question: a dedicated instance on an encrypted volume rather than the shared one whose administrative credential sat in fifty namespaces.

Documents generated from the graph

The legitimate interests assessment, the record of processing, the privacy notice, five escalation covering letters and six resident letter templates were all generated with tl docs: hand-written prose around regions injected from the items, so a change to a retention clock stales the record of processing until it is regenerated, and tl docs --check fails the build until it is. The privacy notice was measured against the reading-age target the graph's non-functional requirement set: the first draft scored about fourteen, the rewrite about nine. The check that measured it became a script in the repository, then a test in the built service.

The human's three wording faults, "address" read as postal, a notice that overstated what the register does, a sentence pointing residents at a repository, became four naming rules in the writing requirement, then a checkable function in the test suite, which on its first run found two more faults nobody had noticed.

The challenge pass

A sibling tool, throughline-challenger, exists to find what a structural gate cannot: a child stronger than its parent, a UID mentioned in prose with no edge, two names for one thing, a universal claim with one narrow child, a rationale that would break a sibling, a requirement with no failure clause. Its nine checks were run by hand five times, each after a change large enough to hide something.

The first pass found eighteen items citing others in prose with no link, five priorities that understated a statutory duty, and a pack that would have sent per-case detail to the press. The fifth found one contradiction between the vault rule and the records list, by holding two ratified items side by side. Each pass was cheaper than the last.

Editions, ratification, and the gate

EditionWhenWhat it held
v0.1.09 Sep164 items, all ratified in one sitting
v0.2.0 – v0.6.69–10 Septhe human's iterations, each amendment re-ratified before the next tag
v0.7.011 Sepnaming rules; residents send the pack
v0.7.111 Septhe notice's maintainers' note moved

Ratification is tl ratify <UID>: it writes the ratifier's name and a fingerprint of the item's normative content into the file. Amend the item and the gate reports the fingerprint stale until the human ratifies again; that is what made twenty editions safe to iterate on. Within one day of the first commit the human had ratified every item, twelve constraints while the AI was still mid-turn, which the AI's git add -A swept into its own commit. The fix was a habit, stage explicit paths, and a memory note.

The gate ran on every commit in both repositories:

tl-compose -C idd check --strict tl-compose -C idd docs --check

--strict turns a proposed item or a stale ratification into an error rather than a warning, so a specification with anything unaccepted in it cannot be tagged.

What the tooling taught

Author the roots before the requirements. Fourteen constraints written before any requirement meant every later argument was settled by pointing at a citation rather than by opinion.
Proposed is a real status. Every AI-origin item was visibly unaccepted until a person signed it, and --strict said so on every run.
Model journeys as items. The two gaps that survived every requirement-level check were found within an hour of journeys and pages becoming registers.
Borrow clauses; never cite one unread. A satisfies link is a claim. Reading the clause first turned two standards into design changes nobody had planned: deletion confirmation from WCAG and per-record keys from the pattern source.
Generate the paperwork from the graph. The privacy notice, the assessment and the record cannot disagree with the requirements because they are the requirements, rendered, and docs --check holds them there.
Reverse decisions in the open. The link-to-codes reversal cost an afternoon because the links found every dependent item and the old rationale stayed as the record of why.
Turn a human's catch into a test. "Address is ambiguous" became a function in the suite, and found two faults on its first run.
Fix the source, re-tag, re-pin. A pattern that fails one consumer will fail the next; the correction belongs upstream.
Stage explicit paths. A human ratifying while the AI works is the normal case, and git add -A will claim their signatures.