diff --git a/AGENTS.md b/AGENTS.md index 85e0c87..614fcc0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,7 +48,7 @@ In `docs/decisions.md`: - Firefox reads the build - The coverage floors - The size ratchet -- The comprehension floor ships under 7 until items 59, 60, 61 and 62 land +- The project ships under the comprehension floor until items 59, 60, 61 and 62 land - Properties on a fixed seed - The CommonMark suite checks three ways - The flavour spec is read as a source diff --git a/README.md b/README.md index 37874cf..e0e48f1 100644 --- a/README.md +++ b/README.md @@ -198,7 +198,7 @@ emit refuses: | `unspellable-line-start` | a paragraph line begins with a code span whose backticks would read back as a code fence | put any text before the code span | | `unspellable-whitespace` | an `emoji`, `mention` or `status` holds a newline in the text its inline directive spells in the content slot | replace it with a space — an inline directive never spans lines | | `unsupported-nesting-depth` | blocks, marks, an attribute's JSON or a carried node's JSON nest past 500 levels | keep the ADF and pass the document over, or show it read-only; flatten the input where you are the one who wrote it | -| `unsupported-node-shape` | a node carries an attribute, value, argument or body its type does not take, or lacks one it needs — or markdown writes as a directive a node or mark the lossless flavour spells as CommonMark, or a reserved directive stands out of place: `!adf:textBreak{}` or `!adf:listBreak` parting nothing, `!adf:doc` beside another block | fix what the message names, or give the node the shape it names; `spec/flavour.md` lists every type's attributes and body | +| `unsupported-node-shape` | a node carries an attribute, value, argument or body its type does not take, or lacks one it needs — or markdown writes as a directive a node or mark the lossless flavour spells as CommonMark, or a reserved directive stands out of place: `!adf:textBreak{}` or `!adf:listBreak` parting nothing, `!adf:doc` anywhere but as the whole document | fix what the message names; `spec/flavour.md` lists every type's attributes and body | ## The guarantees diff --git a/docs/decisions.md b/docs/decisions.md index a28dd4b..5feca6d 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -496,14 +496,13 @@ config gone missing fails the leg instead of falling back to oxlint's own defaul `--deny-warnings`, since a rule from a category this config never names arrives as a warning it exits 0 on. -## The comprehension floor ships under 7 until items 59, 60, 61 and 62 land +## The project ships under the comprehension floor until items 59, 60, 61 and 62 land 2026-10-03, the maintainer. KISS, a technical principle, and its comprehension floor of 7. Valid while `todo.md` items 59, 60, 61 and 62 are open. -A four-seat comprehension panel scored the project 6 against the floor of 7, and the project ships -under it while the restructures it named land: `todo.md` items 59, 60, 61 and 62 end this. The -scores are the baseline — a later panel may not score lower on any dimension: +A four-seat comprehension panel scored the project 6 against the floor of 7. The scores are the +baseline: a later panel may not score lower on any dimension. | Seat | Navigation | Locality | Shape | Self-sufficiency | Overall | |---|---|---|---|---|---| @@ -672,16 +671,14 @@ them. A primitive knowing neither ADF nor a format stays at `src/` root. A const `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 -holding what both directions read. A construct's reader lives there beside the regex the emitter -escapes against, so the two cannot drift; a reader with no emit counterpart goes in `parse/`, -unless it is part of a construct that side already holds — a grammar stays in one file rather than -splitting across the seam. A rule both directions must answer alike — whether a list marker -interrupts a paragraph — is one function there too, never a copy per direction, however -conservative the copy would be. Where the rule is the emitter's own choice, input consults it -rather than restating it, and that is the only import `parse/` takes from `emit/` — +A flavour's own code, both directions included, sits in its own directory: `markdown/plain/`. +Otherwise 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 escapes against, so the two cannot drift; a reader with no emit counterpart goes in +`parse/`, unless it is part of a construct that side already holds — a grammar stays in one file +rather than splitting across the seam. A rule both directions must answer alike — whether a list +marker interrupts a paragraph — is one function there too, never a copy per direction, however +conservative the copy would be. Where the rule is the emitter's own choice, input consults it rather +than restating it, and that is the only value import `parse/` takes from `emit/` — `commonMarkSpelling` and `openingLinkTakesDirective` — so no fixture the emitter writes can be refused, and a spelling the emitter refuses gives its own error rather than a second name for it. -`markdown/plain/` holds the plain flavour's own code — the writer's reduction to editor-normal ADF -and the conventions and task ids the reader takes — so neither direction reaches into the other -for it. diff --git a/src/markdown/adjacent-text.ts b/src/markdown/adjacent-text.ts index b063caa..9b1547e 100644 --- a/src/markdown/adjacent-text.ts +++ b/src/markdown/adjacent-text.ts @@ -14,7 +14,7 @@ export function joinsWhenRead(previous: AdfNode, node: AdfNode): boolean { return isPlainText(previous) && isPlainText(node) && identicalMarks(nodeMarks(previous), nodeMarks(node)) } -// The editor's rule, parting from the reader's only on shapes editor-normal ADF erases: an empty attrs, content or marks, and -0 in a mark. +// The editor's rule, differing from the reader's only on shapes editor-normal ADF erases: an empty attrs, content or marks, and -0 in a mark. export function joinsWhenEditorNormal(previous: AdfNode, node: AdfNode): boolean { if (!joinsAsEditorText(previous) || !joinsAsEditorText(node)) return false return identicalMarks(nodeMarks(previous).map(normalMark), nodeMarks(node).map(normalMark)) diff --git a/todo.md b/todo.md index 0215807..7842034 100644 --- a/todo.md +++ b/todo.md @@ -34,8 +34,8 @@ | 52 | 0.2.0 | | **Spell `colwidth` as a comma list, `colwidth="340,420"`.** | 3 | 3 | 5 | 6 | 5 | 13.0 | | 51 | 0.2.0 | | **Match a reference label to its definition under Unicode case folding.** | 2 | 2 | 2 | 7 | 3, 4 | 12.4 | | 53 | 0.2.0 | | **Put a block's `marks` spelling to the writer panel and adopt its pick.** | 4 | 5 | 5 | 6 | 5 | 11.5 | -| 60 | 0.2.0 | decision | **Give the parser's questions to the emitter one named module, so the parse→emit dependency reads as intended.** | 3 | 4 | 2 | 6 | 2 | 10.7 | -| 59 | 0.2.0 | decision | **Group the directive grammar into `src/markdown/directive/`, and move `Read` to `result.ts` and `Flavour` out of the plain flavour.** | 3 | 5 | 2 | 6 | 2 | 10.4 | +| 60 | 0.2.0 | decision | **Collect the questions `parse/` asks `emit/` into one named module.** | 3 | 4 | 2 | 6 | 2 | 10.7 | +| 59 | 0.2.0 | decision | **Group the directive grammar into `src/markdown/directive/`, move `Read` to `result.ts`, and move `Flavour` to `markdown/flavour.ts`.** | 3 | 5 | 2 | 6 | 2 | 10.4 | | 50 | 0.2.0 | | **Read `[](/url)` and `[]()` as CommonMark's empty link.** | 4 | 4 | 3 | 6 | 3, 4, 6 | 10.4 | | 62 | 0.2.0 | decision | **Move the plain reader out of `parse/markdown-to-adf.ts` into `markdown/plain/`.** | 3 | 4 | 2 | 5 | 2 | 8.9 | | 38 | 0.3.0 | | **Spell a lone surrogate in a text node so it survives a UTF-8 encode.** | 2 | 2 | 4 | 7 | 1 | 19.5 | @@ -106,7 +106,7 @@ spellings" bullet, which counts one fewer. The comprehension panel's worst place: `escapeClaims` and `escapeClosedRuns` re-implement the reader's view — flanking, code-span closers, link-definition openings, highlight flanking — and -only the property tests catch drift. It caps Locality. +only the property tests catch drift. It caps the panel's Locality score. ### 52. Spell `colwidth` as a comma list, `colwidth="340,420"`. @@ -126,16 +126,17 @@ Today `marks="[{\"attrs\":{\"mode\":\"wide\"},\"type\":\"breakout\"}]"`, the mar escaped JSON. Breaking where the panel picks another spelling: `MIGRATION.md`'s Spellings table gains its row. -### 60. Give the parser's questions to the emitter one named module, so the parse→emit dependency reads as intended. +### 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. -### 59. Group the directive grammar into `src/markdown/directive/`, and move `Read` to `result.ts` and `Flavour` out of the plain flavour. +### 59. Group the directive grammar into `src/markdown/directive/`, move `Read` to `result.ts`, and move `Flavour` to `markdown/flavour.ts`. Eight directive files sit across three directories, and the `markdown/` root holds 14 entries. HTML -needs `Read`, `Flavour`, the carry and the mark spellings out of `markdown/`. +needs `Read` and `Flavour` out of the plain flavour and `markdown/`; it needs the carry and the +mark spellings too, which item 7 moves where it learns what HTML shares. ### 50. Read `[](/url)` and `[]()` as CommonMark's empty link. @@ -148,7 +149,8 @@ which counts one fewer. ### 62. Move the plain reader out of `parse/markdown-to-adf.ts` into `markdown/plain/`. - +What moves: the plain reader's code in `markdown-to-adf.ts` — the alert and task-marker reads, the +`mintTaskIds` call and the `inlineLeaves` use. ### 38. Spell a lone surrogate in a text node so it survives a UTF-8 encode. @@ -172,8 +174,8 @@ punctuation — and, where they do, read the code point, with a fixture per dire ### 42. Trim a text leaf's trailing blanks in linear time. -`plain/inline-reduction.ts`'s `leafEdges` finds the trail with an unanchored `/[ \t]*$/`, quadratic in a run -of blanks inside one leaf: a paragraph of `a`, 80 000 spaces, `b` takes 6.5 s in +`plain/inline-reduction.ts`'s `leafEdges` finds the trail with an unanchored `/[ \t]*$/`, quadratic +in a run of blanks inside one leaf: a paragraph of `a`, 80 000 spaces, `b` takes 6.5 s in `adfToPlainMarkdown`. Scan backward, as the expand title's trim does. ### 48. Keep the release path publishing past npm's bypass-2FA token retirement.