diff --git a/docs/decisions.md b/docs/decisions.md index 9632390..9fa33ed 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -294,8 +294,8 @@ nesting a spelling inside its own kind (`*(*a*)*`) names that mark once. ## The gate runs on Deno and Bun -2026-09-01, the maintainer. Goals 7 and 8. Valid while Deno is the only leg refusing an -extensionless specifier and Bun the only engine that is not V8. +2026-09-01, the maintainer. Goals 7 and 8. Valid while Bun is the gate's only engine that is +not V8. The gate runs the suite under Deno and Bun as well as Node, and neither extra leg is Node's proof twice. Deno refuses an extensionless or directory specifier, so it holds the module graph to the @@ -345,7 +345,7 @@ compared against `undefined` — have a half no valid document reaches. ## The size ratchet 2026-09-20, the maintainer. Goal 10. Valid while no measure picks out what readers find hard -(the comprehension panel, 2026-09-20). +better than a function's length. `.oxlintrc.json`'s single rule, over the files `tsconfig.build.json` builds, is a per-function line ceiling, set at that set's worst and moving only downward. It covers the built files alone, since @@ -371,10 +371,11 @@ markdown, on a fixed seed in the gate; a counterexample found becomes a round-tr 2026-09-01, the maintainer. Goal 1. Valid while `spec/flavour.md` restates the node tables in prose. -`spec/flavour.md` is read as a source, so the node tables cannot drift from the prose they copy: its -node and mark bullets must equal the tables in `adf/`. It guards the attributes alone: nodes that -differ in content model share a bullet, and the argument attribute is spelled ahead of `Attributes: -`, so both answer to the round-trip corpus and to nothing else where a node has no fixture. +`spec/flavour.md` is read as a source, so the node tables cannot drift from the prose they copy: +its node and mark bullets must equal the tables in `adf/`. It guards the attributes alone: nodes +that differ in content model share a bullet, and the argument attribute is spelled outside the +bullet's attribute list, so both answer to the round-trip corpus and to nothing else where a node +has no fixture. ## The node tables answer to Atlassian's schema @@ -433,12 +434,12 @@ two cannot disagree — which is what makes the kept value a memo rather than a 2026-09-19, the maintainer. Goal 9. Valid while the `commonMarkSpelling` ask spells a node once per level above it otherwise. -The parse keeps each node's readable spelling in a memo, so the `commonMarkSpelling` ask stops -spelling a node once per level above it. `text` and `spelling` carry no depth and `headroom` is -affine in it, so a read at or above the depth that filled the entry rebases; a read below re-spells, -because a hit skips the depth guards the walk it replaces runs and an ordered list past the marker -cap gives way, spending two emitter levels where the parser spent one. Only what succeeded is kept, -so no path minted at another position is ever read. +The parse and the plain reduction keep each node's readable spelling in a memo, so the +`commonMarkSpelling` ask stops spelling a node once per level above it. `text` and `spelling` carry +no depth and `headroom` is affine in it, so a read at or above the depth that filled the entry +rebases; a read below re-spells, because a hit skips the depth guards the walk it replaces runs and +an ordered list past the marker cap gives way, spending two emitter levels where the parser spent +one. Only what succeeded is kept, so no path minted at another position is ever read. ## Only the hard break holds a raw newline diff --git a/src/markdown/emit/adf-to-markdown.ts b/src/markdown/emit/adf-to-markdown.ts index 9808159..7077bd7 100644 --- a/src/markdown/emit/adf-to-markdown.ts +++ b/src/markdown/emit/adf-to-markdown.ts @@ -20,7 +20,7 @@ type BlockSpelling = 'commonmark' | 'directive' | 'list' type EmittedBlock = { headroom: number; spelling: BlockSpelling; text: string } type KeptSpelling = { block: EmittedBlock | undefined; depth: number } type PlacedBlock = Omit & { node: AdfNode } -// Keyed by reference: the parse builds one object per position; a consumer's document may share one, so adfToMarkdown passes none. +// Keyed by reference: only a caller building one object per position (the parse, the plain reduction) passes one; a consumer's document may share a node. export type SpellingMemo = Map type Walk = { blocks: readonly PlacedBlock[]; headroom: number } type WalkedItem = { node: AdfNode; walk: Walk } diff --git a/todo.md b/todo.md index 2426e22..38c4a36 100644 --- a/todo.md +++ b/todo.md @@ -73,6 +73,9 @@ so the first raise to the measured figure reddens a run that changed nothing. Make the measurement repeatable, or state the number the floor may be raised to and why it is not the measured one. +- **37 — State what the Deno leg proves that Node's does not, or drop it.** `docs/decisions.md` §The + gate runs on Deno and Bun credits Deno with holding the module graph to fully-spelled specifiers, + which Node already refuses under `"type": "module"`, and `tsc` under `NodeNext`. - **33 — Make a carried mark run cost the line one re-emit.** `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 (Goal 9), and the plain reduction's