3
0
mirror of https://github.com/larvit/adf-codec.git synced 2026-11-12 11:48:29 +01:00

Drop README goal 10, which restates code health, and move its Moves cells to code health

This commit is contained in:
2026-10-21 10:43:35 +02:00
committed by Lillem4n
parent bed7146d33
commit b18bcbe129
3 changed files with 100 additions and 117 deletions
-4
View File
@@ -180,10 +180,6 @@ whatever it costs; the Achieve goals are weighed against each other in the order
7. **Maintain:** Lossy conversion drops form, never content. A link's or an image's title counts as content.
8. **Maintain:** Runs in any JavaScript engine, with no runtime dependencies and nothing to configure or connect.
9. **Maintain:** Fast, and linear in the document's size as JSON.
10. **Achieve:** Easy for a contributor to understand and change safely.
- Signal: new readers find the source easier to follow, and contributors read less before a change's first commit.
- Counter-signal: fewer features and fixes for the other goals ship per release.
- Why: readability bought by stalling the audience's work costs more than it saves.
## Audience
+1 -1
View File
@@ -596,7 +596,7 @@ any weights of its own. The script puts code health first, since every project p
above each achieve goal its README states. The README's Achieve goals follow in the order `## Goals`
gives them, and by position each weighs 0.97 times the one before. While the comprehension panel's
overall score is under 7.0, or `docs/comprehension-history.md` holds no scoring run, code health
weighs 4 times its position weight. README goal 10 restates code health until item 124 drops it.
weighs 4 times its position weight.
`todo.md`'s `## Scoring` keeps the formula and the bar, the lowest Score a non-exempt item may have.
Both are the project's to tune: the maintainer confirmed the bar at 0.1 on 2026-10-21.
+99 -112
View File
@@ -12,44 +12,43 @@
| ID | Release | Blocked by | Exempt | Item | L | E | I | R | S | Moves | Score |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 124 | 0.3.0 | | | **Drop goal 10, which restates code health, and point the Moves cells that cite it at `code health`.** | 1 | 2 | 1 | 2 | 2 | code health by 2 | 14.2 |
| 123 | 0.3.0 | | defect | **Spell a link title holding a backslash, an entity reference or, in a table cell, a pipe, instead of dropping it.** | 3 | 1 | 2 | 4 | 3 | 7 by 2 | 10.6 |
| 122 | 0.3.0 | | defect | **Keep an image's URL and title when a link's text holds the image, instead of dropping them.** | 3 | 1 | 2 | 4 | 5 | 7 by 4 | 8.5 |
| 33 | 0.3.0 | | defect | **Emit a line in time linear in its mark runs, in `adfToLosslessMarkdown` and `adfToPortableMarkdown`.** | 3 | 1 | 2 | 4 | 5 | 9 by 3 | 6.4 |
| 109 | 0.3.0 | | defect | **Spell nested lists and quotes in time linear in the markdown.** | 3 | 1 | 2 | 4 | 5 | 9 by 3 | 6.4 |
| 115 | 0.3.0 | | defect | **Build text holding no `border` mark in `losslessMarkdownToAdf`, and have `adfToLosslessMarkdown` carry text holding one.** | 2 | 1 | 2 | 3 | 3 | 4 by 3 | 3.7 |
| 60 | 0.3.0 | | decision | **Collect the questions `parse/` asks `emit/` into one named module.** | 2 | 1 | 2 | 3 | 4 | 10 by 4 | 3.1 |
| 92 | 0.3.0 | | | **Lower the reading cost of the files a chunk reads most before its first commit.** | 2 | 1 | 1 | 2 | 4 | 10 by 4 | 3.1 |
| 102 | 0.3.0 | | decision | **Place a block a portable task list refuses at its path in the returned document without replaying `splitTaskList` on stand-in nodes.** | 2 | 1 | 2 | 3 | 4 | 10 by 4 | 3.1 |
| 95 | 0.3.0 | | decision | **Find the code-span closers an escape forms, the mark runs a line cannot spell and the text `[` that opens link text by reading the line back, in place of `escapeClosedRuns`, `unspellableRuns` and `lastLinkClose` predicting them.** | 4 | 1 | 3 | 6 | 6 | 10 by 7 | 1.8 |
| 91 | 0.3.0 | 95 | | **Spell an emphasis once across the code spans, an emphasis or link once across the hard breaks, dates, emoji, inline cards, mentions and statuses between two nodes it marks, and an emphasis across inline media, in both writers.** | 3 | 2 | 2 | 5 | 5 | 6 by 6 | 2.9 |
| 59 | 0.3.0 | | decision | **Group the directive grammar into `src/markdown/directive/`, move `Read<T>` to `result.ts`, and move `Flavour` and `flavourClaims` to `markdown/flavour.ts`.** | 3 | 1 | 2 | 4 | 5 | 10 by 5 | 2.4 |
| 62 | 0.3.0 | | decision | **Move the portable flavour's reading out of `parse/` and its writing out of `emit/` into `markdown/portable/`, so `emit/` no longer imports `portable/`.** | 3 | 1 | 2 | 4 | 5 | 10 by 5 | 2.4 |
| 73 | 0.3.0 | 33, 59, 60, 62, 91, 92, 95, 102, 109, 115, 122, 123, 124 | | **Announce the package where someone needing an ADF converter already reads: JRACLOUD-77436, the Atlassian developer community and Stack Overflow's ADF-to-markdown questions.** | 1 | 2 | 2 | 3 | 2 | 3 by 8 | 13.7 |
| 45 | 0.4.0 | | defect | **Replace `isAdfDocument` with a reader returning `Result<AdfDocument>`.** | 2 | 1 | 3 | 4 | 3 | 1 by 2 | 10.6 |
| 68 | 0.4.0 | | defect | **Carry a non-text inline node holding `content` or `text`, a block holding `text`, and a leaf block holding `content`.** | 2 | 1 | 2 | 3 | 3 | 1 by 2 | 10.6 |
| 112 | 0.4.0 | | | **Pad a GFM pipe table's short rows with empty cells, read its body rows holding no pipe, and read a one-column table without leading pipes in `portableMarkdownToAdf`.** | 2 | 1 | 2 | 3 | 3 | 4 by 4 | 5.0 |
| 80 | 0.4.0 | | | **Name the node's path and the attribute key when `not-an-adf-document` refuses a node inside the document.** | 2 | 1 | 2 | 3 | 3 | 6 by 4 | 4.8 |
| 81 | 0.4.0 | | | **Fail the gate unless the README's error tables list exactly the codes `ConvertErrorCode` holds.** | 1 | 1 | 1 | 1 | 2 | 3 by 2 | 3.4 |
| 75 | 0.4.0 | | | **Make the generated-markdown property write block carries as `adf:` fences, canonical and hostile.** | 1 | 1 | 1 | 1 | 2 | 10 by 2 | 3.1 |
| 60 | 0.3.0 | | decision | **Collect the questions `parse/` asks `emit/` into one named module.** | 2 | 1 | 2 | 3 | 4 | code health by 4 | 17.4 |
| 92 | 0.3.0 | | | **Lower the reading cost of the files a chunk reads most before its first commit.** | 2 | 1 | 1 | 2 | 4 | code health by 4 | 17.4 |
| 102 | 0.3.0 | | decision | **Place a block a portable task list refuses at its path in the returned document without replaying `splitTaskList` on stand-in nodes.** | 2 | 1 | 2 | 3 | 4 | code health by 4 | 17.4 |
| 59 | 0.3.0 | | decision | **Group the directive grammar into `src/markdown/directive/`, move `Read<T>` to `result.ts`, and move `Flavour` and `flavourClaims` to `markdown/flavour.ts`.** | 3 | 1 | 2 | 4 | 5 | code health by 5 | 13.1 |
| 62 | 0.3.0 | | decision | **Move the portable flavour's reading out of `parse/` and its writing out of `emit/` into `markdown/portable/`, so `emit/` no longer imports `portable/`.** | 3 | 1 | 2 | 4 | 5 | code health by 5 | 13.1 |
| 123 | 0.3.0 | | defect | **Spell a link title holding a backslash, an entity reference or, in a table cell, a pipe, instead of dropping it.** | 3 | 1 | 2 | 4 | 3 | 7 by 2 | 13.1 |
| 122 | 0.3.0 | | defect | **Keep an image's URL and title when a link's text holds the image, instead of dropping them.** | 3 | 1 | 2 | 4 | 5 | 7 by 4 | 10.5 |
| 95 | 0.3.0 | | decision | **Find the code-span closers an escape forms, the mark runs a line cannot spell and the text `[` that opens link text by reading the line back, in place of `escapeClosedRuns`, `unspellableRuns` and `lastLinkClose` predicting them.** | 4 | 1 | 3 | 6 | 6 | code health by 7 | 10.2 |
| 33 | 0.3.0 | | defect | **Emit a line in time linear in its mark runs, in `adfToLosslessMarkdown` and `adfToPortableMarkdown`.** | 3 | 1 | 2 | 4 | 5 | 9 by 3 | 7.8 |
| 109 | 0.3.0 | | defect | **Spell nested lists and quotes in time linear in the markdown.** | 3 | 1 | 2 | 4 | 5 | 9 by 3 | 7.8 |
| 115 | 0.3.0 | | defect | **Build text holding no `border` mark in `losslessMarkdownToAdf`, and have `adfToLosslessMarkdown` carry text holding one.** | 2 | 1 | 2 | 3 | 3 | 4 by 3 | 4.6 |
| 91 | 0.3.0 | 95 | | **Spell an emphasis once across the code spans, an emphasis or link once across the hard breaks, dates, emoji, inline cards, mentions and statuses between two nodes it marks, and an emphasis across inline media, in both writers.** | 3 | 2 | 2 | 5 | 5 | 6 by 6 | 3.6 |
| 73 | 0.3.0 | 33, 59, 60, 62, 91, 92, 95, 102, 109, 115, 122, 123 | | **Announce the package where someone needing an ADF converter already reads: JRACLOUD-77436, the Atlassian developer community and Stack Overflow's ADF-to-markdown questions.** | 1 | 2 | 2 | 3 | 2 | 3 by 8 | 16.9 |
| 75 | 0.4.0 | | | **Make the generated-markdown property write block carries as `adf:` fences, canonical and hostile.** | 1 | 1 | 1 | 1 | 2 | code health by 2 | 17.4 |
| 45 | 0.4.0 | | defect | **Replace `isAdfDocument` with a reader returning `Result<AdfDocument>`.** | 2 | 1 | 3 | 4 | 3 | 1 by 2 | 13.1 |
| 68 | 0.4.0 | | defect | **Carry a non-text inline node holding `content` or `text`, a block holding `text`, and a leaf block holding `content`.** | 2 | 1 | 2 | 3 | 3 | 1 by 2 | 13.1 |
| 112 | 0.4.0 | | | **Pad a GFM pipe table's short rows with empty cells, read its body rows holding no pipe, and read a one-column table without leading pipes in `portableMarkdownToAdf`.** | 2 | 1 | 2 | 3 | 3 | 4 by 4 | 6.2 |
| 80 | 0.4.0 | | | **Name the node's path and the attribute key when `not-an-adf-document` refuses a node inside the document.** | 2 | 1 | 2 | 3 | 3 | 6 by 4 | 6.0 |
| 81 | 0.4.0 | | | **Fail the gate unless the README's error tables list exactly the codes `ConvertErrorCode` holds.** | 1 | 1 | 1 | 1 | 2 | 3 by 2 | 4.2 |
| 6 | 0.4.0 | | decision | **Specify the HTML dialect.** | 3 | 2 | 1 | 4 | 6 | | 0.0 |
| 7 | 0.4.0 | 6, 59, 60, 62 | decision | **Ship HTML: `adfToHtml`, `htmlToAdf`, and markdown to and from HTML composed through ADF, each call named by its flavour.** | 4 | 2 | 2 | 6 | 9 | 3 by 6 | 0.2 |
| 46 | 0.4.0 | 7 | | **Publish the bundle size in the README, failing the release pipeline when it drifts.** | 2 | 2 | 1 | 3 | 4 | 3 by 3 | 2.6 |
| 51 | 0.4.0 | | | **Match a reference label to its definition under Unicode case folding.** | 2 | 1 | 2 | 3 | 4 | 4 by 2 | 1.7 |
| 7 | 0.4.0 | 6, 59, 60, 62 | decision | **Ship HTML: `adfToHtml`, `htmlToAdf`, and markdown to and from HTML composed through ADF, each call named by its flavour.** | 4 | 2 | 2 | 6 | 9 | 3 by 6 | 0.3 |
| 46 | 0.4.0 | 7 | | **Publish the bundle size in the README, failing the release pipeline when it drifts.** | 2 | 2 | 1 | 3 | 4 | 3 by 3 | 3.2 |
| 51 | 0.4.0 | | | **Match a reference label to its definition under Unicode case folding.** | 2 | 1 | 2 | 3 | 4 | 4 by 2 | 2.1 |
| 77 | 0.4.0 | | decision | **Read the input once into a plain copy, so an accessor property or a throwing Proxy yields `not-an-adf-document` and every call returns a result.** | 3 | 1 | 3 | 5 | 4 | | 0.0 |
| 107 | 0.4.0 | | decision | **Spell the block carry's JSON so its length stays linear in the node's JSON.** | 3 | 1 | 3 | 5 | 5 | | 0.0 |
| 72 | 0.4.0 | 6, 7, 45, 46, 51, 68, 75, 77, 80, 81, 107, 112 | decision | **Re-create `gitea.larvit.se/larvit/adf-codec` as a read-only pull mirror of GitHub, its issues and Actions off and its description pointing at GitHub.** | 1 | 2 | 1 | 2 | 1 | 3 by 1 | 2.1 |
| 42 | 0.5.0 | | defect | **Trim a text leaf's trailing blanks in linear time.** | 1 | 1 | 2 | 2 | 2 | 9 by 3 | 21.2 |
| 38 | 0.5.0 | | defect | **Spell a lone surrogate in a text node so it survives a UTF-8 encode.** | 1 | 1 | 2 | 2 | 2 | 1 by 2 | 14.2 |
| 31 | 0.5.0 | | | **Make the branch-coverage figure repeat across runs of an unchanged tree.** | 2 | 1 | 1 | 2 | 3 | 10 by 3 | 3.5 |
| 57 | 0.5.0 | | | **Make each `ci.sh` leg build what it reads, so one leg run alone tests the current tree.** | 2 | 1 | 1 | 2 | 3 | 10 by 3 | 3.5 |
| 76 | 0.5.0 | | | **State the files the package does not ship once, so the build, the lint ceiling and the coverage exclusions cannot drift apart.** | 1 | 1 | 1 | 1 | 2 | 10 by 2 | 3.1 |
| 56 | 0.5.0 | | | **Give each piece of `blocks.ts`'s block-walk state and `inline-content.ts`'s `Scan` one owner that returns what it changes.** | 3 | 1 | 2 | 4 | 5 | 10 by 5 | 2.4 |
| 118 | 0.5.0 | | | **Spell a portable link's title through `oneLine`.** | 1 | 1 | 1 | 1 | 1 | 10 by 1 | 1.9 |
| 9 | 0.5.0 | | | **Ship an online sandbox: a web page with two textboxes converting between ADF and markdown on the library's browser build.** | 3 | 2 | 1 | 4 | 6 | 3 by 6 | 1.7 |
| 34 | 0.5.0 | | | **Read emphasis flanking by the whole character beside an astral symbol.** | 2 | 1 | 2 | 3 | 3 | 4 by 1 | 1.2 |
| 82 | 0.5.0 | | | **Give the directive tests and fixtures random UUID v7 ids in place of `a-1`, `a`, `x` and `h`.** | 2 | 1 | 1 | 2 | 3 | 10 by 1 | 1.2 |
| 58 | 0.5.0 | | | **Port `ci.sh`, `publish.sh` and `docker-runner.sh` to standalone Python scripts.** | 3 | 1 | 2 | 4 | 6 | 10 by 3 | 0.8 |
| 72 | 0.4.0 | 6, 7, 45, 46, 51, 68, 75, 77, 80, 81, 107, 112 | decision | **Re-create `gitea.larvit.se/larvit/adf-codec` as a read-only pull mirror of GitHub, its issues and Actions off and its description pointing at GitHub.** | 1 | 2 | 1 | 2 | 1 | 3 by 1 | 2.5 |
| 42 | 0.5.0 | | defect | **Trim a text leaf's trailing blanks in linear time.** | 1 | 1 | 2 | 2 | 2 | 9 by 3 | 26.2 |
| 31 | 0.5.0 | | | **Make the branch-coverage figure repeat across runs of an unchanged tree.** | 2 | 1 | 1 | 2 | 3 | code health by 3 | 19.6 |
| 57 | 0.5.0 | | | **Make each `ci.sh` leg build what it reads, so one leg run alone tests the current tree.** | 2 | 1 | 1 | 2 | 3 | code health by 3 | 19.6 |
| 38 | 0.5.0 | | defect | **Spell a lone surrogate in a text node so it survives a UTF-8 encode.** | 1 | 1 | 2 | 2 | 2 | 1 by 2 | 17.4 |
| 76 | 0.5.0 | | | **State the files the package does not ship once, so the build, the lint ceiling and the coverage exclusions cannot drift apart.** | 1 | 1 | 1 | 1 | 2 | code health by 2 | 17.4 |
| 56 | 0.5.0 | | | **Give each piece of `blocks.ts`'s block-walk state and `inline-content.ts`'s `Scan` one owner that returns what it changes.** | 3 | 1 | 2 | 4 | 5 | code health by 5 | 13.1 |
| 118 | 0.5.0 | | | **Spell a portable link's title through `oneLine`.** | 1 | 1 | 1 | 1 | 1 | code health by 1 | 10.5 |
| 82 | 0.5.0 | | | **Give the directive tests and fixtures random UUID v7 ids in place of `a-1`, `a`, `x` and `h`.** | 2 | 1 | 1 | 2 | 3 | code health by 1 | 6.5 |
| 58 | 0.5.0 | | | **Port `ci.sh`, `publish.sh` and `docker-runner.sh` to standalone Python scripts.** | 3 | 1 | 2 | 4 | 6 | code health by 3 | 4.4 |
| 9 | 0.5.0 | | | **Ship an online sandbox: a web page with two textboxes converting between ADF and markdown on the library's browser build.** | 3 | 2 | 1 | 4 | 6 | 3 by 6 | 2.1 |
| 34 | 0.5.0 | | | **Read emphasis flanking by the whole character beside an astral symbol.** | 2 | 1 | 2 | 3 | 3 | 4 by 1 | 1.5 |
| 74 | 0.5.0 | | owner | **Keep the release path publishing after npm retires bypass-2FA tokens.** | 3 | 2 | 4 | 7 | 4 | | 0.0 |
| 8 | 0.6.0 | | owner | **Ship a CLI.** | 3 | 2 | 1 | 4 | 7 | | 0.0 |
@@ -59,17 +58,44 @@
## Details
### 124. Drop goal 10, which restates code health, and point the Moves cells that cite it at `code health`.
### 60. Collect the questions `parse/` asks `emit/` into one named module.
`~/.claude/CLAUDE.md` → "Shared goal" now states code health, the goal every project shares, and
goal 10 restates it. In lilleman's words (2026-10-18): "every
project shares the goal, so it lives in `~/.claude` once ... It replaces each project's own
readability goal, such as fejkdata's goal 3, visthus's goal 8 and adf-codec's goal 10: once it
merges, file an item in each such project's `todo.md` to drop its own goal." Goal 10 is the last
goal, so no other goal renumbers. The chunk moves each `10 by N` in a Moves cell to
`code health by N`, 14 cells today, and revises any `docs/decisions.md` entry that cites goal 10;
none does today. Dropping goal 10 changes README.md's goals, so the chunk asks lilleman to approve
the drop before it merges, and is reviewed like any chunk.
`parse/` asks `emit/` through `commonMarkSpelling` and `openingLinkTakesDirective`, each imported
from where it happens to live. One module naming the questions keeps `docs/decisions.md` §The source
parts by ADF and format true as they grow.
### 92. Lower the reading cost of the files a chunk reads most before its first commit.
The last five chunks' median is 106412 new input tokens before their first commit, the five before
them 104037. Files read most over the last five chunks, counting opens and searches: `todo.md` 19,
`README.md` 18, `docs/decisions.md` 9, `CHANGELOG.md` 6, `MIGRATION.md` 6,
`src/conformance/commonmark-spec.test.ts` 6, `src/markdown/portable/inline-reduction.ts` 6,
`spec/flavour.md` 5, `src/markdown/portable/adf-to-portable-markdown.test.ts` 5,
`src/markdown/parse/inline-content.ts` 4.
### 102. Place a block a portable task list refuses at its path in the returned document without replaying `splitTaskList` on stand-in nodes.
`splitPath` (`src/markdown/parse/markdown-to-adf.ts`) builds stand-ins and `splitPlace`
(`src/markdown/portable/task-list.ts`) finds them again by object identity, right only while
`taskOfParagraphs` keeps the original child objects. Both paired-ruling seats on 2026-10-11 named it
the reason the project read worse.
### 59. Group the directive grammar into `src/markdown/directive/`, move `Read<T>` to `result.ts`, and move `Flavour` and `flavourClaims` to `markdown/flavour.ts`.
Eight directive files sit across three directories, and the `markdown/` root holds 14 entries. HTML
needs `Read<T>` and `Flavour` out of the portable flavour and `markdown/`; it needs the carry and
the mark spellings too, which item 7 moves where it learns what HTML shares.
Once moved, a reader takes a `Flavour` and looks up its claims, as a writer does, so no reader is
handed a set of claims no flavour holds.
### 62. Move the portable flavour's reading out of `parse/` and its writing out of `emit/` into `markdown/portable/`, so `emit/` no longer imports `portable/`.
Reading: the alert and task-marker reads in `parse/markdown-to-adf.ts` and the `mintTaskIds` call.
Writing: `spellPortableBlock`, `quotedUnder`, `tryTaskList` and `taskBlocks` in
`emit/adf-to-markdown.ts`. And `highlightDelimiter` and `highlightFlanking` move out of
`portable/conventions.ts`, which `emit/` imports them from; item 59 moves `Flavour` and
`flavourClaims`. Every comprehension reader on 2026-10-03 named the portable flavour's spread across
three directories.
### 123. Spell a link title holding a backslash, an entity reference or, in a table cell, a pipe, instead of dropping it.
@@ -93,6 +119,14 @@ the link marks, its guarantee that inside another link the title is dropped, and
`docs/decisions.md` §An image reads as an image only alone in its paragraph that says the drop
stands until this item ends it.
### 95. Find the code-span closers an escape forms, the mark runs a line cannot spell and the text `[` that opens link text by reading the line back, in place of `escapeClosedRuns`, `unspellableRuns` and `lastLinkClose` predicting them.
`escapeClosedRuns` works out which backtick strings an escape leaves, `unspellableRuns` rebuilds the
reader's emphasis and highlight pairing, and `lastLinkClose` guesses which text `[` opens link
text. Only the property tests catch drift between these and the reader, and it holds the project
under the comprehension floor. Reading the line back costs one parse each time a line is tried. The
run it carries may differ from today's, which changes lossless output (§The formats are API).
### 33. Emit a line in time linear in its mark runs, in `adfToLosslessMarkdown` and `adfToPortableMarkdown`.
`adfToLosslessMarkdown` spends 28 s on one paragraph of 2000 × `un` plus `**-r**`: each run its
@@ -118,36 +152,6 @@ on such a document. Found while shipping item 106, 2026-10-11.
(`src/adf/inline-nodes.ts`) restricts text only when it is marked `code`. It changes how stored
markdown reads, so it ships in a minor. Found while building item 114, 2026-10-14.
### 60. Collect the questions `parse/` asks `emit/` into one named module.
`parse/` asks `emit/` through `commonMarkSpelling` and `openingLinkTakesDirective`, each imported
from where it happens to live. One module naming the questions keeps `docs/decisions.md` §The source
parts by ADF and format true as they grow.
### 92. Lower the reading cost of the files a chunk reads most before its first commit.
The last five chunks' median is 106412 new input tokens before their first commit, the five before
them 104037. Files read most over the last five chunks, counting opens and searches: `todo.md` 19,
`README.md` 18, `docs/decisions.md` 9, `CHANGELOG.md` 6, `MIGRATION.md` 6,
`src/conformance/commonmark-spec.test.ts` 6, `src/markdown/portable/inline-reduction.ts` 6,
`spec/flavour.md` 5, `src/markdown/portable/adf-to-portable-markdown.test.ts` 5,
`src/markdown/parse/inline-content.ts` 4.
### 102. Place a block a portable task list refuses at its path in the returned document without replaying `splitTaskList` on stand-in nodes.
`splitPath` (`src/markdown/parse/markdown-to-adf.ts`) builds stand-ins and `splitPlace`
(`src/markdown/portable/task-list.ts`) finds them again by object identity, right only while
`taskOfParagraphs` keeps the original child objects. Both paired-ruling seats on 2026-10-11 named it
the reason the project read worse.
### 95. Find the code-span closers an escape forms, the mark runs a line cannot spell and the text `[` that opens link text by reading the line back, in place of `escapeClosedRuns`, `unspellableRuns` and `lastLinkClose` predicting them.
`escapeClosedRuns` works out which backtick strings an escape leaves, `unspellableRuns` rebuilds the
reader's emphasis and highlight pairing, and `lastLinkClose` guesses which text `[` opens link
text. Only the property tests catch drift between these and the reader, and it holds the project
under the comprehension floor. Reading the line back costs one parse each time a line is tried. The
run it carries may differ from today's, which changes lossless output (§The formats are API).
### 91. Spell an emphasis once across the code spans, an emphasis or link once across the hard breaks, dates, emoji, inline cards, mentions and statuses between two nodes it marks, and an emphasis across inline media, in both writers.
It needs item 95, which lets the joined spelling be tried and kept only where it reads back. No
@@ -159,23 +163,6 @@ mention or code spelled `!adf:text{text=" "}`. Joining the run across the break
`inline-line.ts` ruled worse with two comprehension panels (PR #22): a joined run that cannot be spelled needs its own fallback,
first as a set of parted breaks, then as a flag the first fallback clears.
### 59. Group the directive grammar into `src/markdown/directive/`, move `Read<T>` to `result.ts`, and move `Flavour` and `flavourClaims` to `markdown/flavour.ts`.
Eight directive files sit across three directories, and the `markdown/` root holds 14 entries. HTML
needs `Read<T>` and `Flavour` out of the portable flavour and `markdown/`; it needs the carry and
the mark spellings too, which item 7 moves where it learns what HTML shares.
Once moved, a reader takes a `Flavour` and looks up its claims, as a writer does, so no reader is
handed a set of claims no flavour holds.
### 62. Move the portable flavour's reading out of `parse/` and its writing out of `emit/` into `markdown/portable/`, so `emit/` no longer imports `portable/`.
Reading: the alert and task-marker reads in `parse/markdown-to-adf.ts` and the `mintTaskIds` call.
Writing: `spellPortableBlock`, `quotedUnder`, `tryTaskList` and `taskBlocks` in
`emit/adf-to-markdown.ts`. And `highlightDelimiter` and `highlightFlanking` move out of
`portable/conventions.ts`, which `emit/` imports them from; item 59 moves `Flavour` and
`flavourClaims`. Every comprehension reader on 2026-10-03 named the portable flavour's spread across
three directories.
### 73. Announce the package where someone needing an ADF converter already reads: JRACLOUD-77436, the Atlassian developer community and Stack Overflow's ADF-to-markdown questions.
Blocked by every other `0.3.0` item (the maintainer, 2026-10-14): the posts announce the finished
@@ -186,6 +173,14 @@ its versions and its date in step with the output. We draft each post for the pl
maintainer posts them once `0.3.0` is published (`docs/decisions.md` §Posts announcing a release
wait for its publish). Found while planning discoverability, 2026-10-04.
### 75. Make the generated-markdown property write block carries as `adf:` fences, canonical and hostile.
`blockCarry` and `hostileBlockCarry` (`src/conformance/markdown-property.test.ts`) write a fence
whose info string is `carry`. Under the `docs/decisions.md` entry "The carry fence names the node
type", that is an ordinary code language, so the property reaches `readCarriedBlock`'s refusals only
where a random edit lands in a canonical document. Write `adf:<type>` and `adf:` fences, canonical
and hostile. Technical principle: for pure logic over many inputs, prefer a property test, so the property must reach the fenced carry's refusals itself. Found by the technical-principles audit, 2026-10-04.
### 45. Replace `isAdfDocument` with a reader returning `Result<AdfDocument>`.
Lands with item 77: the reader returns item 77's copy. Goal 1 has every call return a result; the
@@ -224,14 +219,6 @@ document's whole tree. Give the node's path, and name its type and the attribute
leaves the README's tables a false contract under a green gate. Assert that the tables list exactly
the union. Found by the technical-principles audit of item 78, 2026-10-05.
### 75. Make the generated-markdown property write block carries as `adf:` fences, canonical and hostile.
`blockCarry` and `hostileBlockCarry` (`src/conformance/markdown-property.test.ts`) write a fence
whose info string is `carry`. Under the `docs/decisions.md` entry "The carry fence names the node
type", that is an ordinary code language, so the property reaches `readCarriedBlock`'s refusals only
where a random edit lands in a canonical document. Write `adf:<type>` and `adf:` fences, canonical
and hostile. Technical principle: for pure logic over many inputs, prefer a property test, so the property must reach the fenced carry's refusals itself. Found by the technical-principles audit, 2026-10-04.
### 6. Specify the HTML dialect.
Element-by-element mapping, the `data-*` fidelity scheme, the opaque-carry form, and the documented
@@ -307,11 +294,6 @@ interim sentence.
quadratic in a run of blanks inside one leaf: a paragraph of `a`, 80 000 spaces, `b` takes 6.5 s in
`adfToPortableMarkdown`. Scan backward, as the expand title's trim does.
### 38. Spell a lone surrogate in a text node so it survives a UTF-8 encode.
`adfToLosslessMarkdown` emits it verbatim, so markdown stored as UTF-8 reads back U+FFFD; attribute
values already escape it.
### 31. Make the branch-coverage figure repeat across runs of an unchanged tree.
Three Node test legs over one unchanged tree reported `emit/inline-line.ts` at 95.83%, 96.23% and
@@ -329,6 +311,11 @@ Technical principle "Compose, do not entangle": Deno, Bun and the tests read the
`node_modules`, the floor and consumer legs the pack leg's, and the browser leg whatever `dist/` the
build leg left, so a leg run alone can pass on stale output.
### 38. Spell a lone surrogate in a text node so it survives a UTF-8 encode.
`adfToLosslessMarkdown` emits it verbatim, so markdown stored as UTF-8 reads back U+FFFD; attribute
values already escape it.
### 76. State the files the package does not ship once, so the build, the lint ceiling and the coverage exclusions cannot drift apart.
Technical principle DRY: the set is written in `tsconfig.build.json`'s `exclude`, `.oxlintrc.json`'s
@@ -358,13 +345,6 @@ for a module of its own.
hand, so a change to what a portable line drops would miss link titles. Technical principle: DRY.
Found by the principles audit of item 99, 2026-10-14.
### 34. Read emphasis flanking by the whole character beside an astral symbol.
Check whether `delimiterRunToken` (`inline-tokens.ts`), `emittedRuns` and `resolveEmphasis`
(`line-escaping.ts`) read one UTF-16 unit beside an astral symbol. A lone surrogate is neither
punctuation nor symbol, but CommonMark reads `😀` as punctuation. Where they do, read the code
point, with a fixture per direction.
### 82. Give the directive tests and fixtures random UUID v7 ids in place of `a-1`, `a`, `x` and `h`.
Technical principle: tests use random UUID v7 ids; `portable-markdown-to-adf.test.ts` already does.
@@ -375,6 +355,13 @@ Found by the technical-principles audit of item 78, 2026-10-05.
Technical principle "Prefer a standalone Python script over a shell script": `AGENTS.md` §3 teaches
bash footguns (`&&` chaining under `||`, `tee /dev/stderr`) the port removes.
### 34. Read emphasis flanking by the whole character beside an astral symbol.
Check whether `delimiterRunToken` (`inline-tokens.ts`), `emittedRuns` and `resolveEmphasis`
(`line-escaping.ts`) read one UTF-16 unit beside an astral symbol. A lone surrogate is neither
punctuation nor symbol, but CommonMark reads `😀` as punctuation. Where they do, read the code
point, with a fixture per direction.
### 74. Keep the release path publishing after npm retires bypass-2FA tokens.
Lands after 2027-01-01, or after a release run fails on the token, whichever comes first: the