3
0
mirror of https://github.com/larvit/adf-codec.git synced 2026-11-12 11:48:29 +01:00

Write the property's block carries as adf: fences, canonical and hostile

This commit is contained in:
2026-11-09 00:33:24 +01:00
committed by Lillem4n
parent 47b95fbaa5
commit 6b33a2e299
3 changed files with 46 additions and 15 deletions
+17
View File
@@ -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
+29 -6
View File
@@ -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<Edit> = 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 },
-9
View File
@@ -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:<type>` 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`