From f66efabcbb87902913987522686ee3969dc06970 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sun, 13 Sep 2026 15:34:32 +0200 Subject: [PATCH] Design 11: vendor Atlassian's ADF schema and gate the node tables against it --- AGENTS.md | 13 +++++++++++-- todo.md | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1b476a5..d771ea9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -63,8 +63,11 @@ module, so entity decoding is complete without one. The CommonMark spec suite is data and ships vendored at `corpus/commonmark-spec/` rather than as the `commonmark-spec` dev dependency — that package is CommonJS-only, and Renovate auto-bumping a spec version would silently point the vendored exception list's example numbers at a renumbered suite. A spec bump is a -deliberate re-pin, exceptions re-derived by hand beside it. `devDependencies`: few, each earning its -keep; they never reach a consumer. +deliberate re-pin, exceptions re-derived by hand beside it. Atlassian's ADF JSON Schemas ship +vendored the same way, at `spec/adf-schema/`, rather than as the `@atlaskit/adf-schema` dev +dependency — CommonJS-only, some fifty packages with React among them, and a release most days for +Renovate to automerge — re-pinned by hand when a payload or a report shows the need. +`devDependencies`: few, each earning its keep; they never reach a consumer. ## 6. The package contract @@ -231,6 +234,12 @@ of a bullet; fenced examples are skipped. It guards the attributes alone: nodes content model share a bullet, and the argument attribute is spelled ahead of `Attributes: `, so both answer to the round-trip corpus and to nothing else where a node has no fixture. +The tables answer to Atlassian's schema too (§5): for every node and mark they spell, the attribute +names and kinds equal what `full.json` and `stage-0.json` hold between them. Value sets stay +documentation, since any value round-trips. What the schema holds and the tables do not spell is +pinned by name — an attribute as a gap, a type as carried — so a re-pin adding either goes red until +someone spells it or pins it. + ## 11. Code rules - Two-space indent, strict TypeScript, English everywhere. Alphabetical order wherever order diff --git a/todo.md b/todo.md index d516b01..8c1d50b 100644 --- a/todo.md +++ b/todo.md @@ -5,11 +5,11 @@ milestone. A done item shrinks to its title here; its full text moves to `todo-h ## Milestones -Shipping order: 3h, 3i, 3j, 5a, 5b, 5c, 5d, 5 → `0.1.0` (shipped 2026-09-05); 3k, 11, 4, 12, 4b, 4c, 10, 5g → `0.2.0`; 4d, 5f → `0.2.1`; +Shipping order: 3h, 3i, 3j, 5a, 5b, 5c, 5d, 5 → `0.1.0` (shipped 2026-09-05); 3k, 11, 4, 12, 13, 4b, 4c, 10, 5g → `0.2.0`; 4d, 5f → `0.2.1`; 6, 7 → `0.3.0`; 9 → TBD; 5e last. The numbering is the order the work was planned in, not the order it ships. `0.2.0`'s order is settled (the maintainer, 2026-09-13): 11 makes the tables 4 generates from answer to Atlassian's schema, 4 -proves 12, and 12 rewrites code 4b and 4c change. +proves 12, 13 spells 11's gaps in 12's grammar, and 12 rewrites code 4b and 4c change. - [x] **0 — Scaffold.** - [x] **1a — The directive grammar.** @@ -57,6 +57,10 @@ proves 12, and 12 rewrites code 4b and 4c change. a document that round-trips proves no other document shares its spelling — so decide here whether that gate stays as the parser-free, faster-failing signal or goes; the half holding no fixture duplicates is hygiene rather than a round-trip claim, and stays either way. + **Settled** (the maintainer, 2026-09-13): the generators draw from the node tables — each + node's content model and attribute vocabulary as `adf/` records them, which 11 holds to + Atlassian's schema — and misplace a share of nodes so the carry (§3) is exercised; no JSON + Schema walker enters the tests. - [ ] **4b — The block walk's retry (`0.2.0`).** `emitBlock` walks a subtree twice wherever `readableBlock` reads it whole and then gives up — a list item whose first line reads back as a thematic break — and the walk below does the same, so the cost doubles per level: @@ -146,7 +150,27 @@ proves 12, and 12 rewrites code 4b and 4c change. - [ ] **8 — CLI.** A later goal, shaped around the personas once the library exists. - [ ] **9 — The online sandbox.** A web page with two textboxes converting back and forth between ADF and markdown, powered by the library's browser build. - [ ] **10 — Lossy conversion (`0.2.0`).** A direction that only converts what Markdown actually supports, keeping the ADF's data while dropping what markdown cannot hold — format, design and the richer nodes. -- [ ] **11 — Evaluate `@atlaskit/adf-schema` (`0.2.0`).** Whether to add `@atlaskit/adf-schema` as a dev dependency to use as truth for the ADF schema. +- [ ] **11 — Atlassian's ADF schema as the tables' truth (`0.2.0`).** `@atlaskit/adf-schema`'s two + JSON Schemas vendored rather than the package installed (AGENTS.md §5), and the node tables + gated against them (§10). **Settled** (the maintainer, 2026-09-13): vendored at + `spec/adf-schema/` and re-pinned by hand when a need shows; the gate compares attribute names + and kinds, never value sets, over `full.json` and `stage-0.json` together. + - [ ] **11a — The vendored schema.** `full.json` and `stage-0.json`, byte-exact from + `@atlaskit/adf-schema@57.4.9`'s `dist/json-schema/v1/`, at `spec/adf-schema/`, each pinned + by its SHA-256 in a test the way `spec.json` is. The version, the source and the Apache-2.0 + attribution sit beside them with the licence text; no gate re-serializes either file. + - [ ] **11b — The gate.** For each node and mark type the tables spell, the attribute names and + kinds equal the union over every definition in both files whose `type` enum names it, + `anyOf`/`allOf` branches included, the argument slot (`panelType`, `state`) counting as + spelled. Kinds: `string`; `number`, `integer` included; `boolean`; `json` for an object, an + array or an untyped value; an `enum`-only attribute takes its values' kind. What the schema + holds past the tables is pinned in two exact lists — an entry the schema no longer needs is + red, like a difference neither list names: gaps, attributes of a spelled type (57.4.9: + `link` `collection` `id` `occurrenceKey`, `rule` `color` `style` `weight`, `layoutSection` + `columnRuleStyle`), emptied by 13; and carried, types the tables do not spell (`alignment` + `annotation` `backgroundColor` `blockCard` `bodiedRule` `breakout` `dataConsumer` + `embedCard` `fontSize` `fragment` `indentation` `inlineExtension` `placeholder`), `doc` and + `text` counting as the grammar's own. - [ ] **12 — The `!adf:` re-spelling (`0.2.0`).** Replace the colon directive grammar with the namespaced prefix, a breaking change to the emitted contract (shipped `0.1.0`, so §8 makes it `0.2.0`). Forms: block container `!adf:name arg {attrs}` … `!adf:/name` — the `/` parts open @@ -177,6 +201,11 @@ proves 12, and 12 rewrites code 4b and 4c change. `markdownToAdf(adfToMarkdown(doc))` still equals `doc`. - [ ] **12d — The README and the sweep.** The README's examples follow; sweep docs and fixtures for any stale `::`/`:name` spelling. +- [ ] **13 — The schema's gap attributes (`0.2.0`).** Spell the attributes 11b pins as gaps, in + 12's grammar, and empty the list. `rule`'s `color`, `style` and `weight` and + `layoutSection`'s `columnRuleStyle` join their tables and `spec/flavour.md` bullets. `link`'s + `collection`, `id` and `occurrenceKey` have no slot in `[text](url)`, so a link carrying one + rides the inline carry whole today; the spelling it takes instead is designed here. ## The ADF inventory to cover