Decision records
One file per decision, kept because the reasoning is the part that rots invisibly: a config line can be re-read, but the four options we rejected and why cannot be recovered from the tree. An agent reaching a fork already covered here should find the answer instead of re-deriving it, and more importantly should find out when the answer was withdrawn.
That last part is why this index exists. Five of these records no longer say what they appear to say, and until now the only way to learn that was to open the file. A retracted ADR read as live guidance is worse than no ADR at all: ADR 0018's mechanism (splice a module's source into vbaProject.bin) was verified, documented, confidently written, and does not work.
Status is the column to read first. Retracted records are kept rather than deleted, because the disproof is the valuable part, but they are guidance about history, never about what to do now.
The acceptance date orders these, not the number. ADR 0029 was renumbered out of a collision and sits last while dating from 2026-07-19; renumbering the twenty-seven records after it would have broken far more cross-references than the collision did.
| # | Decision | Status |
|---|---|---|
| 0001 | Rewrite runtime & toolchain: run .ts directly, defer the bundler | Accepted 2026-07-11 · build slice resolved 07-19 by addendum |
| 0002 | Microsoft OpenXmlValidator as an external conformance oracle | Accepted 2026-07-11 · mechanism superseded 2026-08-15 by 0033; the stance stands |
| 0003 | Zip container is fflate; the write path emits XML directly | Accepted 2026-07-12 |
| 0004 | The XML read path is a lean, hand-written SAX pull parser | Accepted 2026-07-12 |
| 0005 | WorksheetModel stays a semantic value; attached parts are out of scope | Accepted 2026-07-18 |
| 0006 | API docs generated from the types, not a docs framework | Accepted 2026-07-19 |
| 0007 | Spec reference: vendored OOXML schemas + Microsoft Learn MCP | Accepted 2026-07-19 · schema half superseded 2026-08-15 by 0034; the Learn MCP half stands |
| 0008 | Upgrade to TypeScript 6; hold at 6 (not 7) until the printer API ports | Superseded in part 2026-08-05 by 0028 · Accepted 07-19 |
| 0009 | Tighten the lint/type gates where free; decline isolatedDeclarations | Accepted 2026-07-20 · the posture stands; the tool enforcing it changed 2026-08-25, see 0036 |
| 0010 | Make the correctness net easy for agents to dispatch | Accepted 2026-07-20 |
| 0011 | Type-check the harness: migrate test/ + scripts/ to strict .ts | Accepted 2026-07-20 |
| 0012 | Three tiers of correctness evidence; round-trip proves consistency, not conformance | Accepted 2026-07-21 |
| 0013 | Excel Desktop is an automatable Tier-3 oracle for state-observable behavior | Accepted 2026-07-21 |
| 0014 | Charts, vector shapes, slicers, and form controls stay round-trip-only for 1.0 | Accepted 2026-07-21 |
| 0015 | Package name, SemVer, and the first published version | Accepted 2026-07-21 |
| 0016 | The VBA project is readable through a typed view; authoring stays deferred | Accepted 2026-07-22 · amended 07-23 by 0017, which 0019 then retracted; read 0019 before relying on the amendment |
| 0017 | VBA authoring is in scope; the consumer gate is lifted | Retracted 2026-07-24 by 0019 · originally Accepted 07-23 |
| 0018 | Editing an existing macro's source is done by splicing the original .bin | Retracted 2026-07-24 by 0019 |
| 0019 | VBA authoring needs real, compiled p-code; the "recompile cookie" premise is retracted | Accepted 2026-07-24 |
| 0020 | The customUI ribbon is readable through a typed view; authoring stays deferred | Accepted 2026-07-24 |
| 0021 | The VBA project signature is readable as presence, not verified as validity | Accepted 2026-07-24 |
| 0022 | Verification is one cached, parallel entrypoint | Accepted 2026-07-27 |
| 0023 | Seven subpath entry points, disjoint by construction, with the error taxonomy as its own face | Accepted 2026-07-29 |
| 0024 | Async is one writer, not a mirrored pair | Accepted 2026-07-29 |
| 0025 | The workbook default font is declared, not assumed | Accepted 2026-07-29 |
| 0026 | Releasing is a GitHub release, and npm follows with no credential | Accepted 2026-07-29 |
| 0027 | Dependencies are updated by a bot, and CI is the reviewer | Accepted 2026-07-30 |
| 0028 | Move to TypeScript 7; the compiler API scripts move to unstable/* | Accepted 2026-08-05 |
| 0029 | Toolchain standup: Biome for lint/format, node --test kept, tsc for type tests | Accepted 2026-07-19 · lint/format half superseded 2026-08-25 by 0036; the rest stands · renumbered 2026-08-08 from a collision at 0002 |
| 0030 | src/io/xlsx/ stays flat; the read/write directory split is rejected | Accepted 2026-08-08 |
| 0031 | The emitted declarations are typechecked too; two more gate flags go on | Accepted 2026-08-08 |
| 0032 | Package output is reproducible: entry timestamps are pinned, not clocked | Accepted 2026-08-11 |
| 0033 | The OOXML oracle is a shared package, not a repo-owned .NET tool | Accepted 2026-08-15 · supersedes the mechanism of 0002 |
| 0034 | The schema reference is a queryable graph, not a vendored XSD dump | Accepted 2026-08-15 · supersedes part 1 of 0007 |
| 0035 | Coverage is the union of both suites, measured with node's own implementation | Accepted 2026-08-25 · picks up the coverage thread deferred by 0029 |
| 0036 | oxlint and oxfmt replace Biome; the type-aware rules come from tsgolint | Accepted 2026-08-25 · supersedes the toolchain half of 0029 · its exemption counts superseded 2026-08-26 by 0037 + 0038 |
| 0037 | Every tree the linter type-checks has a tsconfig.json the linter can find | Accepted 2026-08-26 · corrects a mechanism 0036 observed but did not diagnose |
| 0038 | The corpus keeps its untyped boundary, and it costs six rules rather than sixteen | Accepted 2026-08-26 · answers the question 0036 deferred |
| 0039 | The website is generated from the docs tree, and drift is a build error | Accepted 2026-08-27 |
| 0040 | The browser boundary is an entry point, and a gate walks the graph to prove it | Accepted 2026-08-28 · narrows the entry-point set of 0023 |
| 0041 | One date-format vocabulary, and it is the spreadsheet's | Accepted 2026-09-03 · changes the meaning of CsvWriteOptions.dateFormat without changing its type |
| 0042 | A hyperlink belongs to the sheet, not to a cell's value | Accepted 2026-09-12 |
Writing one
Take the next free number. State the decision in the title as a claim, not a topic. "Async is one writer, not a mirrored pair" tells a reader what changed; "Async I/O" does not. Give the status line a date and, when the record depends on or alters another, say which and how. Then add the row here, because an index nobody updates is how a retracted record gets read as live guidance.
When a decision turns out to be wrong, retract it in place: mark the status, name the record that retracts it, keep the body, and say in one line what specifically failed. Do not delete it and do not quietly edit the body into being correct. The disproof is why the file is worth keeping, and a body edited to match hindsight destroys exactly the evidence that would stop the next agent trying the same thing.