Skip to content

Latest commit

 

History

History
409 lines (383 loc) · 43.9 KB

README.md

File metadata and controls

409 lines (383 loc) · 43.9 KB

Architecture Decision Records

ADRs record durable decisions and their tradeoffs. They are immutable after acceptance except for status and links to superseding records.

Adding an ADR

  1. Copy template.md and assign the next unused number — currently 0163.
  2. Fill in Status, Date, Owners, and Related as bullets at the top, before ## Context.
  3. Link the ADR from the decision ledger in the same pull request.

Numbers are never reused, including for an ADR that is withdrawn before merge. Allocate the number in the pull request that lands the ADR, not before, so two concurrent branches cannot claim the same one. See the ledger ID convention for the same rule applied to ledger entries.

scripts/check_adr_numbers.py enforces this mechanically, in make lint and in CI. It fails the build when two files claim one number, when an ADR's own # ADR-NNNN: heading disagrees with its filename, when an ADR is missing from the index below or indexed twice, when an index row points at a file that does not exist, and when the advertised next unused number above is not the highest allocated plus one. Gaps are reported as information and never fail. Keeping the next number on one line is deliberate: two branches that both allocate it now conflict textually, so Git refuses the merge instead of accepting it.

Known gaps: there is no ADR-0027, ADR-0082, ADR-0083, ADR-0085, or ADR-0086. Every one of them is spent, not free. Recycling a number would silently repoint every external citation of the draft that reserved it at an unrelated decision, which is the exact failure the never-reuse rule exists to prevent.

  • 0027 was allocated on a branch whose ADR never landed.

  • 0082 was allocated by the branch that first wrote the net-tie copper decision. That branch sat ungated while ADR-0084 and later ADR-0087 each took a number above it, and when the work finally landed it landed as ADR-0092 — a new number, not the one it had reserved. 0083 was claimed by a branch that has not landed, and is spent on the same terms whether or not it ever does.

  • 0085 and 0086 were claimed by two branches still open when ADR-0088 landed, so ADR-0088 took a number above both rather than the next one.

  • 0105 was a live claim held by the branch for issue #172 while the records for issue #164 (0106) and issue #110 (0107) stepped over it. It has since landed with its own record, so it is not a gap — the third consecutive round in which stepping over a live claim cost nothing when the claim landed.

No other number is a gap. This section has repeatedly carried notes recording a number as a "live claim" held by an open branch — 0081, 0089, 0090, 0092, 0094, 0097, and 0099 through 0103 all appeared here that way, several of them contested by two branches at once. Every one has since landed from its own branch, and this sentence is the correction those notes anticipated. The convention that produced them is rule 1 of the ledger ID convention: a branch that cannot see another branch's pre-assigned number steps over it rather than racing it, and records that it did. Keeping the next-unused number on one line above is the textual safety net — two branches that both allocate it conflict in Git rather than merging cleanly into a duplicate.

That safety net is not hypothetical, and this file is the evidence. Between 2026-08-08 and 2026-08-13 the notes above accumulated as five overlapping rewrites of one paragraph, each landing without a textual conflict because they did not overlap line-for-line: one truncated mid-sentence, one contradicted the next line about ADR-0081, one named ADR-0089 as a gap while the record existed and was indexed two lines below, and three separate summary sentences stood under the index at once, disagreeing about the record count. The text above is the union of their facts with every contradiction resolved against the files actually present in this directory. scripts/check_adr_numbers.py is the count to trust, not a sentence here.

How 0066 through 0068 came to be three records: three concurrent branches each created an ADR-0066 — the atomic route bundle preview, ordered-layer routing, and route-aware placement ranking. Their filenames differed, so Git merged all three without a conflict and nothing detected the collision. Two were renumbered by hand after the fact, to 0067 and 0068. This is exactly what scripts/check_adr_numbers.py now refuses.

Status vocabulary

  • Accepted — the decision is in force. This is the status of every ADR below.
  • Superseded — replaced by a later ADR, which is named in this index and in the ADR itself. No ADR currently carries this status; where a later ADR widens an earlier one's scope, the earlier ADR's conditions are recorded as satisfied rather than the ADR being retired.
  • Proposed — under discussion, not yet in force.

An ADR whose status carries a qualifier is noted in the table. A qualifier narrows the decision; it never silently widens it.

