From 6b33a2e29913bd98d9ec80ac19b12016800ef6ae Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Mon, 9 Nov 2026 00:33:24 +0100 Subject: [PATCH] Write the property's block carries as adf: fences, canonical and hostile --- docs/reading-cost.md | 17 +++++++++++ src/conformance/markdown-property.test.ts | 35 +++++++++++++++++++---- todo.md | 9 ------ 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/docs/reading-cost.md b/docs/reading-cost.md index 8565018..62e2203 100644 --- a/docs/reading-cost.md +++ b/docs/reading-cost.md @@ -758,3 +758,20 @@ Read most (opens and searches): - `CHANGELOG.md` 1 - `README.md` 1 - `mutation-tests/run.sh` 1 + +## Reading cost, chunk 1b0dd5a87659, finished 2026-11-08T23:23:58Z + +196044 new input tokens before the first commit. + +Read most (opens and searches): + +- `todo.md` 12 +- `README.md` 5 +- `src/markdown/portable/portable-markdown-to-adf.test.ts` 5 +- `docs/decisions.md` 4 +- `spec/flavour.md` 4 +- `docker-runner.sh` 3 +- `src/markdown/portable/adf-to-portable-markdown.test.ts` 3 +- `src/markdown/portable/portable-markdown-to-adf.ts` 2 +- `AGENTS.md` 1 +- `ci.sh` 1 diff --git a/src/conformance/markdown-property.test.ts b/src/conformance/markdown-property.test.ts index 16ba048..00137c7 100644 --- a/src/conformance/markdown-property.test.ts +++ b/src/conformance/markdown-property.test.ts @@ -12,7 +12,7 @@ import { adfToLosslessMarkdown } from '../markdown/emit/adf-to-markdown.ts' import { adfToPortableMarkdown } from '../markdown/portable/adf-to-portable-markdown.ts' import { blockArgument, marksAttribute } from '../markdown/directive/block.ts' import { blockNodes } from '../adf/block-nodes.ts' -import { carryName } from '../markdown/opaque-carry.ts' +import { carryFencePrefix, carryName } from '../markdown/opaque-carry.ts' import { commonMarkToAdf, losslessMarkdownToAdf } from '../markdown/parse/markdown-to-adf.ts' import { directivePrefix, @@ -168,15 +168,37 @@ function container(opener: string, body: string, closer: string): string { return [opener, body, closer].filter((line) => line !== '').join('\n') } -const carriedNode = fc.oneof( - fc.tuple(fc.constantFrom('mention', 'paragraph', 'status', 'widget'), fc.dictionary(jsonKey, jsonValue, { maxKeys: 2, noNullPrototype: true })).map(([type, attrs]): JsonValue => ({ attrs, type })), - textOf(1).map((held): JsonValue => ({ text: held, type: 'text' })), +type CarriedParts = readonly [type: string, untyped: { [key: string]: JsonValue }] +const carriedAttributes = fc.dictionary(jsonKey, jsonValue, { maxKeys: 2, noNullPrototype: true }) +const carriedParts = fc.oneof( + fc.tuple(fc.constantFrom('mention', 'paragraph', 'status', 'widget'), carriedAttributes).map(([type, attrs]): CarriedParts => [type, { attrs }]), + textOf(1).map((held): CarriedParts => ['text', { text: held }]), ) +const unnamedParts = fc.tuple(fc.constantFrom(' x', 'a`b', 'a\\b', 'a&b'), carriedAttributes).map(([type, attrs]): CarriedParts => [type, { attrs }]) +const carriedNode = carriedParts.map(([type, untyped]): JsonValue => ({ ...untyped, type })) const spellCarry = (json: string) => spellInlineLeafDirective(carryName, `{json=${spellStringAttribute(json)}}`) const inlineCarry = carriedNode.map((node) => spellCarry(serializeCanonicalJson(node, 'compact'))) const hostileInlineCarry = fc.oneof(carriedNode, jsonValue).map((node) => spellCarry(JSON.stringify(node, null, 1))) -const blockCarry = carriedNode.map((node) => fencedCodeBlock(carryName, serializeCanonicalJson(node, 'two-space'))) -const hostileBlockCarry = fc.oneof(carriedNode, jsonValue).map((node) => fencedCodeBlock(carryName, JSON.stringify(node))) +const blockCarry = fc.oneof( + { arbitrary: carriedParts.map(([type, untyped]) => fencedCodeBlock(`${carryFencePrefix}${type}`, serializeCanonicalJson(untyped, 'two-space'))), weight: 3 }, + { arbitrary: unnamedParts.map(([type, untyped]) => fencedCodeBlock(carryFencePrefix, serializeCanonicalJson({ ...untyped, type }, 'two-space'))), weight: 1 }, +) +const hostileBlockCarry = fc + .tuple( + fc.oneof(carriedParts, unnamedParts), + fc.oneof( + { arbitrary: fc.constantFrom(undefined, ''), weight: 6 }, + { arbitrary: fc.constantFrom('widget', 'x y', 'a`b', 'a\\b', 'a&b', ' padded'), weight: 1 }, + ), + fc.oneof({ arbitrary: fc.constantFrom('untyped', 'typed'), weight: 6 }, { arbitrary: fc.constant('other'), weight: 1 }), + fc.oneof({ arbitrary: fc.constant('canonical'), weight: 4 }, { arbitrary: fc.constantFrom('compact', 'indented'), weight: 1 }), + jsonValue, + ) + .map(([[type, untyped], named, body, spelling, other]) => { + const value = body === 'untyped' ? untyped : body === 'typed' ? { ...untyped, type } : other + const json = spelling === 'canonical' ? serializeCanonicalJson(value, 'two-space') : spelling === 'compact' ? JSON.stringify(value) : JSON.stringify(value, null, 1) + return fencedCodeBlock(`${carryFencePrefix}${named ?? type}`, json) + }) function prefixLines(body: string, first: string, rest: (index: number) => string): string { return body @@ -402,6 +424,7 @@ const edit: Arbitrary = fc.tuple(fc.nat(), fc.nat({ max: 3 }), fc.oneof(pi const markdown = fc.oneof( { arbitrary: cleanMarkdown, weight: 4 }, { arbitrary: hostileMarkdown, weight: 2 }, + { arbitrary: fc.oneof(blockCarry, hostileBlockCarry), weight: 2 }, { arbitrary: canonical, weight: 2 }, { arbitrary: fc.tuple(fc.oneof(cleanMarkdown, canonical), fc.array(edit, { maxLength: 3, minLength: 1 })).map(([held, edits]) => edited(held, edits)), weight: 3 }, { arbitrary: fc.string({ maxLength: 40, unit: piece }), weight: 1 }, diff --git a/todo.md b/todo.md index 3dcda69..da87bd6 100644 --- a/todo.md +++ b/todo.md @@ -13,7 +13,6 @@ | ID | Release | Blocked by | Exempt | Item | L | E | I | R | S | Moves | Score | |---|---|---|---|---|---|---|---|---|---|---|---| | 153 | 0.4.0 | | question | **Decide whether `src/markdown/` root may import `emit/`, `parse/` or `portable/`, and hold the answer in `.oxlintrc.json` and §The source parts by ADF and format.** | 1 | 2 | 2 | 3 | 2 | code health by 3 | 26.2 | -| 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 | | 154 | 0.4.0 | | | **Pair highlights over the inline reader's pieces by their offsets before building nodes, so no placeholder text node stands in for a delimiter.** | 2 | 1 | 2 | 3 | 4 | code health by 4 | 17.4 | | 155 | 0.4.0 | | | **Have `closeBracket` return the link, image or literal bracket it builds, and apply it to the scan in one caller.** | 2 | 1 | 2 | 3 | 4 | code health by 4 | 17.4 | | 162 | 0.4.0 | | | **Pin each rule of the emphasis sweep with a hand-worked line, so every mutant that lets a pair fall where the one-run-per-pass reference keeps it fails a test.** | 2 | 1 | 1 | 2 | 4 | code health by 4 | 17.4 | @@ -75,14 +74,6 @@ a paragraph opening with a spellable `!adf:link` (§Nothing recurses unbounded). `src/markdown/**` bans only `html/`; banning `emit/`, `parse/` and `portable/` for the root's own files, with its `ci.sh` probe, extends the decision. -### 75. Make the generated-markdown property write block carries as `adf:` fences, canonical and hostile. - -`blockCarry` and `hostileBlockCarry` (`src/conformance/markdown-property.test.ts`) write a fence -whose info string is `carry`. Under the `docs/decisions.md` entry "The carry fence names the node -type", that is an ordinary code language, so the property reaches `readCarriedBlock`'s refusals only -where a random edit lands in a canonical document. Write `adf:` and `adf:` fences, canonical -and hostile. Technical principle: for pure logic over many inputs, prefer a property test, so the property must reach the fenced carry's refusals itself. Found by the technical-principles audit, 2026-10-04. - ### 154. Pair highlights over the inline reader's pieces by their offsets before building nodes, so no placeholder text node stands in for a delimiter. A comprehension seat found `src/markdown/inline-content.ts` `resolveNodes` / `pairedHighlights`