From 48e5f045d1efe3e56c11a60815708c839d62a1b2 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Thu, 22 Oct 2026 00:06:35 +0200 Subject: [PATCH] =?UTF-8?q?Answer=20prose=20round=201:=200=20=F0=9F=94=B4,?= =?UTF-8?q?=205=20=F0=9F=9F=A1;=20state=20the=20directive=20boundary,=20sp?= =?UTF-8?q?lit=20the=20spelling-questions=20sentence=20and=20tighten=20the?= =?UTF-8?q?=20plan=20items?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/decisions.md | 12 ++++++------ src/result.ts | 2 +- todo.md | 37 +++++++++++++++++++------------------ 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/docs/decisions.md b/docs/decisions.md index c32363c..912c0a6 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -924,15 +924,15 @@ them. A primitive knowing neither ADF nor a format stays at `src/` root. A const what both read where there are two. A flavour's own code, both directions included, sits in its own directory: `markdown/portable/`; -`todo.md` item 62 moves what still sits in `parse/` and `emit/`. The directive grammar likewise -sits in `markdown/directive/`. Otherwise each format directory parts into `emit/` (ADF→format) and +`todo.md` item 62 moves what still sits in `parse/` and `emit/`. The directive grammar, both +directions included, sits in `markdown/directive/`, which imports neither `emit/` nor `parse/`. +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 import `parse/` takes from `emit/`, -through `emit/spelling-questions.ts`, which names `commonMarkSpelling`, `openingLinkTakesDirective` -and the `SpellingMemo` type — 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. +consults it rather than restating it, 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. That consulting +is the only import `parse/` takes from `emit/`, through `emit/spelling-questions.ts`. diff --git a/src/result.ts b/src/result.ts index 457671e..3ac0c20 100644 --- a/src/result.ts +++ b/src/result.ts @@ -25,7 +25,7 @@ export type ParseError = ConvertError & { position: SourcePosition } export type Result = { error: E; ok: false } | { ok: true; value: T } -// A reader with no path to name; the walk attaches the path where it knows it. +// A fault without a path, for a reader that cannot know it; its caller adds the path. export type Read = { fault: ConvertFault; value?: undefined } | { fault?: undefined; value: T } export function failure(code: ConvertErrorCode, message: string, path: ConvertErrorPath): Result { diff --git a/todo.md b/todo.md index 13c843e..72e8102 100644 --- a/todo.md +++ b/todo.md @@ -28,7 +28,7 @@ | 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`.** | 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 | -| 127 | 0.4.0 | | principle | **Hold what `result.test.ts`, `json-text.test.ts` and `entity-references.test.ts` check by asserting outcomes, so none reads source text.** | 2 | 2 | 1 | 3 | 3 | code health by 2 | 13.1 | +| 127 | 0.4.0 | | principle | **Hold what `entity-references.test.ts`, `json-text.test.ts` and `result.test.ts` check by asserting outcomes, so none reads source text.** | 2 | 2 | 1 | 3 | 3 | code health 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 | @@ -63,13 +63,14 @@ Two boundaries in `docs/decisions.md` §The source parts by ADF and format hold by convention only: `src/markdown/parse/` imports from `src/markdown/emit/` only `emit/spelling-questions.ts`, and -`src/markdown/directive/` imports neither `emit/` nor `parse/`. A test scanning the source is -refused, since `~/.claude/principles/technical-principles.md` says a test never scans source. -oxlint 1.89.0 can hold both with `overrides` carrying `no-restricted-imports`. Verified: a -`from '…'`, a bare `import '…'`, a double-quoted and a dynamic `import()` each fail the -`size-ratchet` leg, and the exception `!../emit/spelling-questions.ts` passes. +`src/markdown/directive/` imports neither `emit/` nor `parse/`. The technical principles refuse a +test that scans the source: principle "Assert the outcome" bans it. oxlint 1.89.0 can hold both +boundaries with `overrides` carrying `no-restricted-imports`. Verified: a forbidden import fails +the `size-ratchet` leg in each form, as `from '…'`, as a bare `import '…'`, with a double-quoted +specifier and as a dynamic `import()`. An import of `../emit/spelling-questions.ts`, listed as the +exception, passes. -But §The size ratchet, the maintainer's, gives `.oxlintrc.json` a single rule and says no second +But the maintainer's §The size ratchet gives `.oxlintrc.json` a single rule and says no second lint rule joins it. The question for the maintainer: may the config hold a second kind of rule, the import boundaries? If so, amend that entry's "single rule" and "no second lint rule", and rename the `size-ratchet` script and `ci.sh` leg, whose name would no longer say what they hold. @@ -205,23 +206,23 @@ round-trips them; `docs/decisions.md` §The code list gives a cause the carry an them from the README's `unsupported-node-shape` cause "Writing, in `adfToLosslessMarkdown`". Found by the README-goals audit, 2026-10-04. -### 127. Hold what `result.test.ts`, `json-text.test.ts` and `entity-references.test.ts` check by asserting outcomes, so none reads source text. +### 127. Hold what `entity-references.test.ts`, `json-text.test.ts` and `result.test.ts` check by asserting outcomes, so none reads source text. Technical principle "Assert the outcome", which bans scanning the source with `ast` or a regex. Three tests read source text: +- `src/markdown/commonmark/entity-references.test.ts` regex-extracts the `packedReferences` literal + from `entity-references.ts`, to guard that each of the 2125 packed references parts into one name + and one value the table decodes. Reformatting the literal fails it with the table unchanged. +- `src/json-text.test.ts` greps every shipped file for `JSON.parse(`, to guard that only + `parseJsonText` reads JSON text. `JSON.parse (` or `JSON['parse'](` escapes it. - `src/result.test.ts` regex-parses the `ConvertErrorCode` union out of `result.ts`, and each `failure('…'` and `code: '…'` out of every shipped file, to guard that every declared code has a production cause and every call site names a declared code. A code held in a variable or passed through another helper escapes it. -- `src/json-text.test.ts` greps every shipped file for `JSON.parse(`, to guard that only - `parseJsonText` reads JSON text. `JSON.parse (` or `JSON['parse'](` escapes it. -- `src/markdown/commonmark/entity-references.test.ts` regex-extracts the `packedReferences` literal - from `entity-references.ts`, to guard that each of the 2125 packed references parts into one name - and one value the table decodes. Reformatting the literal fails it with the table unchanged. -Item 81 also touches `result.test.ts`; a lint rule for the `JSON.parse` ban meets item 126's -question about what `.oxlintrc.json` may hold. Found by the technical-principles audit of item 60, +Item 81 changes `result.test.ts` too. Whether `.oxlintrc.json` may hold a lint rule for the +`JSON.parse` ban is item 126's question. Found by the technical-principles audit of item 60, 2026-10-22. ### 112. 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`. @@ -261,9 +262,9 @@ documents markdown, its comparison table names HTML, and its tagline and `packag description and keywords name HTML again. `htmlToAdf` refuses a value that is not a string as `not-a-string` (`docs/decisions.md` §Which code a cause takes). -`html/` may not import `markdown/`. Whatever it shares with the carry (`opaque-carry.ts`) or the -mark spellings (`mark-spellings.ts`) moves out of `markdown/` as `docs/decisions.md` §The source -parts by ADF and format says. A composed call naming a markdown `Flavour` sits above both peers. +Whatever `html/` shares with the carry (`opaque-carry.ts`) or the mark spellings +(`mark-spellings.ts`) splits: the ADF question moves to `adf/`, and each format keeps its own +spelling. Where a composed call naming a markdown `Flavour` sits is this item's to settle. ### 46. Publish the bundle size in the README, failing the release pipeline when it drifts.