From 4349df9017aaa7cc2549f4d872dd45d47120cabc Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sat, 3 Oct 2026 15:17:53 +0200 Subject: [PATCH] Final wording: the reserved-directive refusal row, the adf:-alone fence, the changelog's carry and scope, the developer panel, the migration's empty shape --- AGENTS.md | 4 ++-- CHANGELOG.md | 16 +++++++--------- MIGRATION.md | 2 +- README.md | 2 +- docs/decisions.md | 6 +++--- spec/flavour.md | 5 +++-- src/conformance/property-harness.ts | 2 +- 7 files changed, 18 insertions(+), 19 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index af7002b..39b5314 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -175,8 +175,8 @@ settle it; otherwise four more read, five of seven settle it, and less is a miss The verdict lands in `docs/decisions.md`. A writer panel settles every new or changed markdown or HTML spelling: a reader panel whose readers -are the people who read and write that format (README `## Audience`). A question about what an app -relies on goes to the developer personas instead. +are the people who read and write that format (README `## Audience`). A panel of the developer +personas settles a question about what an app relies on. ### Stated numbers diff --git a/CHANGELOG.md b/CHANGELOG.md index a10c5de..3123e48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,20 +5,18 @@ - **Breaking:** directives, the inline opaque carry among them (now `!adf:carry{json="…"}`), are spelled under an `!adf:` prefix (`!adf:name … !adf:/name`, `!adf:name[content]{attrs}`, `!adf:name arg {attrs}`) in place of the `:::`/`::`/`:name` forms: text holding an unescaped - `!adf:` is claimed, and `adf` is an ordinary code block language. Convert stored markdown per - `MIGRATION.md`. + `!adf:` is claimed. Convert stored markdown per `MIGRATION.md`. - **Breaking:** the block carry is a code fence whose info string `adf:` names the node's type, its body the node's JSON without `type`, and a code fence whose info string opens `adf:` is - claimed. Convert stored markdown per `MIGRATION.md`. + claimed, and `adf` is an ordinary code block language. Convert stored markdown per `MIGRATION.md`. - **Breaking:** `markdownToAdf` and `plainMarkdownToAdf` read markdown holding no block as a document whose `content` is empty, as Atlassian's schema requires; `!adf:doc {content=none}` spells a document holding no `content` key. -- `markdownToAdf(adfToMarkdown(doc))` deep-equals `doc` as JSON, for a document of plain objects as - `JSON.parse` builds them: two adjacent text nodes CommonMark would read back as one are parted by - `!adf:textBreak{}`, an empty `attrs`, `content` or `marks` is spelled `{attrs=empty}`, - `{content=empty}` or `{marks=empty}`, `-0` is spelled `-0`, and a `codeBlock` of several text - nodes is a fence per node. A `codeBlock` holding other than plain text nodes rides the block - carry, where it was refused. +- `markdownToAdf(adfToMarkdown(doc))` deep-equals `doc` as `JSON.parse` builds it: two adjacent text + nodes CommonMark would read back as one are parted by `!adf:textBreak{}`, an empty `attrs`, + `content` or `marks` is spelled `{attrs=empty}`, `{content=empty}` or `{marks=empty}`, `-0` is + spelled `-0`, and a `codeBlock` of several text nodes is a fence per node. A `codeBlock` holding + other than plain text nodes rides the block carry, where it was refused. - **Breaking:** `unspellable-link` leaves `ConvertErrorCode`; a link whose `href` or `title` no CommonMark escape spells is written as `!adf:link[text]{attrs}`. - **Breaking:** some directive refusals carry `malformed-directive` where they carried diff --git a/MIGRATION.md b/MIGRATION.md index d213215..a5fb98d 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -7,7 +7,7 @@ turning each directive into text and each carried node into an `adf` code block. reads any `0.1.0` markdown, convert what is stored or in flight (an open editor, a queue) with the recipe below, and rewrite markdown your code writes or matches (templates, prompts, patterns) by the tables below. Stored ADF needs one change: give a document holding no `content` key `content: []`. -`0.1.0` read empty markdown to that shape, meaning the empty document. +`0.1.0` built that shape from empty markdown, meaning the empty document. ### Convert markdown diff --git a/README.md b/README.md index 0fb2474..37874cf 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 (`!adf:textBreak{}`, `!adf:listBreak`, `!adf:doc`) stands where it parts nothing | make the edit the message opens with; `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` 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 | ## The guarantees diff --git a/docs/decisions.md b/docs/decisions.md index 19c83f3..b8f09d3 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -110,9 +110,9 @@ node's JSON without `type`: ```` ```adf:blockCard ````. A type no info string ca rule a code language follows — leaves the info string `adf:` and keeps `type` in the body. Every info string opening `adf:` is reserved, so a `codeBlock` whose language opens so takes the `language` attribute, and `carry` is an ordinary language. A body holding `type` under a named type, -or a bare `adf:` fence whose body's `type` an info string could carry, is `unsupported-node-shape`. -The reservation claims a fence CommonMark reads as code until `todo.md` item 43 gives CommonMark its -own reader. +or a fence whose info string is `adf:` alone while its body's `type` could be spelled in the info +string, is `unsupported-node-shape`. The reservation claims a fence CommonMark reads as code until +`todo.md` item 43 gives CommonMark its own reader. ## A code block is a fence per text node diff --git a/spec/flavour.md b/spec/flavour.md index 9e7396b..3c4b6fc 100644 --- a/spec/flavour.md +++ b/spec/flavour.md @@ -181,8 +181,9 @@ product). Block and inline positions canonicalize differently, each fitting wher - **Block position**: a fenced code block with info string `adf:` and the node's type, body = the node's JSON without its `type` — two-space indent, object keys sorted: ```` ```adf:blockCard ````. A type no info string carries back, by the rule a `codeBlock`'s language follows, leaves the info - string `adf:` and keeps `type` in the body. A body holding `type` under a named type, or a bare - `adf:` fence whose body's `type` an info string could carry, is a named error. + string `adf:` and keeps `type` in the body. A body holding `type` under a named type, or a fence + whose info string is `adf:` alone while its body's `type` could be spelled in the info string, is + a named error. - **Inline position**: `!adf:carry{json="…"}` — compact serialization (keys sorted, no whitespace), JSON-string-escaped into the attribute. diff --git a/src/conformance/property-harness.ts b/src/conformance/property-harness.ts index d38579e..32ddcf5 100644 --- a/src/conformance/property-harness.ts +++ b/src/conformance/property-harness.ts @@ -78,7 +78,7 @@ function heldAttributes(held: Readonly>): return attrs } -// An empty attrs, content or marks key, and adjacent text a reader would merge, stay occasional: each takes a spelling outside CommonMark. +// An empty attrs, content or marks key, and adjacent text CommonMark would read back as one, stay occasional: each takes a spelling outside CommonMark. // The copy gives fast-check's null-prototype records the prototype a parsed node has. function occasionallyEmpty(arbitrary: Arbitrary): Arbitrary { return fc.tuple(arbitrary, fc.nat({ max: 9 })).map(([held, roll]) => (roll === 0 ? { ...held } : withoutEmptyKeys(held)))