Index

ADR Title Status
0001 Candidate-first mutation model Accepted
0002 MCP is an external adapter Accepted
0003 Python reference core with Rust-ready contracts Accepted
0004 Authoritative KiCad CLI DRC gate Accepted
0005 Canonical integer Board IR v0.1 Accepted
0006 Bounded deterministic A* as the first routing reference Accepted
0007 Disposable KiCad snapshots for route-candidate validation Accepted
0008 Candidate-bound authoritative KiCad DRC evidence Accepted
0009 Bounded non-mutating route preview as the first public routing surface Accepted
0010 Read-only Board IR inspection and a shared request boundary Accepted
0011 Model existing copper as exact rectangular obstacles Accepted
0012 Through vias as selected-layer obstacles Accepted
0013 Conservative polygon zone-boundary obstacles Accepted
0014 Canonical circuit intent and deterministic schematic rendering Accepted
0015 Bounded Circuit Intent schematic delivery Accepted
0016 Attach to existing same-net copper and complete partial routes Accepted
0017 Conservative integer envelopes for diagonal foreign copper Accepted
0018 Chained integer squares as the core of diagonal attachment copper Accepted
0019 Route multi-pin nets by deterministic component merging Accepted
0020 Treat same-net through vias as connectivity joints Accepted
0021 Trust poured copper only against a fresh KiCad refill Accepted
0022 Observe a board as a semantic scene, with its text held at arm's length Accepted
0023 Render a board deterministically, and only as an advisory aid Accepted
0024 Typed placement intent, validated by a deterministic legalizer Accepted
0025 Apply a route candidate by splicing bytes, not by rewriting a board Accepted (mutating path added 2026-08-04)
0026 Make footprints revision-bound Board IR objects before moving them Accepted
— 0027 is deliberately unused; see Known gaps above. —
0028 Make Circuit Scene net references directly actionable for routing Accepted
0029 Add a redacted, read-only KiCad IPC observer Accepted
0030 Bind a bounded KiCad IPC snapshot to Circuit Scene Accepted
0031 Keep live KiCad route proposals read-only and revision-bound Accepted
0032 Keep live placement proposals read-only and revision-bound Accepted
0033 Keep live editor context read-only and revision-bound Accepted
0034 Keep placement candidate rendering source-preserving and subset-bound Accepted
0035 Keep the layered A* search seam internal until board fidelity exists Accepted
0036 Bind the layered search oracle to a narrow Board IR proposal contract Accepted
0037 Render layered route proposals into disposable KiCad bytes Accepted
0038 Candidate-bound KiCad DRC for layered proposals Accepted
0039 Freshness-bound fill islands as routing obstacles Accepted
0040 Advertise freshness-bound fill provenance on routed previews Accepted
0041 Close layered and freshness-bound routing safety gaps Accepted
0042 Public, candidate-only layered route preview Accepted
0043 Durable routing-job ledger before protocol tasks Accepted for the internal lifecycle subset
0044 Live layered route proposals are session- and snapshot-bound Accepted, with a 2026-08-05 amendment superseding only the HMAC derivation
0045 Verify layered candidate topology before serialization Accepted
0046 Execute durable routing jobs with bounded leases; defer MCP Tasks Accepted
0047 Persist redacted candidate manifests, not route geometry Accepted
0048 Durable layered routing request, result, and export boundary Accepted
0049 Add a bounded one-Steiner topology ordering policy Accepted
0050 Expose opt-in DRC evidence for file-backed layered proposals Accepted
0051 Narrow exact obstacle queries with a conservative spatial index Accepted
0052 Emit candidate DRC as a redacted in-toto Statement payload Accepted
0053 Bind the supported placement subset to private KiCad DRC Accepted
0054 Close review-bot boundary gaps in routing and live observation Accepted
0055 Add a bounded negotiated-congestion coordinator Accepted (request-shape clause superseded in part by 0126)
0056 Verify bounded Circuit Intent schematic parity Accepted
0057 Observe bounded front/back footprint poses Accepted
0058 Check same-side rectangular courtyard legality Accepted
0059 Separately authorize bounded placement application Accepted
0060 Expose opt-in placement DRC evidence through the file-backed preview Accepted
0061 Observe post-placement state through one captured scene and DRC context Accepted
0062 Keep padless courtyards in placement collision evidence Accepted
0063 Carry live IPC deadlines into bounded board parsing Accepted
0064 Bind a closed routing-policy decision to the initial negotiated order Accepted
0065 Observe and legalize bounded orthogonal courtyard chains Accepted
0066 Publish a composed route bundle only as one all-or-nothing read-only plan Accepted
0067 Keep route-aware placement ranking private, bounded, and opt-in Accepted
0068 Keep ordered-layer routing bounded and non-serializing Accepted
0069 Gate live KiCad IPC on an operator opt-in and establish document type at the observer Accepted
0070 Shrink a layered zone envelope only against proved fill Accepted
0071 Authoritative KiCad schematic ERC and generated-schematic round trip Accepted
0072 Model foreign arc tracks as conservative integer envelopes rather than refusing the board Accepted
0073 Declare negotiation strategy as three separately digest-bound policy slots Accepted
0074 Gate live editor mutation on its own consent, and ship its preconditions before it Accepted
0075 Model KiCad's courtyard cache, and make courtyard legality three-valued Accepted
0076 Assemble the board outline from Edge.Cuts segments, and never repair it Accepted (identity clause superseded by 0087)
0077 Roundrect corner radii round up, by geometry role Accepted
0078 Net-0 copper is an obstacle with no connectivity contribution Accepted
0079 Make the structural parse budgets operator-settable, and name the one that refused Accepted
0080 Bracket chamfered and circular courtyards instead of widening them Accepted
0081 Reconstruct the congestion ledger incrementally and bound rip-up by a spatial window Accepted
— 0082 and 0083 are deliberately unused; see Known gaps above. —
0084 Authoritative source-to-board parity via a board-eligible intent projection Accepted
— 0085 and 0086 are deliberately unused; see Known gaps above. —
0087 Name an assembled Edge.Cuts outline by the sorted set of its members' own uuids Accepted
0088 A truncated scene withholds whole kinds instead of emptying them Accepted
0089 Scope the obstacle model to a routing region, and split the budget that was counting three things Accepted
0090 A root board group is organisation, accepted and counted rather than modelled Accepted
0091 Accept the pad zone-connection overrides that attach, refuse the one that detaches Accepted
0092 Net-tie copper is a netless obstacle, and the tie is never a connectivity claim Accepted
0093 An off-grid refusal carries the pad, the pitch and the exact miss Accepted
0094 A root board property is a text variable, accepted and counted rather than modelled Accepted
0095 Copper text has no envelope derivable from the board, and refuses under its own name Accepted
0096 An edge-connector pad converts as an SMD pad, and the discarded token is counted Accepted
0097 A courtyard keeps out on the layer it is drawn on, not on its footprint's side Accepted
0098 A mutation claim is evidence only if the repository can re-run it Accepted
0099 A pad refusal names the field it refused, and seven of eight fabrication properties convert Accepted
0100 A custom pad has a derivable envelope and nowhere in Board IR to put it Accepted
0101 Fill currency is not in the board file, so keep the model and gate the shrink Accepted
0102 A refusal evaluation must observe a permit, and prove it kept observing one Accepted
0103 A candidate records the obstacle model that produced it, and a replay refuses every other one Accepted
0104 The fill-vertex budget sits behind a parse, and is calibrated as what it is Accepted
0105 A schema version moves with its accepted set, and 0.2.0 is frozen where it stands Accepted
0106 A layered candidate records its obstacle model before the layered seam may reach one Accepted
0107 An aggregator's repository licence does not govern the data it aggregated Accepted
0108 The single-layer fill seam refuses malformed evidence under its own numbers Accepted
0109 A published DRC count carries the comparability it was taken with Accepted
0110 Placement boundary verdicts bracket KiCad parity Accepted
0111 Carry custom-pad copper separately from its attachment anchor Accepted
0112 External route candidates enter through a bounded disposer Accepted
0113 External route patches preserve multi-pin topology Accepted
0114 Accepted external candidates continue to private KiCad DRC Accepted
0115 External route verification is a versioned read-only MCP boundary Accepted
0116 Layered fill islands have a measured source boundary Accepted
0117 Local exact repair is an opt-in verified transaction Accepted (two-pad repair-target clause widened by 0127)
0118 Keep authoritative signoff closed until a bounded executor exists Accepted
— 0119 is a live parallel-branch claim and is not free; see Adding an ADR above. —
0119 A sign-off claim rests on repeated agreement from a registered backend Accepted
0120 Withheld apply authority has one closed, non-echoing reason Accepted
0121 A refusal is an answer and a crash is not Accepted
0122 The stackup is read per field, because three of its fields are not about Z Accepted
0123 A container refusal that names no field is the defect, not the refusal Accepted
0124 An outline arc is inscribed, and an arc that cuts into the board is refused Accepted
0125 Stray footprint copper is bounded by a box because no fill rule is written down Accepted
0126 Negotiated routing admits bounded multi-pin nets on request-local lattices Accepted (multi-pin repair deferral satisfied by 0127)
0127 Multi-pin local repair replaces one proven responsible branch Accepted
0128 A private surrogate ranking is bounded and never signs off Accepted
0129 The live version window is KiCad's own, and an acceptance is never published as a proof Accepted
0130 Use OrcaRouter only as a redacted advisory policy provider Proposed
0131 Composed bundle DRC evidence Accepted
0132 Supervised optimization keeps evidence, consent and application separate Proposed; internal foundation only
0133 Native optimization execution and host confirmation Proposed
0134 Freeze readiness requirements before measuring progress Proposed
0135 Inner placement exhaustion is not completed search work Proposed
0136 Electrical artifact capture is not engineering authority Proposed
0137 Isolate census recomputation without dropping compatibility checks Proposed
0138 Private bounded schematic hierarchy Proposed
0139 Captured project bytes precede electrical execution Proposed
0140 Project ERC observations do not invent intent identities Proposed
0141 Project ERC prepares an explicit rule and library derivative Proposed
0142 Project connectivity ERC binds execution and source freshness Proposed
0143 Project parity uses native liveness and immutable candidates Proposed
0144 Native component inventory precedes BOM reconciliation Proposed
0145 BOM agreement binds content and native components Proposed
0146 Placed-pin census precedes model interface binding Proposed
0147 Captured SPICE definitions precede terminal binding Proposed
0148 Native pin-net maps preserve source aliases Proposed
0149 Project terminal joins consume native evidence Proposed
0150 Confined SPICE source preserves declared settings Proposed
0151 Operating-point numbers are not physics authority Proposed
0152 Joint native views share acquisition, not authority Proposed
0153 Confined ngspice execution confirms cleanup Proposed
0154 Operating-point cases bind explicit inputs Proposed
0155 Operating-point topology is conservative preflight Proposed
0156 Project operating point is bounded observation Proposed
0157 Verified-host confirmation is challenge-bound Proposed
0158 Layered trees bind complete pad connectivity Proposed
0159 Composed clearance is an input-bound measurement Proposed
0160 Custom-rule loading needs a native witness Proposed
0161 Measured optimization is an end-to-end versioned workflow Proposed
0162 Cutouts survive the ordinary-board workflow Proposed

