10 - decisions: the lossy pair never saves back, a callout's title is its marker line, task ids by position, 5g names the flavours
CI / gate (push) Successful in 42s
CI / publish (push) Successful in 6s

This commit was merged in pull request #131.
This commit is contained in:
2026-09-26 14:45:29 +02:00
parent 1f4a94974a
commit 1271365b38
3 changed files with 28 additions and 15 deletions
+2 -1
View File
@@ -9,7 +9,8 @@ that item's entry in `todo-history.md`.
ADF, one markdown flavour, one HTML dialect. Six directions exposed, but markdown↔HTML compose ADF, one markdown flavour, one HTML dialect. Six directions exposed, but markdown↔HTML compose
through ADF: four conversions exist to keep correct — never write a fifth. The lossy pair wraps through ADF: four conversions exist to keep correct — never write a fifth. The lossy pair wraps
two of them, an ADF→ADF reduction ahead of `adfToMarkdown` and an ADF→ADF lift after two of them, an ADF→ADF reduction ahead of `adfToMarkdown` and an ADF→ADF lift after
`markdownToAdf`, and whatever it adds stays ADF→ADF (the maintainer, 2026-09-14). No fourth format, `markdownToAdf`, and whatever it adds stays ADF→ADF (the maintainer, 2026-09-14) — save a callout's
marker line, which the lift reads through the block parser (10e, 2026-09-26). No fourth format,
ever; each one doubles the directions. ever; each one doubles the directions.
## 2. The round-trip is the product ## 2. The round-trip is the product
+3 -1
View File
@@ -39,7 +39,9 @@ In priority order.
plain markdown cannot hold — format, design, structure — never content: what a reader of the plain markdown cannot hold — format, design, structure — never content: what a reader of the
rendered document sees or follows, its text, images and link targets. Content the document only rendered document sees or follows, its text, images and link targets. Content the document only
references is marked where it stood, by a note that reads as the converter's and names what was references is marked where it stood, by a note that reads as the converter's and names what was
left out. What is dropped goes the way the audience expects. left out. What is dropped goes the way the audience expects. The lossy pair creates and
exports; it never saves back over the document it read — identity (task, mention, media ids)
lives only in the lossless pair.
6. **Failures are values.** Nothing throws, and `code` is a closed list — as much a contract as 6. **Failures are values.** Nothing throws, and `code` is a closed list — as much a contract as
the emitted formats are. the emitted formats are.
7. **Nothing in the way.** No runtime dependencies, no I/O, no configuration, no host API: ESM on 7. **Nothing in the way.** No runtime dependencies, no I/O, no configuration, no host API: ESM on
+23 -13
View File
@@ -143,7 +143,10 @@ chunk clearing a §11 seam.
everything exported sits near the bottom. The HTML directions were to stay an aside until a everything exported sits near the bottom. The HTML directions were to stay an aside until a
later release shipped them; 7 now ships in this one and reads ahead of this item, so the later release shipped them; 7 now ships in this one and reads ahead of this item, so the
README documents HTML as it documents markdown, the tagline and `description` naming both README documents HTML as it documents markdown, the tagline and `description` naming both
(the maintainer, 2026-09-13, revised 2026-09-18). (the maintainer, 2026-09-13, revised 2026-09-18). They name the lossy pair too, and the
flavours it writes and reads by name — GitHub Flavored Markdown's alerts and task lists,
Obsidian Flavored Markdown's callouts — so a search for either finds the package (the
maintainer, 2026-09-26).
- [x] **5a — Rename to `@larvit/adf-codec`.** - [x] **5a — Rename to `@larvit/adf-codec`.**
- [x] **5b — The consumer's error surface.** - [x] **5b — The consumer's error surface.**
- [x] **5b1 — The error's source position.** - [x] **5b1 — The error's source position.**
@@ -198,7 +201,7 @@ chunk clearing a §11 seam.
Discord, GitHub's renderer confirming each shape. Writing refuses only what the document guard Discord, GitHub's renderer confirming each shape. Writing refuses only what the document guard
refuses (`not-an-adf-document`, `unsupported-document-version`, `unsupported-nesting-depth`) refuses (`not-an-adf-document`, `unsupported-document-version`, `unsupported-nesting-depth`)
and degrades every other shape; reading refuses what `markdownToAdf` refuses. A lifted node and degrades every other shape; reading refuses what `markdownToAdf` refuses. A lifted node
carries no `localId`. The lift also reads other tools' spellings — type words in any case, carries no `localId`, save a task node's position id (10f, the maintainer, 2026-09-26). The lift also reads other tools' spellings — type words in any case,
Obsidian's aliases, `[X]` — since it reads their output and never writes those spellings. Obsidian's aliases, `[X]` — since it reads their output and never writes those spellings.
- A `panel` is an alert: the marker alone on the quote's first line, a blank `>`, then the body - A `panel` is an alert: the marker alone on the quote's first line, a blank `>`, then the body
(`> [!WARNING]`), in GitHub's five words by colour — info `NOTE`, note `IMPORTANT`, tip and (`> [!WARNING]`), in GitHub's five words by colour — info `NOTE`, note `IMPORTANT`, tip and
@@ -206,11 +209,12 @@ chunk clearing a §11 seam.
back (`NOTE` info, `IMPORTANT` note, `TIP` tip, `WARNING` warning, `CAUTION` error) and back (`NOTE` info, `IMPORTANT` note, `TIP` tip, `WARNING` warning, `CAUTION` error) and
Obsidian's by meaning (hint tip; success, check and done success; attention warning; danger, Obsidian's by meaning (hint tip; success, check and done success; attention warning; danger,
failure, fail, missing, bug and error error — `error` by a panel, 3 of 3, 2026-09-25; any failure, fail, missing, bug and error error — `error` by a panel, 3 of 3, 2026-09-25; any
other word info). Text after a marker in its paragraph is the panel's first body paragraph. other word info). Text after a marker on its line is the panel's first body paragraph, and
the lines after it open the body (10e).
- An `expand` or `nestedExpand` is Obsidian's folded callout, `> [!NOTE]- Title`, a blank `>`, - An `expand` or `nestedExpand` is Obsidian's folded callout, `> [!NOTE]- Title`, a blank `>`,
then the body. The lift reads a fold sign (`-` or `+`) as an expand whatever the word, the then the body. The lift reads a fold sign (`-` or `+`) as an expand whatever the word, the
rest of the marker's paragraph as its title, and an expand inside an expand as a rest of the marker's line as its title and the lines after it as the body (10e), and an
`nestedExpand`. expand inside an expand as a `nestedExpand`.
- A `taskList` is a bullet list whose items lead with `[x]` or `[ ]` (`- [x] Write the spec`). - A `taskList` is a bullet list whose items lead with `[x]` or `[ ]` (`- [x] Write the spec`).
The lift reads a list whose every item is so marked back as a `taskList` — a `blockTaskItem` The lift reads a list whose every item is so marked back as a `taskList` — a `blockTaskItem`
where an item holds more than one block, a nested task list moved beside its item — and where an item holds more than one block, a nested task list moved beside its item — and
@@ -266,14 +270,20 @@ chunk clearing a §11 seam.
it is a gap to ask. The same class runs the other way: a highlighted `=` writes `=====`, which it is a gap to ask. The same class runs the other way: a highlighted `=` writes `=====`, which
reads back as text, and a highlighted `a==b` writes `==a==b==`, highlighting `a` alone; 10c's reads back as text, and a highlighted `a==b` writes `==a==b==`, highlighting `a` alone; 10c's
byte-for-byte property misses both, since the wrong document re-spells to the same bytes. byte-for-byte property misses both, since the wrong document re-spells to the same bytes.
- [ ] **10e — A callout's body stays its body.** `plainMarkdownToAdf` reads Obsidian's own - [ ] **10e — A callout's title is its marker's line.** `> [!faq]- Why?` with the body on the
spelling, `> [!faq]- Why?` with the body on the next `>` line, as an expand titled with the next `>` line lifts to an expand titled `Why?` whose body keeps the next lines' link
whole paragraph — `"Why? See the docs and code."` — and an empty body, dropping the link targets and marks, and `> [!tip] Title` then `> body` to a panel whose paragraphs are
target and the code mark Goal 5 keeps; an unfolded `> [!tip] Title` merges the title into `Title` and `body`. The rest of the marker's line is the title (an expand) or the first
the body's first line. Item 10's settled "the rest of the marker's paragraph as its title" body paragraph (a panel), and the lines after it open the body. The line edge is gone once
reads against Goal 5 here, and the line edge is gone once `markdownToAdf` has joined the `markdownToAdf` has joined the paragraph, so `plainMarkdownToAdf` reads the marker line
paragraph, so which seam reads it is part of the gap to ask: candidate rule "the title is through the block parser — the one place the lift leaves ADF→ADF (the maintainer,
the marker's line; the lines after it open the body" (10c's review, 2026-09-26). 2026-09-26; 10c's review found the paragraph rule dropping link targets Goal 5 keeps).
- [ ] **10f — Lifted task nodes carry position ids.** `plainMarkdownToAdf` gives each `taskList`,
`taskItem` and `blockTaskItem` it lifts a deterministic `localId` from its position in
document order, so a site that rejects a missing `localId` takes the document and the same
markdown lifts to the same ids every run; README §Plain markdown's `localId` bullet says so
(the maintainer, 2026-09-26). The id spelling — unique within the document, no host API —
is part of the chunk.
- [x] **11 — Atlassian's ADF schema as the tables' truth.** - [x] **11 — Atlassian's ADF schema as the tables' truth.**
- [x] **11a — The vendored schema.** - [x] **11a — The vendored schema.**
- [x] **11b — The gate.** - [x] **11b — The gate.**