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.
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.
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:
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
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.
| Item | What was wrong | What found it | Now |
|---|---|---|---|
| CTL-0006 | Policy 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 control | open — flagged ambiguous inside the signed item |
| CTL-0057 | The 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-0058 | CTL-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 rationale | closed, ratified, then amended again on 20 September because the first version could refuse an honest sign-in |
| CON-0002 | Titled "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 afterwards | closed — the rule was wrong and was rewritten as a floor |
| CAP-0062 | Nine 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 sweeps | closed — 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 graphs | open — the source is composed again; 0 clause citations are reattached |
| NG-0008 | Rationale "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 it | closed — 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
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.
| Measure | Value | Taken from |
|---|---|---|
| Items, local | 664 | graph, at the commit the description was complete |
| Items in the record it replaced | 198 | the editor's graph — so 466 statements are newly written, describing behaviour that already ran |
| Live · rejected | 660 · 4 | graph |
| Accepted by a human | 660 (ratio 1.00) | graph |
| AI-origin | 664 (all of them) | graph |
| Links, local | 1,554 | graph |
| Clauses composed | 363 ASVS · 114 STRIDE | graph |
| Clause citations made | 0 | graph — the sixth finding, still open |
| Commits · span | 56 · 2.39 days | git |
| Commits citing an item | 17 (30%) | git |
| Items amended after ratification | 81, in 176 amending commits | git |
| AI working time · wall clock | 7.7 h · 57.6 h | session record, 10-minute idle gap |
| Messages from the person | 101 | session record |
What the tooling taught
The account. What happened, who did what, what it cost and what went wrong is on the account for a general reader.