One hundred and thirty-five numbers allocated, one hundred and thirty records, no duplicates — and scripts/check_adr_numbers.py proves that last clause on every run rather than asserting it. Read its output, not this sentence: three earlier revisions of it stood here at once, disagreeing about the count, and each was stale by a landing or two before it was ever read. 0027, 0082, 0083, 0085 and 0086 are unused; see Known gaps above. 0107 was allocated over the live claims 0105 and 0106 rather than racing them, the mechanism the ledger README describes for D-/R-/B- numbers. Both have since landed — 0106 with issue #164's record and 0105 with issue #172's — so neither is a gap, and all of 0100 through 0119 are real records in the index above.

Reading order

The ADRs are chronological, not thematic. To follow one arc, read it in this order:

2026-09-05 integration: the reserved ADR-0131 from PR #265 is included, and ADR-0133 records execution above the existing ADR-0132 foundation. No spent identifier is recycled.

2026-09-08 corrective increment: ADR-0155 through ADR-0159 remain held by the topology, project-execution, host-consent, layered-tree and measured-clearance lanes. The urgent DRC correction takes ADR-0160 and amends existing D-005/SEC-003; no held number is recycled.

2026-09-08 parallel increment: ADR-0154 through ADR-0157 are held by the operating-point cases, topology, project execution and host-consent stages. The layered-tree increment takes ADR-0158 rather than reusing those pending identities. Its isolated branch does not yet contain their files.