36d - todo.md's settled text and §11 and §15's dated rules move to docs/decisions.md #140

Merged
lilleman merged 2 commits from 36d into main 2026-09-28 18:58:30 +02:00
5 changed files with 73 additions and 85 deletions
Showing only changes of commit 2701e165c4 - Show all commits
+27 -30
View File
@@ -13,13 +13,14 @@ In `docs/decisions.md`:
- Markdown in is a canonical fixpoint - Markdown in is a canonical fixpoint
- Equality is editor-normal - Equality is editor-normal
- Unknown nodes ride the carry - Unknown nodes ride the carry
- Foreign HTML is refused by name - Foreign HTML sorts three ways
- Names stay text - Names stay text
- Directives under `!adf:` - Directives under `!adf:`
- CommonMark is a subset - CommonMark is a subset
- Tables - Tables
- Links - Links
- Ids stay site-local - Ids stay site-local
- Plain task ids come from position
- The HTML dialect - The HTML dialect
- No runtime dependencies - No runtime dependencies
- Standards ship as data - Standards ship as data
@@ -54,12 +55,12 @@ In `docs/decisions.md`:
- The attribute vocabulary is ADF's - The attribute vocabulary is ADF's
- The source parts by ADF and format - The source parts by ADF and format
## 7. Nothing about any consumer ## 1. Nothing about any consumer
No Jira client, no HTTP, no REST shapes, no issue keys, no actual consumer named anywhere. Design No Jira client, no HTTP, no REST shapes, no issue keys, no actual consumer named anywhere. Design
against the README's personas. against the README's personas.
## 9. Release automation ## 2. Release automation
- The bump commit renames `CHANGELOG.md`'s `## Unreleased` to the version. - The bump commit renames `CHANGELOG.md`'s `## Unreleased` to the version.
- Exact versions: `save-exact=true` in `.npmrc`. - Exact versions: `save-exact=true` in `.npmrc`.
@@ -68,10 +69,10 @@ against the README's personas.
specific as the publisher tags: `oven/bun:1.4.0-alpine` pins Bun's patch and leaves the base specific as the publisher tags: `oven/bun:1.4.0-alpine` pins Bun's patch and leaves the base
floating because Bun publishes nothing narrower. Actions pin semver tags. floating because Bun publishes nothing narrower. Actions pin semver tags.
## 10. Tests first, in Docker ## 3. Tests first, in Docker
Test for the behaviour wanted first, then implement until green. `node --test`, beside the code. Test for the behaviour wanted first, then implement until green. `node --test`, beside the code.
Node, tsc and npm never run on the host — only via the pinned images (§9). Tests are independent, Node, tsc and npm never run on the host — only via the pinned images (§2). Tests are independent,
containers are torn down after a run. A test reaches only for what Node's, Deno's and Bun's `node:` containers are torn down after a run. A test reaches only for what Node's, Deno's and Bun's `node:`
shims all carry. shims all carry.
@@ -91,12 +92,11 @@ Each `- ` bullet in `spec/flavour.md`'s `## Block nodes`, `## Inline nodes` and
the nodes named before its first em dash, with the attributes following `Attributes: ` — a the nodes named before its first em dash, with the attributes following `Attributes: ` — a
parenthesized value set reading `string`; fenced examples are skipped. Keep prose out of a bullet. parenthesized value set reading `string`; fenced examples are skipped. Keep prose out of a bullet.
## 11. Code rules ## 4. Code rules
- Two-space indent, English everywhere. Alphabetical order wherever order - Two-space indent, English everywhere. Alphabetical order wherever order carries no meaning,
carries no meaning, keyed on the name a line introduces rather than where it came from: an keyed on the name a line introduces: an import sorts on its first binding, type imports ahead of
import sorts on its first binding, type imports ahead of value imports, so moving or renaming a value imports, so moving or renaming a module reorders nothing.
module reorders nothing (the maintainer, 2026-09-18).
- No casts: `as`, `as unknown as`, non-null `!`. A boundary owes a type guard validating the - No casts: `as`, `as unknown as`, non-null `!`. A boundary owes a type guard validating the
fields it claims (`isAdfDocument`); past it everything is typed. Make invalid states fields it claims (`isAdfDocument`); past it everything is typed. Make invalid states
unrepresentable. unrepresentable.
@@ -107,28 +107,25 @@ parenthesized value set reading `string`; fenced examples are skipped. Keep pros
a second consumer, or it goes. a second consumer, or it goes.
- Explicit over implicit; descriptive names; no catch-all files (`utils`, `helpers`, `misc`); a file - Explicit over implicit; descriptive names; no catch-all files (`utils`, `helpers`, `misc`); a file
does not repeat its directory in its name — `adf/document.ts`, never `adf/adf-document.ts`. A name does not repeat its directory in its name — `adf/document.ts`, never `adf/adf-document.ts`. A name
is the noun `spec/flavour.md` or ADF's schema uses for the thing; a directory follows a split the is the noun `spec/flavour.md` or ADF's schema uses for the thing.
spec draws; a placement neither this section nor `docs/decisions.md` §The source parts by ADF and
format settles goes beside its only reader, or in what both read where there are two (the
maintainer, 2026-09-18).
## 12. Prose to a minimum ## 5. Prose to a minimum
Applies everywhere: comments, every markdown file in this repo (this one included), PR text. Applies everywhere: comments, every markdown file in this repo (this one included), PR text.
- Default is no comment. One earns its single line only by naming an invariant, footgun or - Default is no comment. One earns its single line only by naming an invariant, footgun or
external constraint the code cannot show — never restatement, history, absence or arrangement. external constraint the code cannot show — never restatement, history, absence or arrangement.
A second line belongs in the commit message or a decision entry here. A second line belongs in the commit message or a `docs/decisions.md` entry.
- A doc paragraph says what the repo cannot say for itself, or it goes. The fix for a redundant - A doc paragraph says what the repo cannot say for itself, or it goes. The fix for a redundant
one is deletion, not trimming. A false claim in any doc is a bug, fixed where found. one is deletion, not trimming. A false claim in any doc is a bug, fixed where found.
- Published text — npm README, error messages, API docs — never references internal systems, - Published text — npm README, error messages, API docs — never references internal systems,
tickets or repos. tickets or repos.
## 13. Commits and PRs ## 6. Commits and PRs
One-line commit messages and PR titles; short PR summaries. No AI-attribution markers, ever. One-line commit messages and PR titles; short PR summaries. No AI-attribution markers, ever.
## 15. The working loop ## 7. The working loop
`todo.md` lists what is left under the release that ships it, in shipping order. One item per `todo.md` lists what is left under the release that ships it, in shipping order. One item per
session — the first under the earliest release — in the smallest PR-able chunk; split a big item session — the first under the earliest release — in the smallest PR-able chunk; split a big item
@@ -139,13 +136,13 @@ release is done names the chain, not the session. An open PR is a chunk already
finishing it is the session. finishing it is the session.
Per chunk: Per chunk:
1. Fresh worktree off updated `origin/main`; implement tests-first (§10). 1. Fresh worktree off updated `origin/main`; implement tests-first (§3).
2. Run the larv-review flow until it passes and CI is green. A reviewer launch states the latest 2. Run the larv-review flow until it passes and CI is green. A reviewer launch states the latest
gate result (commit and outcome); a reviewer does not re-run `ci.sh` or the tests when a gate result (commit and outcome); a reviewer does not re-run `ci.sh` or the tests when a
result exists for the commit under review, or when the diff since that result cannot affect result exists for the commit under review, or when the diff since that result cannot affect
it (docs-only) — re-run only what its own findings or fixes invalidate. it (docs-only) — re-run only what its own findings or fixes invalidate.
3. Merge the PR (standing authorization, this repo only, granted through the `0.2.0` release — 3. Merge the PR (standing authorization, this repo only, granted by the maintainer through the
the maintainer, 2026-09-13), delete the item from `todo.md` — what a consumer sees of it is `0.2.0` release), delete the item from `todo.md` — what a consumer sees of it is
reworded for them into `CHANGELOG.md`'s `## Unreleased` — report, stop. reworded for them into `CHANGELOG.md`'s `## Unreleased` — report, stop.
Reserved for the maintainer whatever any rule here says: changing `version` in `package.json` (a Reserved for the maintainer whatever any rule here says: changing `version` in `package.json` (a
@@ -157,22 +154,22 @@ maintainer's) and the `NPM_TOKEN` secret.
Any choice where what the maintainer would pick is not near-certain gets asked. The confidence bar Any choice where what the maintainer would pick is not near-certain gets asked. The confidence bar
is very high — asking too often is the accepted cost, guessing wrong is not. is very high — asking too often is the accepted cost, guessing wrong is not.
An ask is a gap in this file, and its answer is the rule that closes it, landing here — never the An ask is a gap in `docs/decisions.md`, and its answer is the entry that closes it, landing there —
instance alone. Before asking, name the class the question belongs to and the earlier never the instance alone; an answer that is a goal lands in the README, one that is a working rule
`(the maintainer, …)` entries of that class; where a rule already decides it, apply it without here. Before asking, name the class the question belongs to and the entries of that class; where
asking, and where the rule reads two ways on this input, that reading is the ask. Never ask "A or one already decides it, apply it without asking, and where it reads two ways on this input, that
B?": state the gap, the earlier asks of its class, the nearest text here, a candidate rule in this reading is the ask. Never ask "A or B?": state the gap, the earlier entries of its class, the
file's voice and section, and the instance it yields. A rule that keeps collecting instances is nearest text, a candidate entry in that file's voice, and the instance it yields. An entry that
wrong: rewrite it. keeps collecting instances is wrong: rewrite it.
Which output the audience expects — README goal 5 — is settled by a reader panel rather than Which output the audience expects — README goal 5 — is settled by a reader panel rather than
asked: three fresh-context readers, one per README persona the conversion serves, each given only asked: three fresh-context readers, one per README persona the conversion serves, each given only
`## Audience` and the input, writing what they expect before picking among outputs the goals `## Audience` and the input, writing what they expect before picking among outputs the goals
allow, rendered, shuffled, with no rationale and nothing saying what is implemented. Three agreeing allow, rendered, shuffled, with no rationale and nothing saying what is implemented. Three agreeing
settle it; otherwise four more read, five of seven settle it, and less is a missing goal, asked. settle it; otherwise four more read, five of seven settle it, and less is a missing goal, asked.
The verdict lands in the item it settles (the maintainer, 2026-09-25). The verdict lands in `docs/decisions.md`.
### Rules the loop has settled (the maintainer, 2026-09-18) ### Findings, numbers and empty releases
- 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 `docs/decisions.md` in a release, weighed against every item on that release by the personas and `docs/decisions.md`
+1 -1
View File
@@ -61,7 +61,7 @@ In priority order.
## Audience ## Audience
Application developers embedding the library, addressed as personas rather than named consumers Application developers embedding the library, addressed as personas rather than named consumers
(AGENTS.md §7). All four rely on the guarantees below and on `code` being a closed list; none may (AGENTS.md §1). All four rely on the guarantees below and on `code` being a closed list; none may
rely on an error message's wording, which is free text. rely on an error message's wording, which is free text.
- **Viewer/editor app** — shows a document, lets a human edit, posts it back. Relies on the - **Viewer/editor app** — shows a document, lets a human edit, posts it back. Relies on the
+32 -7
View File
@@ -49,12 +49,25 @@ checking its position, and refusing loses a document ADF itself keeps in an `uns
Where a container's own spelling cannot hold the child it has — a `bulletList` outside `listItem`, Where a container's own spelling cannot hold the child it has — a `bulletList` outside `listItem`,
a `codeBlock` outside text — the error result names that instead. a `codeBlock` outside text — the error result names that instead.
## Foreign HTML is refused by name ## Foreign HTML sorts three ways
2026-08-23, the maintainer. Goals 1 and 6. Valid until the HTML dialect's element set lands 2026-08-23, the sort 2026-09-20, the maintainer. Goals 1 and 6. Valid while ADF holds no node for a
(`todo.md`, 6). bare container, a comment or a script.
An unmappable foreign HTML element is an error result naming the element — never a silent drop. Every foreign element `htmlToAdf` and `markdownToAdf` read sorts one of three ways, never a silent
drop of what a reader saw:
- A container around document content that ADF has no node for unwraps to its children, its own
attributes dropped: `<div align="center">text</div>` keeps `text`.
- Content ADF cannot hold is an error result naming it. A comment is one: a person wrote those
words, and neither of Atlassian's schemas holds them — `annotation`'s `inlineComment` carries an
id, `placeholder` is the editor's own hint, `extension` names a vendor app.
- What is not document content drops whole: `<script>` and `<style>`, their text with them.
`<details><summary>Title</summary>…</details>` is an `expand` titled by its summary, a `nestedExpand`
inside another; an empty one is refused, since `expand` requires content. A `style` attribute is not
read at `0.2.0`: the `textColor` and `backgroundColor` it could reach cost more than they buy.
`plainMarkdownToAdf` reads through `markdownToAdf`'s parser, so it takes the same set.
## Names stay text ## Names stay text
@@ -107,6 +120,16 @@ where its reference implementation nests one `<a>` in another.
Identity-bearing nodes carry their ids in attributes; a document is only portable within its site — Identity-bearing nodes carry their ids in attributes; a document is only portable within its site —
accepted. accepted.
## Plain task ids come from position
2026-09-26, the maintainer. Goals 5 and 7. Valid while a site rejects a task node with no
`localId`.
`plainMarkdownToAdf` gives each `taskList`, `taskItem` and `blockTaskItem` a `localId` from its
position in document order, unique within the document and minted with no host API, so a site
rejecting a missing `localId` takes the document and the same markdown reads to the same ids every
run.
## The HTML dialect ## The HTML dialect
2026-08-23, the maintainer. Goals 4 and 7. Valid while HTML output is read by consumers styling it 2026-08-23, the maintainer. Goals 4 and 7. Valid while HTML output is read by consumers styling it
@@ -301,7 +324,7 @@ The gate runs the suite under Deno and Bun as well as Node. Bun runs JavaScriptC
of the three that is not V8, where the Unicode property escapes emphasis matching leans on can of the three that is not V8, where the Unicode property escapes emphasis matching leans on can
disagree. Deno shares Node's V8 and stays to prove the library runs there too, catching what the disagree. Deno shares Node's V8 and stays to prove the library runs there too, catching what the
two runtimes leave undocumented. Both refuse a run matching no test, so Node's is the only two runtimes leave undocumented. Both refuse a run matching no test, so Node's is the only
vacuous-green guard, and `AGENTS.md` §10's `node:` shims rule is the price of proving those engines vacuous-green guard, and `AGENTS.md` §3's `node:` shims rule is the price of proving those engines
over the corpus rather than over a smoke import. over the corpus rather than over a smoke import.
## The gate installs the tarball ## The gate installs the tarball
@@ -482,7 +505,7 @@ is the markdown flavour's choice, not ADF's.
## The source parts by ADF and format ## The source parts by ADF and format
2026-08-27, `adf/`'s bar 2026-09-21, the maintainer. Goal 2. Valid while each format has a reader 2026-08-27, placement 2026-09-18, `adf/`'s bar 2026-09-21, the maintainer. Goal 2. Valid while each format has a reader
and a writer through ADF. and a writer through ADF.
`src/adf/` holds ADF's own knowledge, imports no format, and is where a construct both formats read `src/adf/` holds ADF's own knowledge, imports no format, and is where a construct both formats read
@@ -492,7 +515,9 @@ is two constructs, the ADF question there and the spelling in each format, the s
`markAttributes` and `markSpellings` already draw; one that cannot be split is a gap to ask. `markAttributes` and `markSpellings` already draw; one that cannot be split is a gap to ask.
`markdown/` and `html/` are peers: neither imports the other, and no third directory sits between `markdown/` and `html/` are peers: neither imports the other, and no third directory sits between
them. A primitive knowing neither ADF nor a format stays at `src/` root. A construct rises to them. A primitive knowing neither ADF nor a format stays at `src/` root. A construct rises to
`adf/` on its second consumer, not in anticipation of one. `adf/` on its second consumer, not in anticipation of one. A directory follows a split
`spec/flavour.md` draws, and a placement nothing here settles goes beside its only reader, or in
what both read where there are two.
Each format directory parts into `emit/` (ADF→format) and `parse/` (format→ADF), the rest of it Each format directory parts into `emit/` (ADF→format) and `parse/` (format→ADF), the rest of it
holding what both directions read. A construct's reader lives there beside the regex the emitter holding what both directions read. A construct's reader lives there beside the regex the emitter
+1 -1
View File
@@ -178,7 +178,7 @@ In block-directive position `!adf:carry` is a named error — the carry's block
## Raw HTML in input ## Raw HTML in input
CommonMark input may contain raw HTML. `markdownToAdf` routes each construct through the foreign CommonMark input may contain raw HTML. `markdownToAdf` routes each construct through the foreign
HTML element mapping (`docs/decisions.md` §Foreign HTML is refused by name; specified with the HTML HTML element mapping (`docs/decisions.md` §Foreign HTML sorts three ways; specified with the HTML
dialect, todo.md milestone 6) — ADF has no raw-HTML node, so a construct without a mapping, comments dialect, todo.md milestone 6) — ADF has no raw-HTML node, so a construct without a mapping, comments
and processing instructions included, is an error result naming it. The flavour never emits raw and processing instructions included, is an error result naming it. The flavour never emits raw
HTML. HTML.
+12 -46
View File
@@ -7,15 +7,10 @@
one no goal serves is proposed as a goal and asked. Sources: `AGENTS.md`'s body, the settled text one no goal serves is proposed as a goal and asked. Sources: `AGENTS.md`'s body, the settled text
in this file's items, and `todo-history.md`, deleted with the bare `(28)` citations into it once in this file's items, and `todo-history.md`, deleted with the bare `(28)` citations into it once
nothing cites it. Split by `AGENTS.md` section where one chunk is too big. nothing cites it. Split by `AGENTS.md` section where one chunk is too big.
- **36d — Move the settled text in `todo.md`'s items and §11 and §15's dated rules, and point
§15's "the rule that closes it, landing here" at `docs/decisions.md`.** `AGENTS.md`'s sections
are renumbered once only working rules remain, their citations with them.
- **36e — Move `todo-history.md`'s decisions, re-point its citations and delete it.** - **36e — Move `todo-history.md`'s decisions, re-point its citations and delete it.**
- **35 — Read and write plain markdown as a flavour of the markdown grammar.** Per Goal 2 and - **35 — Read and write plain markdown as a flavour of the markdown grammar.** Per Goal 2 and
`docs/decisions.md` §Plain markdown is a flavour of the grammar, `plainMarkdownToAdf` is `docs/decisions.md` §Plain markdown is a flavour of the grammar, 10's rows are read by
`markdownToAdf`'s parser and `adfToPlainMarkdown` `adfToMarkdown`'s writer, each with the plain `markdownToAdf`'s parser and written by `adfToMarkdown`'s writer, and the lift goes. The exports, their refusals and 10's rows stay as they are.
flavour set; 10's rows are read and written there, and the lift goes (the maintainer, 2026-09-27).
The exports, their refusals and 10's rows stay as they are.
- **35a — Read the plain flavour in the parser and delete the lift.** 10's rows are read while - **35a — Read the plain flavour in the parser and delete the lift.** 10's rows are read while
parsing, and `plain-lift.ts` is deleted, its tests reading through `plainMarkdownToAdf`. `> parsing, and `plain-lift.ts` is deleted, its tests reading through `plainMarkdownToAdf`. `>
[!faq]- Why?` with the body on the next `>` line reads to an expand titled `Why?` whose body [!faq]- Why?` with the body on the next `>` line reads to an expand titled `Why?` whose body
@@ -30,37 +25,13 @@
(today `==a==b==`, highlighting `a` alone) come back highlighted whole, or lose the highlight (today `==a==b==`, highlighting `a` alone) come back highlighted whole, or lose the highlight
where no spelling holds them; 10c's byte-for-byte property misses both, since the wrong document where no spelling holds them; 10c's byte-for-byte property misses both, since the wrong document
re-spells to the same bytes. The reduction keeps only degrading what the flavour cannot spell. re-spells to the same bytes. The reduction keeps only degrading what the flavour cannot spell.
- **10f — Give task nodes read from plain markdown position ids.** `plainMarkdownToAdf` gives each - **10f — Give task nodes read from plain markdown position ids.** Per `docs/decisions.md` §Plain
`taskList`, `taskItem` and `blockTaskItem` a deterministic `localId` from its position in document task ids come from position, README §Plain markdown's `localId` bullet saying so. The id spelling
order, so a site that rejects a missing `localId` takes the document and the same markdown reads is part of the chunk.
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.
- **6 — Specify the HTML dialect.** Element-by-element mapping, the `data-*` fidelity scheme, the - **6 — Specify the HTML dialect.** Element-by-element mapping, the `data-*` fidelity scheme, the
opaque-carry form, and the documented foreign-element set `htmlToAdf` accepts — the set opaque-carry form, and the documented foreign-element set `htmlToAdf` accepts — the set
`markdownToAdf` shares (`spec/flavour.md` §Raw HTML in input; 29). `markdownToAdf` shares (`spec/flavour.md` §Raw HTML in input; 29).
**Settled** (the maintainer, 2026-09-20), the four answers that shape the set: The set sorts per `docs/decisions.md` §Foreign HTML sorts three ways.
- A container ADF has no node for unwraps to its children, its own attributes dropped, so `<div
align="center">text</div>` keeps `text` and loses the box and the alignment ADF cannot hold.
- `<details><summary>Title</summary>…</details>` is an `expand`, the summary its `title`; one
inside another is a `nestedExpand`, as 10 already spells for the lossy pair. An empty
`<details>` is still refused — `expand` requires content, so there is nothing to build.
- A comment stays an error result. Neither schema holds a comment node: across 84 and 98
definitions the only "comment" in either file is `annotationType: "inlineComment"` on the
`annotation` mark, which carries an `id` and no text, the words living behind an Atlassian API.
`placeholder` is the editor's own visible hint, and `extension` demands an `extensionKey` naming
a vendor app. Nothing can hold the words, so nothing accepts them.
- `<script>` and `<style>` drop whole, their text with them. Neither holds anything a reader of
the document ever saw, so nothing is lost; unwrapping them would put `alert(1)` on the page as
prose. A `style` attribute is a separate question — `textColor` and `backgroundColor` are the
marks it could reach — and is not read at `0.2.0`, the work outweighing what it buys.
So the set sorts every element three ways, and that is what `docs/decisions.md` §Foreign HTML is
refused by name becomes in place of "error result naming the element": a container around document
content unwraps, content ADF cannot hold is an error result naming it, and what is not document
content at all drops whole. A comment sorts into the second rather than the third because a person
wrote those words on purpose. 10's "Rejected in the survey" line names raw HTML and comments and
does not contradict this: it rejects them as spellings the lossy pair writes and reads back, where
`plainMarkdownToAdf` reads through `markdownToAdf`'s parser and so inherits whatever this set
accepts.
- **7 — Ship HTML: `adfToHtml`, `htmlToAdf`, and `markdownToHtml` / `htmlToMarkdown` composed - **7 — Ship HTML: `adfToHtml`, `htmlToAdf`, and `markdownToHtml` / `htmlToMarkdown` composed
through ADF.** CommonMark spec suite runs against `markdownToHtml` from here. The README's through ADF.** CommonMark spec suite runs against `markdownToHtml` from here. The README's
tagline and `package.json`'s `description` regain HTML (5g). tagline and `package.json`'s `description` regain HTML (5g).
@@ -93,17 +64,14 @@
- **5g — Reweight the README for the reader.** It opens with the pre-launch rationale — Atlassian's - **5g — Reweight the README for the reader.** It opens with the pre-launch rationale — Atlassian's
REST APIs, `pf-editor-service/convert` being decommissioned, a link to JRACLOUD-77436 — where a REST APIs, `pf-editor-service/convert` being decommissioned, a link to JRACLOUD-77436 — where a
shipped package should answer what it is, what it does and for whom first, then the shortest shipped package should answer what it is, what it does and for whom first, then the shortest
runnable example. runnable example. The background goes entirely, no endpoint, ticket or
**Settled** (the maintainer, 2026-09-13): the background goes entirely, no endpoint, ticket or
"why" note left. The top follows the package-README order: an npm version badge and the Gitea "why" note left. The top follows the package-README order: an npm version badge and the Gitea
Actions badge, a tagline that is also `package.json`'s `description`, a feature list and a Actions badge, a tagline that is also `package.json`'s `description`, a feature list and a
one-line table of contents, then install and the shortest runnable example; a table of everything one-line table of contents, then install and the shortest runnable example; a table of everything
exported sits near the bottom. The HTML directions were to stay an aside until a later release exported sits near the bottom. The README documents HTML as it documents markdown, the tagline and
shipped them; 7 now ships in this one and reads ahead of this item, so the README documents HTML `description` naming both, the lossy pair, and the flavours it writes and reads by name — GitHub
as it documents markdown, the tagline and `description` naming both (the maintainer, 2026-09-13, Flavored Markdown's alerts and task lists, Obsidian Flavored Markdown's callouts — so a search for
revised 2026-09-18). They name the lossy pair too, and the flavours it writes and reads by name — either finds the package.
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).
## 0.3.0 ## 0.3.0
@@ -116,9 +84,7 @@
planned without a date. So the release path has an expiry date and no drop-in successor yet. planned without a date. So the release path has an expiry date and no drop-in successor yet.
Revisit: whether npm has added Gitea or self-hosted OIDC, and otherwise whether the release moves Revisit: whether npm has added Gitea or self-hosted OIDC, and otherwise whether the release moves
to the staged publish — which fits badly with publish-on-merge, and is the trade to weigh rather to the staged publish — which fits badly with publish-on-merge, and is the trade to weigh rather
than discover on a red release run. than discover on a red release run. It stays out of `0.2.0` knowing the cutoff may land first.
**Settled** (the maintainer, 2026-09-13, placed in `0.3.0` 2026-09-27): clear of `0.2.0`, knowing
the cutoff may land before `0.2.0` ships.
- **8 — Ship a CLI, shaped around the personas.** - **8 — Ship a CLI, shaped around the personas.**
- **9 — Ship an online sandbox: a web page with two textboxes converting between ADF and markdown on - **9 — Ship an online sandbox: a web page with two textboxes converting between ADF and markdown on
the library's browser build.** the library's browser build.**