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
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
`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.
## 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
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
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
the emitted formats are.
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
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
(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] **5b — The consumer's error surface.**
- [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
refuses (`not-an-adf-document`, `unsupported-document-version`, `unsupported-nesting-depth`)
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.
- 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
@@ -206,11 +209,12 @@ chunk clearing a §11 seam.
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,
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 `>`,
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
`nestedExpand`.
rest of the marker's line as its title and the lines after it as the body (10e), and an
expand inside an expand as a `nestedExpand`.
- 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`
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
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.
- [ ] **10e — A callout's body stays its body.** `plainMarkdownToAdf` reads Obsidian's own
spelling, `> [!faq]- Why?` with the body on the next `>` line, as an expand titled with the
whole paragraph — `"Why? See the docs and code."` — and an empty body, dropping the link
target and the code mark Goal 5 keeps; an unfolded `> [!tip] Title` merges the title into
the body's first line. Item 10's settled "the rest of the marker's paragraph as its title"
reads against Goal 5 here, and the line edge is gone once `markdownToAdf` has joined the
paragraph, so which seam reads it is part of the gap to ask: candidate rule "the title is
the marker's line; the lines after it open the body" (10c's review, 2026-09-26).
- [ ] **10e — A callout's title is its marker's line.** `> [!faq]- Why?` with the body on the
next `>` line lifts to an expand titled `Why?` whose body keeps the next lines' link
targets and marks, and `> [!tip] Title` then `> body` to a panel whose paragraphs are
`Title` and `body`. The rest of the marker's line is the title (an expand) or the first
body paragraph (a panel), and the lines after it open the body. The line edge is gone once
`markdownToAdf` has joined the paragraph, so `plainMarkdownToAdf` reads the marker line
through the block parser — the one place the lift leaves ADF→ADF (the maintainer,
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] **11a — The vendored schema.**
- [x] **11b — The gate.**