36a - §1–§6's decisions move to docs/decisions.md, indexed from AGENTS.md #134

Merged
lilleman merged 2 commits from 36a into main 2026-09-28 00:48:21 +02:00
4 changed files with 38 additions and 34 deletions
Showing only changes of commit c694896090 - Show all commits
+13 -12
View File
@@ -172,16 +172,16 @@ functions, and a branch floor that only ever moves upward. It sits below 100 bec
compared against `undefined` — have a half no valid document reaches. compared against `undefined` — have a half no valid document reaches.
The size ratchet is the other such number, `.oxlintrc.json`'s single rule over the files The size ratchet is the other such number, `.oxlintrc.json`'s single rule over the files
`tsconfig.build.json` builds — `oxlint`, since TypeScript 7 is a native compiler publishing no `tsconfig.build.json` builds, measured by `oxlint` since TypeScript 7 is a native compiler
in-process parser, only the `unstable/` AST surface an out-of-process handshake reaches: a publishing no in-process parser, only the `unstable/` AST surface an out-of-process handshake
per-function line ceiling, set at that set's worst and moving only downward. It covers the built reaches. It is a per-function line ceiling, set at that set's worst and moving only downward. It
files alone, since one ceiling over the tests too would have to be their worst, loosening the guard covers the built files alone, since one ceiling over the tests too would have to be their worst,
over the shipped code. It guards against drift and never drives a refactor, so no cyclomatic rule loosening the guard over the shipped code. It guards against drift and never drives a refactor, so
and no second lint rule join it: neither measure picked out what nine readers found hard (the no cyclomatic rule and no second lint rule join it: neither measure picked out what nine readers
comprehension panel, 2026-09-20). Three switches guard a silent green: `IIFEs: true`, since oxlint found hard (the comprehension panel, 2026-09-20). Three switches guard a silent green: `IIFEs:
exempts an IIFE otherwise; an explicit `-c`, so a config gone missing fails the leg instead of true`, since oxlint exempts an IIFE otherwise; an explicit `-c`, so a config gone missing fails the
falling back to oxlint's own defaults; and `--deny-warnings`, since a rule from a category this leg instead of falling back to oxlint's own defaults; and `--deny-warnings`, since a rule from a
config never names arrives as a warning it exits 0 on. category this config never names arrives as a warning it exits 0 on.
The corpus, all checked in: hand-built fixtures per node and combination; real ADF Atlassian's The corpus, all checked in: hand-built fixtures per node and combination; real ADF Atlassian's
editor wrote; the CommonMark spec suite against `markdownToAdf` and `markdownToHtml`. editor wrote; the CommonMark spec suite against `markdownToAdf` and `markdownToHtml`.
@@ -372,8 +372,9 @@ The verdict lands in the item it settles (the maintainer, 2026-09-25).
### Rules the loop has settled (the maintainer, 2026-09-18) ### Rules the loop has settled (the maintainer, 2026-09-18)
- A finding inside the chunk's item is fixed in the chunk. Outside it, a new `todo.md` item, always - A finding inside the chunk's item is fixed in the chunk. Outside it, a new `todo.md` item, always
in a release, weighed against every item on that release by the personas and Goals 1 and 2 — an in a release, weighed against every item on that release by the personas and `docs/decisions.md`
item it outweighs moves later. A weighing no rule decides is asked as a gap. §Plain markdown is a flavour of the grammar through §Names stay text — an item it outweighs moves
later. A weighing no rule decides is asked as a gap.
- A stated number — 500 levels, the branch floor — is kept; a chunk that cannot keep it asks, - A stated number — 500 levels, the branch floor — is kept; a chunk that cannot keep it asks,
naming the number it can reach. A number the code needs and no rule states is a gap. naming the number it can reach. A number the code needs and no rule states is a gap.
- An earliest release with no items left and nothing shipped toward it is planned as the chunk: - An earliest release with no items left and nothing shipped toward it is planned as the chunk:
+6 -4
View File
@@ -5,7 +5,8 @@ an HTML dialect.
**Status: published — the markdown round-trip (`adfToMarkdown`, `markdownToAdf`); HTML at **Status: published — the markdown round-trip (`adfToMarkdown`, `markdownToAdf`); HTML at
`0.2.0`.** `0.2.0`.**
Plan: `todo.md`. Decisions: `AGENTS.md`. Changes: Plan: `todo.md`. Decisions:
[`docs/decisions.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/docs/decisions.md). Changes:
[`CHANGELOG.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/CHANGELOG.md). The lossless flavour's grammar: [`CHANGELOG.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/CHANGELOG.md). The lossless flavour's grammar:
[`spec/flavour.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/spec/flavour.md). [`spec/flavour.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/spec/flavour.md).
Upgrading from `0.1.0`: [convert your markdown first](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/MIGRATION.md). Upgrading from `0.1.0`: [convert your markdown first](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/MIGRATION.md).
@@ -143,7 +144,7 @@ saving what this pair read replaces mentions, attachments and macros with text.
## The errors ## The errors
An ADF node type this version does not know is not an error: it is carried opaquely and restores An ADF node type this version does not know is not an error: it is carried opaquely and restores
unchanged (`docs/decisions.md §Unknown nodes ride the carry`). unchanged ([`docs/decisions.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/docs/decisions.md#unknown-nodes-ride-the-carry)).
`ConvertError` is `{ code, message, path, position? }`. `code` is the exported `ConvertErrorCode`, `ConvertError` is `{ code, message, path, position? }`. `code` is the exported `ConvertErrorCode`,
stable across minors and safe to `switch` on exhaustively with no `default`; `message` is free text stable across minors and safe to `switch` on exhaustively with no `default`; `message` is free text
@@ -195,7 +196,7 @@ emit refuses:
## The guarantees ## The guarantees
- `markdownToAdf(adfToMarkdown(doc))` equals `doc` — unknown node types included, carried opaquely - `markdownToAdf(adfToMarkdown(doc))` equals `doc` — unknown node types included, carried opaquely
(`docs/decisions.md §Unknown nodes ride the carry`). ([`docs/decisions.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/docs/decisions.md#unknown-nodes-ride-the-carry)).
- Plain CommonMark is valid input to `markdownToAdf` apart from the raw HTML `unmappable-html` - Plain CommonMark is valid input to `markdownToAdf` apart from the raw HTML `unmappable-html`
names, with three carve-outs — literal text matching directive, pipe-table or strikethrough names, with three carve-outs — literal text matching directive, pipe-table or strikethrough
syntax is claimed (escapable — `spec/flavour.md`) — and one gap: a CommonMark image fits only as syntax is claimed (escapable — `spec/flavour.md`) — and one gap: a CommonMark image fits only as
@@ -234,4 +235,5 @@ emit refuses:
ESM only, no runtime dependencies, public npmjs. Built JavaScript with `.d.ts` beside it. ESM only, no runtime dependencies, public npmjs. Built JavaScript with `.d.ts` beside it.
Pure ECMAScript at an ES2022 baseline, reaching for no host API; the test suite runs under Node, Pure ECMAScript at an ES2022 baseline, reaching for no host API; the test suite runs under Node,
Deno and Bun, and a headless Firefox converts the corpus through the built entrypoint. Deno and Bun, and a headless Firefox converts the corpus through the built entrypoint.
Contract: `docs/decisions.md`. Contract: [`docs/decisions.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/docs/decisions.md#any-es2022-engine), §Any
ES2022 engine to §Public on npm.
+8 -7
View File
@@ -40,7 +40,7 @@ array the absent key — the only domain markdown can restore.
## Unknown nodes ride the carry ## Unknown nodes ride the carry
2026-08-23, extended to misplaced known nodes 2026-08-26, the maintainer. Goal 1. Valid while ADF 2026-08-23, extended to misplaced known nodes 2026-08-26, the maintainer. Goal 1. Valid while ADF
gains node types faster than this library spells them. holds nodes, or node positions, this library does not spell.
An unknown ADF node is carried opaquely — raw JSON rides a dedicated syntax in both formats and An unknown ADF node is carried opaquely — raw JSON rides a dedicated syntax in both formats and
restores to a deep-equal node. The round-trip holds for documents newer than the library. So does restores to a deep-equal node. The round-trip holds for documents newer than the library. So does
@@ -76,7 +76,7 @@ opener nests by itself and leaf versus container falls out of the node's content
## CommonMark is a subset ## CommonMark is a subset
2026-08-23, the maintainer. Goal 3. Valid while the carve-outs stay the flavour's only claims. 2026-08-23, the maintainer. Goal 3. Valid while prose rarely writes the shapes the carve-outs claim.
Plain CommonMark is a subset, with carve-outs (`spec/flavour.md`): literal text shaped like a Plain CommonMark is a subset, with carve-outs (`spec/flavour.md`): literal text shaped like a
directive, a pipe table or a `~~` pair is claimed — plus one image gap. directive, a pipe table or a `~~` pair is claimed — plus one image gap.
@@ -117,7 +117,8 @@ for what HTML cannot express, text always escaped. No stylesheet ships.
## No runtime dependencies ## No runtime dependencies
2026-08-23, the maintainer. Goal 7. Valid while Goal 7 names no runtime dependencies. 2026-08-23, the maintainer. Goal 7. Valid while ~20 lines of own code, or a vendored table, do each
job a dependency would.
`dependencies` is empty. A runtime dependency enters only through an entry here stating why ~20 `dependencies` is empty. A runtime dependency enters only through an entry here stating why ~20
lines of own code cannot do the job, who maintains it, and what auditing it costs. So the CommonMark lines of own code cannot do the job, who maintains it, and what auditing it costs. So the CommonMark
@@ -125,8 +126,8 @@ and HTML parsers are written in this repo.
## Standards ship as data ## Standards ship as data
2026-08-30, the CommonMark suite 2026-09-05 and ADF's schemas 2026-09-13, the maintainer. Goals 1 2026-08-30, the CommonMark suite 2026-09-05 and ADF's schemas 2026-09-13, the maintainer. Goals 1,
and 7. Valid while each table's upstream package is CommonJS-only or heavy. 3 and 7. Valid while each table is fixed data a dependency would only wrap.
A table a standard fixes is data rather than a dependency: HTML5's 2125 semicolon-terminated A table a standard fixes is data rather than a dependency: HTML5's 2125 semicolon-terminated
character references ship packed in their own module, so entity decoding is complete without one. character references ship packed in their own module, so entity decoding is complete without one.
@@ -163,7 +164,7 @@ never the higher one those repo-only tools want.
## ESM only ## ESM only
2026-08-23, the maintainer. Goal 7. Valid while every supported engine loads ES modules. 2026-08-23, the maintainer. Goal 7. Valid while the audience's toolchains all import ES modules.
No CommonJS build, no dual-package hazard. No CommonJS build, no dual-package hazard.
@@ -177,7 +178,7 @@ an npm consumer.
## Public on npm ## Public on npm
2026-08-23, the maintainer. Goal 7 and the Audience. Valid while the audience installs from public 2026-08-23, the maintainer. The Audience. Valid while the audience installs from public
npm. npm.
Published to public npmjs as `@larvit/adf-codec`. Public source: the Gitea repo goes public, Published to public npmjs as `@larvit/adf-codec`. Public source: the Gitea repo goes public,
+11 -11
View File
@@ -156,13 +156,13 @@ naming no open container or a node other than the innermost open one, a leaf giv
unparseable or duplicate-keyed attrs, invalid JSON in an opaque carry. Never a silent literal-text unparseable or duplicate-keyed attrs, invalid JSON in an opaque carry. Never a silent literal-text
fallback — a typo that reparses as prose is the silent loss the round-trip refuses. fallback — a typo that reparses as prose is the silent loss the round-trip refuses.
## The opaque carry (`docs/decisions.md` §Unknown nodes ride the carry) ## The opaque carry
A node no section spells where it stands — an unknown type, or a known one whose spelling belongs to A node no section spells where it stands (`docs/decisions.md` §Unknown nodes ride the carry) — an
the other position — rides as its raw JSON and restores to a deep-equal node. A carry may hold a unknown type, or a known one whose spelling belongs to the other position — rides as its raw JSON
node the emitter spells natively: it restores unreinterpreted, and the next emit spells it and restores to a deep-equal node. A carry may hold a node the emitter spells natively: it restores
canonically (`docs/decisions.md` §The round-trip is the product). Block and inline positions unreinterpreted, and the next emit spells it canonically (`docs/decisions.md` §The round-trip is the
canonicalize differently, each fitting where it sits: product). Block and inline positions canonicalize differently, each fitting where it sits:
- **Block position**: a fenced code block with info string `carry`, body = the node's JSON — - **Block position**: a fenced code block with info string `carry`, body = the node's JSON —
two-space indent, object keys sorted. two-space indent, object keys sorted.
@@ -490,11 +490,11 @@ the directive form, open to no literal reading, is a named error.
A spelling adds its mark to every inline node it wraps, and nesting is the marks array in order, A spelling adds its mark to every inline node it wraps, and nesting is the marks array in order,
outermost first: `_!adf:underline[x]_` gives marks `[em, underline]`, `!adf:underline[_x_]` the outermost first: `_!adf:underline[x]_` gives marks `[em, underline]`, `!adf:underline[_x_]` the
reverse. reverse. `adfToMarkdown` nests in the order the array holds rather than sorting it —
`adfToMarkdown` nests in the order the array holds rather than sorting it — §2's equality `docs/decisions.md` §Equality is editor-normal restores the array, not a set — and opens each
restores the array, not a set — and opens each spelling once over the longest run of adjacent spelling once over the longest run of adjacent inline nodes carrying an identical mark, attributes
inline nodes carrying an identical mark, attributes included, at that depth. A run breaks at every included, at that depth. A run breaks at every node the emitter carries, so no emitted carry sits
node the emitter carries, so no emitted carry sits inside a mark spelling. inside a mark spelling.
An inline node whose marks no nesting spells — a mark type not listed here, an attrs key its An inline node whose marks no nesting spells — a mark type not listed here, an attrs key its
spelling does not list, a value that is not the spelling's type, an attribute the spelling needs spelling does not list, a value that is not the spelling's type, an attribute the spelling needs