diff --git a/AGENTS.md b/AGENTS.md index 175f93c..66467bc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -130,8 +130,8 @@ One-line commit messages and PR titles; short PR summaries. No AI-attribution ma ## 7. The working loop -A session works one chunk, starting from the first item under the earliest release in `todo.md`, -and stops when that chunk merges, whatever it was asked to finish: a release is a chain of +A session works one chunk, starting from the first item in `todo.md` whose prerequisites have +landed, and stops when that chunk merges, whatever it was asked to finish: a release is a chain of sessions, so an instruction to work until a release is done names the chain, not the session. An open PR is a chunk already in flight, and finishing it is the session. Per chunk: diff --git a/README.md b/README.md index bfcb695..13cac24 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,8 @@ an HTML dialect. **Status: published — the markdown round-trip (`adfToMarkdown`, `markdownToAdf`); HTML at `0.2.0`.** -Plan: `todo.md`. Decisions: +Plan: +[`todo.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/todo.md). Decisions: [`docs/decisions.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/docs/decisions.md). Changes: [`CHANGELOG.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/CHANGELOG.md). The lossless flavour's grammar: [`spec/flavour.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/spec/flavour.md). diff --git a/docs/decisions.md b/docs/decisions.md index a4fdce8..b7f65ed 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -39,7 +39,7 @@ merges. "Equals" is structural equality over editor-normal ADF — adjacent text nodes with identical marks and no attributes merged, JSON number semantics, an empty attrs object, marks array or content array the absent key — the only domain markdown can restore. -Replaced by deep equality with `todo.md` item 40 (2026-09-28, the maintainer). +`todo.md` item 40 replaces this with deep equality (2026-09-28, the maintainer). ## Unknown nodes ride the carry diff --git a/todo.md b/todo.md index e2ba3a6..f088d56 100644 --- a/todo.md +++ b/todo.md @@ -24,7 +24,7 @@ | ID | Release | Exempt | Item | R | S | A | G | Goals | Score | |---|---|---|---|---|---|---|---|---|---| -| 40 | 0.2.0 | decision | **Make `markdownToAdf(adfToMarkdown(doc))` deep-equal `doc` for every document it takes.** | 6 | 7 | 8 | 9 | 1 | 26.2 | +| 40 | 0.2.0 | decision | **Make `markdownToAdf(adfToMarkdown(doc))` deep-equal `doc` for every document `adfToMarkdown` takes.** | 6 | 7 | 8 | 9 | 1 | 26.2 | | 7 | 0.2.0 | | **Ship HTML: `adfToHtml`, `htmlToAdf`, and `markdownToHtml` / `htmlToMarkdown` composed through ADF.** | 6 | 9 | 9 | 9 | 2, 3 | 25.8 | | 45 | 0.2.0 | | **Replace `isAdfDocument` with a reader returning `Result`.** | 2 | 3 | 6 | 8 | 1 | 25.2 | | 6 | 0.2.0 | decision | **Specify the HTML dialect.** | 2 | 6 | 7 | 8 | 2, 3 | 24.7 | @@ -34,25 +34,25 @@ | 34 | 0.3.0 | | **Read emphasis flanking by the whole character beside an astral symbol.** | 2 | 3 | 3 | 6 | 3, 4 | 12.6 | | 42 | 0.3.0 | | **Trim a text leaf's trailing blanks in linear time.** | 1 | 2 | 5 | 9 | 8 | 12.5 | | 48 | 0.3.0 | question | **Keep the release path publishing past npm's bypass-2FA token retirement.** | 4 | 4 | 8 | 3 | 9 | 11.7 | -| 31 | 0.3.0 | | **Make the branch figure the coverage floor is read against repeatable.** | 2 | 3 | 3 | 4 | 1 | 11.2 | -| 33 | 0.3.0 | | **Make a carried mark run cost the line one re-emit.** | 4 | 5 | 6 | 9 | 8 | 10.7 | +| 31 | 0.3.0 | | **Make the branch-coverage figure repeat across runs of an unchanged tree.** | 2 | 3 | 3 | 4 | 1 | 11.2 | +| 33 | 0.3.0 | | **Emit a line in time linear in its mark runs, in `adfToMarkdown` and the plain reduction.** | 4 | 5 | 6 | 9 | 8 | 10.7 | | 46 | 0.3.0 | | **Publish the bundle size in the README, failing the release pipeline when it drifts.** | 2 | 4 | 5 | 5 | 7, 9 | 10.3 | | 9 | 0.3.0 | | **Ship an online sandbox: a web page with two textboxes converting between ADF and markdown on the library's browser build.** | 2 | 6 | 6 | 8 | 9 | 10.3 | | 8 | 0.4.0 | question | **Ship a CLI.** | 4 | 7 | 5 | 5 | 9 | 5.3 | ## Details -### 40. Make `markdownToAdf(adfToMarkdown(doc))` deep-equal `doc` for every document it takes. +### 40. Make `markdownToAdf(adfToMarkdown(doc))` deep-equal `doc` for every document `adfToMarkdown` takes. Today it holds for editor-normal documents only: two adjacent text nodes with the same marks merge, an empty `attrs`, `marks` or `content` drops, and `-0` reads back `0` — shapes pipelines and bots -build. Spell each so it reads back as written, CommonMark's spelling kept wherever none occurs; the -spellings are part of the chunk. `docs/decisions.md` §Equality is editor-normal, `spec/flavour.md` +build. Spell each so it reads back as written; CommonMark's spelling stays wherever the document +holds none of these shapes. The spellings are part of the chunk. `docs/decisions.md` §Equality is editor-normal, `spec/flavour.md` and `corpus/README.md` follow, and the tests drop `toEditorNormal`. ### 7. Ship HTML: `adfToHtml`, `htmlToAdf`, and `markdownToHtml` / `htmlToMarkdown` composed through ADF. -Builds on item 6. The CommonMark spec suite runs against `markdownToHtml` from here. The README +Builds on item 6. Once it lands, the CommonMark spec suite runs against `markdownToHtml`. The README documents HTML as it documents markdown, and its tagline and `package.json`'s `description` regain HTML. @@ -83,14 +83,11 @@ already escape it. ### 47. Open the README with what the package is, what it does and for whom. It opens with the pre-launch rationale — Atlassian's 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 runnable example. 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 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 exported sits near the bottom. The README names the lossy pair and the flavours it -writes and reads — GitHub Flavored Markdown's alerts and task lists, Obsidian Flavored Markdown's -callouts — so a search for either finds the package. +decommissioned, a link to JRACLOUD-77436. The background goes entirely, no endpoint, ticket or +"why" note left. The badges are npm's version and the Gitea Actions status. The README names the +lossy pair, `adfToPlainMarkdown` and `plainMarkdownToAdf`, and the flavours it writes and reads — +GitHub Flavored Markdown's alerts and task lists, Obsidian Flavored Markdown's callouts — so a +search for either finds the package. ### 34. Read emphasis flanking by the whole character beside an astral symbol. @@ -111,13 +108,13 @@ writes was not enough, and npm answered `EOTP` until the token itself bypassed. bypass-2FA tokens for direct publishing around January 2027, leaving them `npm stage publish`, which a maintainer approves with 2FA; its replacement — trusted publishing over OIDC — supports GitHub-hosted Actions, GitLab.com's shared runners and CircleCI's cloud, self-hosted runners planned -without a date. So the release path has an expiry date and no drop-in successor yet. Revisit: +without a date. 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 maintainer's trade to weigh -rather than discover on a red release run. It stays out of `0.2.0` knowing the cutoff may land +staged publish — which fits badly with publish-on-merge, and is the maintainer's trade to weigh. +The Goals and G cells are provisional: no README goal covers the release path. It stays out of `0.2.0` knowing the cutoff may land first. -### 31. Make the branch figure the coverage floor is read against repeatable. +### 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 96.23%, and the total at 98.80%, 98.84% and 98.84% (2026-09-21). `--experimental-test-coverage` @@ -128,7 +125,7 @@ moves upward, so the first raise to the measured figure reddens a run that chang measurement repeatable, or state the number the floor may be raised to and why it is not the measured one. -### 33. Make a carried mark run cost the line one re-emit. +### 33. Emit a line in time linear in its mark runs, in `adfToMarkdown` and the plain reduction. `adfToMarkdown` spends 23 s on one paragraph of 2000 × `un` plus `**-r**`: each run its flanking cannot spell re-emits the whole line before riding the carry, quadratic in the runs, and the plain @@ -138,7 +135,7 @@ reduction's `spellableLine` drops one mark per re-emit the same way. Make both l Lands after item 7, which changes it. The quantity is what a consumer downloads and loads: the tarball `npm pack` produces, its unpacked `dist`, and the built JavaScript minified + gzipped — the -figure the competitors advertise (marklassian's "12kb") and the only apple-to-apple one, since ours +figure the competitors advertise (marklassian's "12kb") and the only apples-to-apples one, since ours ships tsc's unminified output and no minifier yet (decide here whether to minify for the build or report the unminified gzip). The figure lands in README §The package beside the "no runtime dependencies" claim. Measured today, unminified: tarball 60.4 kB, unpacked 221.5 kB, JS gzipped @@ -146,5 +143,6 @@ dependencies" claim. Measured today, unminified: tarball 60.4 kB, unpacked 221.5 ### 8. Ship a CLI. -No goal or persona in the README covers it yet; the maintainer names both before it is built, and -they land in the README's `## Goals` and `## Audience` with it. +The Goals and G cells are provisional: no goal or persona in the README covers a CLI yet. The +maintainer names both before it is built, and they land in the README's `## Goals` and `## Audience` +with it.