36b - §8, §9 and §14's decisions move to docs/decisions.md #136
@@ -28,96 +28,22 @@ In `docs/decisions.md`:
|
|||||||
- ESM only
|
- ESM only
|
||||||
- One built entrypoint
|
- One built entrypoint
|
||||||
- Public on npm
|
- Public on npm
|
||||||
|
- The formats are API
|
||||||
|
- The code list
|
||||||
|
- Which code a cause takes
|
||||||
|
- `message` and `path`
|
||||||
|
- Publish on a version bump
|
||||||
|
- Docs describe the release being built
|
||||||
|
- No schema validation
|
||||||
|
|
||||||
## 7. Nothing about any consumer
|
## 7. Nothing about any consumer
|
||||||
|
|
||||||
No Jira client, no HTTP, no REST shapes, no issue keys, no actual consumer named anywhere. Design
|
No Jira client, no HTTP, no REST shapes, no issue keys, no actual consumer named anywhere. Design
|
||||||
against the README's personas.
|
against the README's personas.
|
||||||
|
|
||||||
## 8. Semver: the formats are API
|
|
||||||
|
|
||||||
The emitted markdown and HTML are contracts. After 1.0: previously-emitted output parsing
|
|
||||||
differently, or not at all, is MAJOR; new syntax while old output still round-trips is MINOR.
|
|
||||||
Pre-1.0, normal 0.x rules. A spelled node's content model is part of that contract — leaf or
|
|
||||||
container is the model, not the syntax — so giving a spelled node's model content it had not, or
|
|
||||||
taking it away, is MAJOR whatever ADF's own schema does.
|
|
||||||
|
|
||||||
The error surface is a contract too; `README.md` §The errors states it to the consumer, and the
|
|
||||||
types in `src/result.ts` hold its shape.
|
|
||||||
|
|
||||||
### The code list
|
|
||||||
|
|
||||||
- Adding, removing or renaming a code is breaking, so a new cause takes an existing code whose
|
|
||||||
name reads true of it in both directions; where none does and a plain name exists, a new code —
|
|
||||||
in any 0.x minor, and after 1.0 only in a MAJOR (the maintainer, 2026-09-18).
|
|
||||||
- A refusal whose cause is this library's own invariant rather than the input takes the existing
|
|
||||||
code nearest what the consumer sees — a document that does not convert is
|
|
||||||
`unsupported-node-shape` — since a code no input reaches is one no consumer can switch on (the
|
|
||||||
maintainer, 2026-09-20).
|
|
||||||
- A refusal no spelling recovers from is a gap in the flavour rather than a code: give the flavour
|
|
||||||
the spelling and the code goes, which the freeze is the last moment for (`unspellable-link`, the
|
|
||||||
maintainer, 2026-09-13). A cause the carry answers gets no code: a mark no spelling writes rides
|
|
||||||
the carry with its node.
|
|
||||||
|
|
||||||
### Which code a cause takes
|
|
||||||
|
|
||||||
- A code names the cause; where one cause recurs across node types, across one mark's attributes
|
|
||||||
or across directions, one code covers them all and `path` and `message` say which —
|
|
||||||
`unsupported-nesting-depth` is the 500-level guard whichever direction hits it,
|
|
||||||
`unspellable-character` the text node and the code block alike. Where two codes stay apart, the
|
|
||||||
line between them is what they name: `unspellable-character` is a character CommonMark rewrites
|
|
||||||
wherever text holds it, `unspellable-whitespace` the newline no inline directive's content slot
|
|
||||||
spans, in either direction.
|
|
||||||
- A claim code names the spelling claimed, never the node that spelling would have built: a
|
|
||||||
malformed `!adf:table` is a `malformed-directive`, and an alignment colon a
|
|
||||||
`malformed-pipe-table` — the flavour's own delimiter row is `-` runs, so the grammar refuses the
|
|
||||||
colon rather than ADF's missing column model doing it. What the grammar itself refuses stays a
|
|
||||||
claim code, key order among it, and a leaf given a body is refused at its opener, as a container
|
|
||||||
missing its closer is (the maintainer, 2026-09-16).
|
|
||||||
- A directive whose name reads back to no node is `unknown-directive-name` rather than a claim
|
|
||||||
code — the spelling is well formed, and telling that apart from a typo is what a consumer
|
|
||||||
switches on when a later MINOR gives the name meaning. A reserved name is a known name, so never
|
|
||||||
that code, and the two the flavour reserves part on form: a form the grammar does not have is a
|
|
||||||
claim code — `!adf:carry`, whose carry is the fence — and a well-formed form in the wrong place
|
|
||||||
is `unsupported-node-shape`, `!adf:listBreak` parting anything but two adjacent lists of one
|
|
||||||
type.
|
|
||||||
- A well-formed directive the node tables refuse — an attribute a node does not hold or spells
|
|
||||||
elsewhere, a value outside its kind or its canonical spelling, an argument, or a body of a shape
|
|
||||||
its content model does not take — is `unsupported-node-shape`, the emitter's code for the same
|
|
||||||
mismatch read the other way: one code across both directions for good, since the call site
|
|
||||||
knows which direction it called and parting them after `0.1.0` is MAJOR.
|
|
||||||
- A non-finite number takes two codes: `unsupported-node-shape` parsing, `not-an-adf-document`
|
|
||||||
emitting — no document holds one, so no round-trip crosses them.
|
|
||||||
|
|
||||||
### `message` and `path`
|
|
||||||
|
|
||||||
- A message names the violation, not the rule alone — a rule by itself states a truth the reader
|
|
||||||
must invert before it reads as a failure — and where the flavour's claim refuses ordinary prose
|
|
||||||
it names the escape that unclaims the form claimed: `\!adf:` for a directive, block line and
|
|
||||||
inline alike, `\|` for every pipe row.
|
|
||||||
- `not-an-adf-document` carries the document's own path throughout: seven of the guard's eight
|
|
||||||
branches read the document's own shape, and threading a path to the eighth — a malformed node
|
|
||||||
anywhere in the tree — wants the manual stack §11's no-recursion rule forces, whose empty half
|
|
||||||
no input reaches. The message names the violation instead.
|
|
||||||
|
|
||||||
## 9. Release automation
|
## 9. Release automation
|
||||||
|
|
||||||
- `package.json` version on `main` is the source of truth. CI on `main`: tests green and version
|
|
||||||
differs from npm → publish and tag `vX.Y.Z`. No bump, no deploy; the bump is each shipping PR's
|
|
||||||
deliberate semver judgment. `publish.sh` is that job, and `private: true` stops it before it
|
|
||||||
reads the token, so the pipeline is live and silent until the maintainer's first bump drops the
|
|
||||||
field.
|
|
||||||
- The bump commit renames `CHANGELOG.md`'s `## Unreleased` to the version.
|
- The bump commit renames `CHANGELOG.md`'s `## Unreleased` to the version.
|
||||||
- Docs on `main` describe the release being built rather than the version npm holds, so they match
|
|
||||||
it the moment the bump publishes; add no interim note marking the gap (the maintainer,
|
|
||||||
2026-09-16).
|
|
||||||
- The publish and the tag each observe their own end state — the version on npm, the tag on the
|
|
||||||
remote — and neither gates the other, so a run that dies between them converges on the next push
|
|
||||||
to `main` rather than leaving npm ahead of the tags. An unanswered registry reads the same as an
|
|
||||||
unpublished version, which npm's own duplicate rejection is what catches. The job rebuilds rather
|
|
||||||
than taking the gate's `dist`: the lockfile is committed, the image is patch-pinned and `tsc` is
|
|
||||||
deterministic, so the two builds agree, and promoting an artifact would make the release path
|
|
||||||
depend on a store that the gate would then have to keep.
|
|
||||||
- Exact versions: `save-exact=true` in `.npmrc`.
|
- Exact versions: `save-exact=true` in `.npmrc`.
|
||||||
- Renovate watches devDependencies, Docker pins and action tags; automerges everything on green CI.
|
- Renovate watches devDependencies, Docker pins and action tags; automerges everything on green CI.
|
||||||
- Docker images pin the full patch version (`node:24.19.0-alpine3.24`, never `node:24`), as
|
- Docker images pin the full patch version (`node:24.19.0-alpine3.24`, never `node:24`), as
|
||||||
@@ -316,16 +242,6 @@ Applies everywhere: comments, every markdown file in this repo (this one include
|
|||||||
|
|
||||||
One-line commit messages and PR titles; short PR summaries. No AI-attribution markers, ever.
|
One-line commit messages and PR titles; short PR summaries. No AI-attribution markers, ever.
|
||||||
|
|
||||||
## 14. Non-goals
|
|
||||||
|
|
||||||
No network or filesystem I/O, no name→id resolution (`docs/decisions.md` §Names stay text), no ADF
|
|
||||||
schema validation or exported validator — a refusal that keeps the round-trip is not schema
|
|
||||||
validation, so the one a spelled node carrying the same mark type twice earns stays, and input
|
|
||||||
nesting a spelling inside its own kind (`*(*a*)*`) names that mark once, no shipped CSS
|
|
||||||
(`docs/decisions.md` §The HTML dialect), no streaming APIs, no performance budget past §11's
|
|
||||||
scanning rule — nothing here is tuned, and no figure is promised. A CLI is a later goal (`todo.md`),
|
|
||||||
not a non-goal.
|
|
||||||
|
|
||||||
## 15. The working loop
|
## 15. The working loop
|
||||||
|
|
||||||
`todo.md` lists what is left under the release that ships it, in shipping order. One item per
|
`todo.md` lists what is left under the release that ships it, in shipping order. One item per
|
||||||
@@ -347,7 +263,8 @@ Per chunk:
|
|||||||
reworded for them into `CHANGELOG.md`'s `## Unreleased` — report, stop.
|
reworded for them into `CHANGELOG.md`'s `## Unreleased` — report, stop.
|
||||||
|
|
||||||
Reserved for the maintainer whatever any rule here says: changing `version` in `package.json` (a
|
Reserved for the maintainer whatever any rule here says: changing `version` in `package.json` (a
|
||||||
bump on `main` publishes, §9 — every release is the maintainer's) and the `NPM_TOKEN` secret.
|
bump on `main` publishes, `docs/decisions.md` §Publish on a version bump — every release is the
|
||||||
|
maintainer's) and the `NPM_TOKEN` secret.
|
||||||
|
|
||||||
### Ask, don't guess
|
### Ask, don't guess
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,10 @@ In priority order.
|
|||||||
the emitted formats are.
|
the emitted formats are.
|
||||||
7. **Nothing in the way.** No runtime dependencies, no I/O, no configuration, no host API: ESM on
|
7. **Nothing in the way.** No runtime dependencies, no I/O, no configuration, no host API: ESM on
|
||||||
any ES2022 engine, in a browser as readily as on a server, installed from public npm.
|
any ES2022 engine, in a browser as readily as on a server, installed from public npm.
|
||||||
|
8. **Correct before fast.** A whole document in, a whole result out, one call; no input makes a
|
||||||
|
call hang or overflow the stack.
|
||||||
|
9. **Fast once correct.** Conversion time grows linearly with the document wherever the goals above
|
||||||
|
allow it; a faster path that risks one of them is not taken.
|
||||||
|
|
||||||
## Audience
|
## Audience
|
||||||
|
|
||||||
@@ -224,7 +228,8 @@ emit refuses:
|
|||||||
- Past that and `~~`, no GFM: an autolink literal and a `- [ ]` marker stay text, and a checklist
|
- Past that and `~~`, no GFM: an autolink literal and a `- [ ]` marker stay text, and a checklist
|
||||||
is the `taskList` directive — `plainMarkdownToAdf` turns the marker into a `taskList`.
|
is the `taskList` directive — `plainMarkdownToAdf` turns the marker into a `taskList`.
|
||||||
- A document nested deeper than 500 levels is an error result, not a stack overflow.
|
- A document nested deeper than 500 levels is an error result, not a stack overflow.
|
||||||
- The emitted formats are semver surface (AGENTS.md §8).
|
- The emitted formats are semver surface
|
||||||
|
([`docs/decisions.md`](https://gitea.larvit.se/larvit/adf-codec/src/branch/main/docs/decisions.md#the-formats-are-api)).
|
||||||
- **`0.2.0`** — `htmlToAdf(adfToHtml(doc))` equals `doc`; fidelity HTML cannot express rides
|
- **`0.2.0`** — `htmlToAdf(adfToHtml(doc))` equals `doc`; fidelity HTML cannot express rides
|
||||||
`data-*` attributes. Foreign HTML maps a documented element set, which markdown's raw HTML reads
|
`data-*` attributes. Foreign HTML maps a documented element set, which markdown's raw HTML reads
|
||||||
through as well, and a construct outside it is an error; well-formed HTML only — no tag-soup
|
through as well, and a construct outside it is an error; well-formed HTML only — no tag-soup
|
||||||
|
|||||||
@@ -182,3 +182,112 @@ an npm consumer.
|
|||||||
|
|
||||||
Published to public npm as `@larvit/adf-codec`. Public source: the Gitea repo goes public,
|
Published to public npm as `@larvit/adf-codec`. Public source: the Gitea repo goes public,
|
||||||
LICENSE in place, before the first publish.
|
LICENSE in place, before the first publish.
|
||||||
|
|
||||||
|
## The formats are API
|
||||||
|
|
||||||
|
2026-08-23, content models 2026-09-16, the maintainer. Goals 1 and 6. Valid while consumers store
|
||||||
|
what the library emits.
|
||||||
|
|
||||||
|
The emitted markdown and HTML are contracts. After 1.0: previously-emitted output parsing
|
||||||
|
differently, or not at all, is MAJOR; new syntax while old output still round-trips is MINOR.
|
||||||
|
Pre-1.0, normal 0.x rules. A spelled node's content model is part of that contract — leaf or
|
||||||
|
container is the model, not the syntax — so giving a spelled node's model content it had not, or
|
||||||
|
taking it away, is MAJOR whatever ADF's own schema does.
|
||||||
|
|
||||||
|
The error surface is a contract too; `README.md` §The errors states it to the consumer, and the
|
||||||
|
types in `src/result.ts` hold its shape.
|
||||||
|
|
||||||
|
## The code list
|
||||||
|
|
||||||
|
2026-08-25, the maintainer; dated below where a rule came later. Goal 6. Valid while a consumer
|
||||||
|
switches on `code` with no `default`.
|
||||||
|
|
||||||
|
- Adding, removing or renaming a code is breaking, so a new cause takes an existing code whose
|
||||||
|
name reads true of it in both directions; where none does and a plain name exists, a new code —
|
||||||
|
in any 0.x minor, and after 1.0 only in a MAJOR (2026-09-18).
|
||||||
|
- A refusal whose cause is this library's own invariant rather than the input takes the existing
|
||||||
|
code nearest what the consumer sees — a document that does not convert is
|
||||||
|
`unsupported-node-shape` — since a code no input reaches is one no consumer can switch on
|
||||||
|
(2026-09-20).
|
||||||
|
- A refusal no spelling recovers from is a gap in the flavour rather than a code: give the flavour
|
||||||
|
the spelling and the code goes (`unspellable-link`, 2026-09-13). A cause the carry answers gets
|
||||||
|
no code: a mark no spelling writes rides the carry with its node.
|
||||||
|
|
||||||
|
## Which code a cause takes
|
||||||
|
|
||||||
|
2026-08-28, the maintainer; dated below where a rule came later. Goal 6. Valid while a consumer
|
||||||
|
handles one cause alike whichever node, attribute or direction raised it.
|
||||||
|
|
||||||
|
- A code names the cause; where one cause recurs across node types, across one mark's attributes
|
||||||
|
or across directions, one code covers them all and `path` and `message` say which —
|
||||||
|
`unsupported-nesting-depth` is the 500-level guard whichever direction hits it,
|
||||||
|
`unspellable-character` the text node and the code block alike. Where two codes stay apart, the
|
||||||
|
line between them is what they name: `unspellable-character` is a character CommonMark rewrites
|
||||||
|
wherever text holds it, `unspellable-whitespace` the newline no inline directive's content slot
|
||||||
|
spans, in either direction.
|
||||||
|
- A claim code names the spelling claimed, never the node that spelling would have built: a
|
||||||
|
malformed `!adf:table` is a `malformed-directive`, and an alignment colon a
|
||||||
|
`malformed-pipe-table` — the flavour's own delimiter row is `-` runs, so the grammar refuses the
|
||||||
|
colon rather than ADF's missing column model doing it. What the grammar itself refuses stays a
|
||||||
|
claim code, key order among it, and a leaf given a body is refused at its opener, as a container
|
||||||
|
missing its closer is (2026-09-16).
|
||||||
|
- A directive whose name reads back to no node is `unknown-directive-name` rather than a claim
|
||||||
|
code — the spelling is well formed, and telling that apart from a typo is what a consumer
|
||||||
|
switches on when a later MINOR gives the name meaning. A reserved name is a known name, so never
|
||||||
|
that code, and the two the flavour reserves part on form: a form the grammar does not have is a
|
||||||
|
claim code — `!adf:carry`, whose carry is the fence — and a well-formed form in the wrong place
|
||||||
|
is `unsupported-node-shape`, `!adf:listBreak` parting anything but two adjacent lists of one
|
||||||
|
type (2026-09-01).
|
||||||
|
- A well-formed directive the node tables refuse — an attribute a node does not hold or spells
|
||||||
|
elsewhere, a value outside its kind or its canonical spelling, an argument, or a body of a shape
|
||||||
|
its content model does not take — is `unsupported-node-shape`, the emitter's code for the same
|
||||||
|
mismatch read the other way: one code across both directions for good, since the call site
|
||||||
|
knows which direction it called and parting them after `0.1.0` is MAJOR (2026-09-23).
|
||||||
|
- A non-finite number takes two codes: `unsupported-node-shape` parsing, `not-an-adf-document`
|
||||||
|
emitting — no document holds one, so no round-trip crosses them (2026-09-23).
|
||||||
|
|
||||||
|
## `message` and `path`
|
||||||
|
|
||||||
|
2026-09-03, the path 2026-09-23, the maintainer. Goals 3 and 6. Valid while a person fixing the
|
||||||
|
input reads `message`.
|
||||||
|
|
||||||
|
- A message names the violation, not the rule alone — a rule by itself states a truth the reader
|
||||||
|
must invert before it reads as a failure — and where the flavour's claim refuses ordinary prose
|
||||||
|
it names the escape that unclaims the form claimed: `\!adf:` for a directive, block line and
|
||||||
|
inline alike, `\|` for every pipe row.
|
||||||
|
- `not-an-adf-document` carries the document's own path throughout: seven of the guard's eight
|
||||||
|
branches read the document's own shape, and threading a path to the eighth — a malformed node
|
||||||
|
anywhere in the tree — wants the manual stack the no-recursion rule (`AGENTS.md` §11) forces,
|
||||||
|
whose empty half no input reaches. The message names the violation instead.
|
||||||
|
|
||||||
|
## Publish on a version bump
|
||||||
|
|
||||||
|
2026-08-23, converging 2026-09-03, the maintainer. Goal 7. Valid while CI on `main` holds the npm
|
||||||
|
token.
|
||||||
|
|
||||||
|
`package.json` version on `main` is the source of truth. CI on `main`: tests green and the version
|
||||||
|
not yet on npm → publish and tag `vX.Y.Z`. No bump, no deploy. `publish.sh` is that job.
|
||||||
|
|
||||||
|
The publish and the tag each check their own end state — the version on npm, the tag on the
|
||||||
|
remote — so a run that dies between them converges on the next push
|
||||||
|
to `main` rather than leaving npm ahead of the tags. An unanswered registry reads the same as an
|
||||||
|
unpublished version, which npm's own duplicate rejection is what catches. The job rebuilds rather
|
||||||
|
than taking the gate's `dist`: the lockfile is committed, the image is patch-pinned and `tsc` is
|
||||||
|
deterministic, so the two builds agree, and promoting an artifact would make the release path
|
||||||
|
depend on a store that the gate would then have to keep.
|
||||||
|
|
||||||
|
## Docs describe the release being built
|
||||||
|
|
||||||
|
2026-09-16, the maintainer. Goal 7. Valid while a bump on `main` publishes.
|
||||||
|
|
||||||
|
Docs on `main` describe the release being built rather than the version npm holds, so they match it
|
||||||
|
the moment the bump publishes; add no interim note marking the gap.
|
||||||
|
|
||||||
|
## No schema validation
|
||||||
|
|
||||||
|
2026-08-23, the mark refusal 2026-08-25, the maintainer. Goal 1. Valid while the site a document
|
||||||
|
is saved to validates it.
|
||||||
|
|
||||||
|
No ADF schema validation or exported validator. A refusal that keeps the round-trip is not schema
|
||||||
|
validation, so the one a spelled node carrying the same mark type twice earns stays, and input
|
||||||
|
nesting a spelling inside its own kind (`*(*a*)*`) names that mark once.
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ fi
|
|||||||
published=$(leg "ask npmjs for $name@$version ($node_image)" published_version "$name" "$version")
|
published=$(leg "ask npmjs for $name@$version ($node_image)" published_version "$name" "$version")
|
||||||
tagged=$(leg "ask origin for v$version" git ls-remote --tags origin "v$version")
|
tagged=$(leg "ask origin for v$version" git ls-remote --tags origin "v$version")
|
||||||
|
|
||||||
# Both steps observe their own end state, so a partial run converges on the next push to main.
|
|
||||||
if [ -z "$published" ]; then
|
if [ -z "$published" ]; then
|
||||||
: "${NPM_TOKEN:?the publish needs NPM_TOKEN}"
|
: "${NPM_TOKEN:?the publish needs NPM_TOKEN}"
|
||||||
leg "install ($node_image)" in_image "$node_image" npm ci
|
leg "install ($node_image)" in_image "$node_image" npm ci
|
||||||
|
|||||||
+20
-19
@@ -1,12 +1,12 @@
|
|||||||
# The markdown flavour
|
# The markdown flavour
|
||||||
|
|
||||||
The grammar of the extended markdown `adfToMarkdown` emits and `markdownToAdf` parses. Plain
|
The grammar of the extended markdown `adfToMarkdown` emits and `markdownToAdf` parses. Plain
|
||||||
CommonMark is a subset apart from raw HTML (below), with three carve-outs: literal text that
|
CommonMark is a subset apart from raw HTML (below), with three carve-outs: literal text that matches
|
||||||
matches directive syntax below or reads as a pipe table is claimed by the flavour, and a matched
|
directive syntax below or reads as a pipe table is claimed by the flavour, and a matched `~~` pair
|
||||||
`~~` pair spells `strike` (escape the `!adf:`, `|` or `~` to keep it literal) — and one gap: a
|
spells `strike` (escape the `!adf:`, `|` or `~` to keep it literal) — and one gap: a CommonMark
|
||||||
CommonMark image fits only as its own
|
image fits only as its own title-less paragraph — mid-text and titled images are named errors. The
|
||||||
title-less paragraph — mid-text and titled images are named errors. The emitted form is contract
|
emitted form is contract (`docs/decisions.md` §The formats are API). Per-node syntaxes build on this
|
||||||
(AGENTS.md §8). Per-node syntaxes build on this grammar in the sections below.
|
grammar in the sections below.
|
||||||
|
|
||||||
## Canonical form
|
## Canonical form
|
||||||
|
|
||||||
@@ -76,13 +76,14 @@ normalizes to it through the round-trip.
|
|||||||
One grammar for everything CommonMark lacks, namespaced: every directive opens with the literal
|
One grammar for everything CommonMark lacks, namespaced: every directive opens with the literal
|
||||||
`!adf:`. A directive name is `[a-z][A-Za-z0-9]*` — the ADF node and mark names the sections below
|
`!adf:`. A directive name is `[a-z][A-Za-z0-9]*` — the ADF node and mark names the sections below
|
||||||
spell as directives. Recognition is syntactic and name-set-independent: anything matching the forms
|
spell as directives. Recognition is syntactic and name-set-independent: anything matching the forms
|
||||||
below parses as a directive regardless of whether the name is known, and an unknown name is an
|
below parses as a directive regardless of whether the name is known, and an unknown name is an error
|
||||||
error result naming it at the opener, whatever follows it — so output an old emitter escaped stays
|
result naming it at the opener, whatever follows it — so output an old emitter escaped stays
|
||||||
escaped, and erroring input gaining meaning later is MINOR, never a reparse (§8). Each name belongs
|
escaped, and erroring input gaining meaning later is MINOR, never a reparse (`docs/decisions.md`
|
||||||
to one position, and a name the other one spells — a mark or an inline node written as a block
|
§The formats are API). Each name belongs to one position, and a name the other one spells — a mark
|
||||||
directive, a block node written inline — is a different error, naming the spelling it takes. Two
|
or an inline node written as a block directive, a block node written inline — is a different error,
|
||||||
reserved names read back to no node: `carry` for the opaque carry, as both directive name and fence
|
naming the spelling it takes. Two reserved names read back to no node: `carry` for the opaque carry,
|
||||||
info string, and `listBreak` for the leaf that parts two adjacent lists (Canonical form).
|
as both directive name and fence info string, and `listBreak` for the leaf that parts two adjacent
|
||||||
|
lists (Canonical form).
|
||||||
|
|
||||||
**Claiming**: an unescaped `!adf:` claims wherever it stands. What follows picks the form: `/name`
|
**Claiming**: an unescaped `!adf:` claims wherever it stands. What follows picks the form: `/name`
|
||||||
closes a container, and a name picks by what follows it in turn — a space or the line's end a block
|
closes a container, and a name picks by what follows it in turn — a space or the line's end a block
|
||||||
@@ -123,7 +124,7 @@ Which of the two a node takes is its content model, never the spelling: a model
|
|||||||
written as an opener–closer pair and one taking none as a leaf, so a leaf given a body and a
|
written as an opener–closer pair and one taking none as a leaf, so a leaf given a body and a
|
||||||
container missing its closer are each a named error. A node holding no content whose model takes
|
container missing its closer are each a named error. A node holding no content whose model takes
|
||||||
some is an empty pair. A spelled node's content model is contract in consequence — changing one is
|
some is an empty pair. A spelled node's content model is contract in consequence — changing one is
|
||||||
MAJOR (AGENTS.md §8).
|
MAJOR (`docs/decisions.md` §The formats are API).
|
||||||
|
|
||||||
Canonical spacing is the only spacing input reads: one space parts the name, `arg` and `{attrs}`,
|
Canonical spacing is the only spacing input reads: one space parts the name, `arg` and `{attrs}`,
|
||||||
and one parts each attribute pair, with no padding inside the braces. Trailing whitespace on a
|
and one parts each attribute pair, with no padding inside the braces. Trailing whitespace on a
|
||||||
@@ -184,11 +185,11 @@ HTML.
|
|||||||
|
|
||||||
## Block nodes
|
## Block nodes
|
||||||
|
|
||||||
The directive name is always the ADF node type. A container's body is the node's `content`; a
|
The directive name is always the ADF node type. A container's body is the node's `content`; a leaf
|
||||||
leaf has none. Every directive parses in any position — `markdownToAdf` builds exactly what is
|
has none. Every directive parses in any position — `markdownToAdf` builds exactly what is written;
|
||||||
written; validity against ADF's content models stays the author's business (AGENTS.md §14). It
|
validity against ADF's content models stays the author's business (`docs/decisions.md` §No schema
|
||||||
parses only in the form the emitter picks, though: a directive spelling a node the emitter would
|
validation). It parses only in the form the emitter picks, though: a directive spelling a node the
|
||||||
have written as CommonMark is a named error.
|
emitter would have written as CommonMark is a named error.
|
||||||
|
|
||||||
Each section lists attributes as `name (type)`. A parenthesized value set documents what real
|
Each section lists attributes as `name (type)`. A parenthesized value set documents what real
|
||||||
payloads hold; the type stays string and any value round-trips verbatim. Values map to attrs by
|
payloads hold; the type stays string and any value round-trips verbatim. Values map to attrs by
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ test('the refusal list is unique per example and names real examples', () => {
|
|||||||
for (const example of exampleToRefusal.keys()) assert.ok(spec.some((entry) => entry.example === example), `refusal ${example} names no example in the suite`)
|
for (const example of exampleToRefusal.keys()) assert.ok(spec.some((entry) => entry.example === example), `refusal ${example} names no example in the suite`)
|
||||||
})
|
})
|
||||||
|
|
||||||
// A mark is counted once per text node it touches (AGENTS.md §14).
|
// A mark is counted once per text node it touches (docs/decisions.md §No schema validation).
|
||||||
const countKeys = ['a', 'blockquote', 'br', 'code', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'img', 'li', 'ol', 'pre', 'strong', 'ul']
|
const countKeys = ['a', 'blockquote', 'br', 'code', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'img', 'li', 'ol', 'pre', 'strong', 'ul']
|
||||||
const nodeElement: Record<string, string> = {
|
const nodeElement: Record<string, string> = {
|
||||||
blockquote: 'blockquote',
|
blockquote: 'blockquote',
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export function readInlineDirectiveNode(
|
|||||||
return success(namedNode(name, attrs.value, undefined))
|
return success(namedNode(name, attrs.value, undefined))
|
||||||
}
|
}
|
||||||
|
|
||||||
// A name the other position spells names that spelling, never the code a later MINOR may fill (AGENTS.md §8).
|
// A name the other position spells names that spelling, never the code a later MINOR may fill (docs/decisions.md §Which code a cause takes).
|
||||||
function inlineSpellingFault(name: string): ConvertFault | undefined {
|
function inlineSpellingFault(name: string): ConvertFault | undefined {
|
||||||
const mark = inlineMarkSpellingFault(name)
|
const mark = inlineMarkSpellingFault(name)
|
||||||
if (mark !== undefined) return mark
|
if (mark !== undefined) return mark
|
||||||
|
|||||||
@@ -477,7 +477,7 @@ function markType(character: string, used: number): string {
|
|||||||
return used === 2 ? 'strong' : 'em'
|
return used === 2 ? 'strong' : 'em'
|
||||||
}
|
}
|
||||||
|
|
||||||
// A node cannot carry one mark type twice (AGENTS.md §14).
|
// A node cannot carry one mark type twice (docs/decisions.md §No schema validation).
|
||||||
function applyMark(nodes: readonly AdfNode[], mark: AdfMark): AdfNode[] {
|
function applyMark(nodes: readonly AdfNode[], mark: AdfMark): AdfNode[] {
|
||||||
return nodes.map((node) => {
|
return nodes.map((node) => {
|
||||||
const marks = nodeMarks(node)
|
const marks = nodeMarks(node)
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ function calledCodes(): string[] {
|
|||||||
return [...called].sort()
|
return [...called].sort()
|
||||||
}
|
}
|
||||||
|
|
||||||
// The list is frozen at 0.1.0 (AGENTS.md §8), so a code outliving its cause is a removal that costs a MAJOR.
|
// Removing a code is breaking (docs/decisions.md §The code list), so a code must not outlive its cause.
|
||||||
test('every ConvertErrorCode is the code of a production call site, and every call site names a declared one', () => {
|
test('every ConvertErrorCode is the code of a production call site, and every call site names a declared one', () => {
|
||||||
assert.deepEqual(calledCodes(), declaredCodes())
|
assert.deepEqual(calledCodes(), declaredCodes())
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -7,8 +7,6 @@
|
|||||||
one no goal serves is proposed as a goal and asked. Sources: `AGENTS.md`'s body, the settled text
|
one no goal serves is proposed as a goal and asked. Sources: `AGENTS.md`'s body, the settled text
|
||||||
in this file's items, and `todo-history.md`, deleted with the bare `(28)` citations into it once
|
in this file's items, and `todo-history.md`, deleted with the bare `(28)` citations into it once
|
||||||
nothing cites it. Split by `AGENTS.md` section where one chunk is too big.
|
nothing cites it. Split by `AGENTS.md` section where one chunk is too big.
|
||||||
- **36b — Move §8, §9 and §14's decisions.** The code list's rules, release automation and the
|
|
||||||
non-goals.
|
|
||||||
- **36c — Move §10 and §11's decisions.** The engine legs, floors, size ratchet, bounds, spellings
|
- **36c — Move §10 and §11's decisions.** The engine legs, floors, size ratchet, bounds, spellings
|
||||||
and layout; the style rules stay working rules.
|
and layout; the style rules stay working rules.
|
||||||
- **36d — Move the settled text in `todo.md`'s items and §15's dated rules, and point §15's
|
- **36d — Move the settled text in `todo.md`'s items and §15's dated rules, and point §15's
|
||||||
|
|||||||
Reference in New Issue
Block a user