Status: Draft Draft

License: CC-BY-4.0

Available formats: HTML, Markdown

&ND Core v1 — Normative Specification (Draft)


1. Core Philosophy

&ND describes document structure. It does not interpret meaning. It does not execute code. It fails on ambiguity.


1.1 Locked Vocabulary

The following terms are used normatively throughout this specification and SHOULD be interpreted consistently by implementations and companion documents.

  • strong: an inline structural node that marks stronger importance or prominence. It does not

prescribe bold rendering.

  • emphasis: an inline structural node that marks emphasis. It does not prescribe italic

rendering.

  • inline code: an inline raw-like node whose payload suppresses nested inline-node parsing, while

still honoring delimiter-safe escapes required by the grammar.

  • raw island: a region whose interior is not recursively parsed as ordinary block or inline

structure. In Core v1, code blocks and extension blocks are raw islands.

  • canonical: the single normalized representation required for semantically equivalent documents

under the canonical rules.

  • strict: the default normative parse mode. Unknown syntax and malformed Core v1 syntax cause

failure.

  • recovery: a non-normative editor-oriented parse mode for partial document recovery from invalid

current-version input.

  • forward_compat: a non-normative opt-in parse mode for compatibility with unknown future syntax.

It is non-conformant and MUST NOT be used where strict semantic agreement is required.

Where editor tooling or theme systems use presentation-oriented terms such as bold or italic, those are implementation-layer mappings only and do not change the language semantics above.


2. Design Principles

2.1 Single Representation

Each feature has exactly one syntax.

2.2 Deterministic Parsing

Same input → same output.

2.3 Fail-Closed

Invalid input MUST produce an error and no document.

2.4 Structural Integrity

  • Proper nesting required
  • No overlapping inline spans

2.5 Non-Executable

No HTML, no scripts, no runtime behavior.

2.6 Opaque Extensions

Unknown extension block names MUST be preserved as opaque blocks but not interpreted.

This rule applies to extension blocks only. Unknown inline tags and invalid core syntax MUST error.


3. Parsing Modes

3.1 Strict Mode (Normative)

  • No AST produced
  • No partial output

3.2 Recovery Mode (Non-Normative)

  • For editors only
  • MUST NOT be used in pipelines

3.3 Forward-Compatibility Mode (Non-Normative)

forward_compat is an explicitly opt-in compatibility mode for shared-version environments where a consumer may encounter future syntax that a current parser does not implement.

Example result shape:

Rules:

  • forward_compat MUST NOT be the default mode
  • forward_compat MUST be explicitly selected by the consumer
  • forward_compat MUST NOT be treated as a conformant strict parse
  • forward_compat MUST NOT be used for canonical emission, signing, validation pipelines, or any

workflow that requires strict semantic agreement

  • forward_compat MAY downgrade unknown future constructs into inert text or opaque compatibility

nodes

  • forward_compat MUST NOT silently repair malformed known Core v1 syntax

The purpose of forward_compat is compatibility with future syntax, not recovery from invalid current-version documents.


3.4 Embedding and Profile Convention

The &ND v1 header is a document-type declaration for standalone &ND files. It is not required in every embedding context.

Profile and embedding rules:

  • A standalone &ND file SHOULD begin with &ND v1 on its first line.
  • An AEON annotation or doc-comment embedding MAY omit the header; the enclosing profile

or tool convention declares the content type.

  • A profile that embeds &ND content MUST document whether the header is required,

optional, or forbidden for that context.

  • Parsers receiving &ND input through a typed channel (e.g., fmt.and.aeon annotation

payload) MUST NOT require the header.

fmt.and.aeon comment payloads are therefore headerless &ND by convention. The &ND v1 header is a file-level signal, not a per-comment payload requirement.


4. Block Model

4.1 Paragraph Boundary Rule (Critical)

Blocks MUST NOT interrupt paragraphs.

This is a paragraph.

Blocks only start:

  • at document start
  • after a blank line

A line that would otherwise look like a block opener is paragraph text when it appears inside an already-open paragraph.

A line that is not block-open eligible MUST NOT be reinterpreted as a block opener. If such a line cannot be consumed as valid paragraph continuation text under the Core v1 paragraph rules, parsing MUST fail rather than silently reclassifying it.

That means block recognition is context-gated:

  • block-open eligible at document start or immediately after a blank line
  • not block-open eligible on continuation lines of an open paragraph

The block-open column for a block-open-eligible position is the current block margin:

  • at top level, the current block margin is column 0
  • inside a nested block context, the current block margin is the structural indentation column of

that nested context


4.2 Block Types

Headings

Rules:

  • MUST start at the current block margin
  • MUST have space after #
  • Single line only

Paragraphs

  • One or more lines
  • Separated by blank line
  • Inline parsing allowed
  • A paragraph continuation line may begin with characters that would open a block in a

block-open-eligible position; those characters are treated as paragraph text instead


Lists

Unordered

Ordered

Rules:

  • MUST have space after marker
  • MUST be contiguous
  • One or more consecutive non-blank lines immediately after a list item head that begin at the

exact two-space item content margin continue the item head paragraph

  • An unindented line immediately after a list item head ends the list and is parsed by the enclosing

block context

  • Nested blocks MUST be indented by exactly two spaces
  • Tabs are invalid
  • Nested lists require a blank line before them
  • Ordered lists MAY start at any non-negative decimal integer, but each following item in the same

source list MUST increase by exactly one

  • A nested block intended for a list item's inner block context MUST begin exactly at that inner

context's current block margin

  • Additional structural indentation beyond that exact nested block margin is invalid in Core v1

Blockquotes

Rules:

  • > MUST be followed by space or end-of-line
  • Blank > line separates paragraphs
  • A blockquote establishes an inner block context
  • Within that inner context, the current block margin is the column immediately after the > marker

and optional following space on each quote line

  • A nested block inside a blockquote is valid only when the enclosing quoted position is

block-open eligible

  • A blank > line preserves the blockquote context while separating quoted paragraphs
  • A nested block intended for a blockquote's inner context MUST begin exactly at that inner current

block margin

  • Additional structural indentation beyond that exact nested block margin is invalid in Core v1

Code Blocks

Rules:

  • Open/close must be at the current block margin
  • Content is opaque
  • No parsing inside
  • MUST be closed

Tables

A B
1 2

Rules:

  • MUST start at the current block margin after a blank line
  • Header row REQUIRED
  • Separator row REQUIRED
  • Separator cells MUST be exactly ---
  • All rows MUST have same column count
  • Once a table is recognized, every subsequent body row MUST conform to the recognized table shape
  • A non-conforming body row MUST cause parse failure rather than fallback to paragraph text
  • Table continuation is container-local: rows from a different enclosing block context or block

margin MUST NOT continue a recognized table

  • Inline parsing allowed inside cells
  • Block content forbidden inside cells
  • Pipes MUST be escaped: \|

Horizontal Rule

Rules:

  • MUST be standalone line
  • Unambiguous with table separator rows: a TableSepRow always starts with |, so a line

beginning with --- (no leading |) is always a HorizontalRule


Extension Blocks

Extension blocks MAY be followed by an adjacent fallback block:

Rules:

  • MUST start at the current block margin
  • MUST be closed
  • MUST NOT be nested (v1)
  • Extension content between opener and closer is opaque
  • No parsing inside extension content
  • fallback is a reserved extension name
  • A +++fallback block is valid only when it immediately follows an extension block with no blank

line or intervening block

  • Only one fallback block may apply to an extension block
  • Fallback block content is parsed as ordinary &ND content
  • A fallback block cannot have its own fallback
  • A +++fallback block found anywhere else MUST fail with orphan_fallback_block
  • Extension names MUST match the lowercase grammar exactly in strict mode; uppercase characters

MUST cause parse failure

  • Extension opener recognition is local to the current block margin and validated extension-name

grammar; a line that does not satisfy that opener grammar is parsed by the ordinary block and paragraph rules instead

Extension name grammar:


5. Inline Model

The inline syntax is intentionally explicit and context-stable. [* ...] is always strong and [$ ...] is always inline code. The same character sequence MUST NOT change meaning based on surrounding prose or parser mode.

5.1 Syntax


5.2 Supported Types


5.3 Rules

Nesting

  • Must form a proper tree
  • Overlapping spans are invalid

Inline Code

Rules:

  • Content is delimiter-opaque rather than tree-opaque
  • No nested inline-node parsing occurs inside inline code
  • Inside inline code, only \], \\, \[, and \| are recognized escapes
  • Escape resolution inside inline code MUST occur before closing-delimiter detection
  • Any unescaped ] terminates the inline code construct
  • MUST be closed

Rules:

  • URL required
  • Label required
  • | is delimiter
  • \| escapes pipe
  • The label MUST contain at least one inline node or text character

Unknown Inline Types

MUST error.


5.4 Symbol Taxonomy

Block symbols

Inline symbols

Reserved — do not use in Core v1

Design rule

A symbol should mean one thing across &ND.

# means heading at block level. [# ...] therefore MUST NOT be repurposed for an unrelated inline meaning. Reserved symbols remain unassigned so that any future assignment can stay aligned with their natural meaning.

Lock

Core v1 recognizes exactly four inline symbols:

Everything else is reserved until there is a strong need and a consistent assignment.


6. Escaping

Escape character:

Supported escapes:

Rules:

  • Escape applies to next character only
  • Escaping invalid characters MUST error
  • Escape resolution is lexical and local: it happens during inline scanning, before any escaped

character can be interpreted as syntax

  • There is no later global "escape resolution" rewrite pass

7. Scanner And Parser Model

The normative parser model is:

  1. Line normalization

Normalize line endings to logical lines for parsing.

  1. Top-level block scan

Scan lines left-to-right and recognize block openers only when the parser is block-open eligible.

  1. Raw islands first

Code blocks and extension blocks are detected before inline parsing and consumed as opaque raw regions.

  1. Inline scan

Inline parsing is a local left-to-right scan inside inline-capable regions. Escapes are resolved lexically during that scan.

Normative constraints:

  • Implementations MUST NOT rely on unbounded backtracking.
  • Implementations MUST use bounded lookahead only.
  • Block recognition MUST depend only on local parser state plus the current line, except for table

recognition which may inspect the immediately following separator line.

  • Inline parsing MUST be local to the current inline-capable region and MUST NOT depend on global

document state.

This model is intentionally stricter than forgiving markup parsers. The goal is deterministic, auditable parsing rather than permissive recovery.


7.1 Normative Scanner Rules

The following scanner rules are normative.

7.1.1 Line classes

Before block parsing, an implementation MUST classify each physical line using only:

  • the normalized line text
  • whether the parser is block-open eligible
  • bounded local lookahead where explicitly allowed

There is no global reinterpretation pass.

7.1.2 Block-open eligible positions

A line is block-open eligible only:

  • at document start
  • immediately after a blank line
  • inside a list item or blockquote only where that enclosing block explicitly permits a nested block

A paragraph continuation line is never block-open eligible.

7.1.3 Raw-island scanning

When a code block opener or extension block opener is recognized:

  • the opener line is consumed immediately
  • subsequent lines are treated as opaque raw payload
  • parsing resumes only at the first valid closing fence at the same block margin as the opener
  • if no closer exists before EOF, parsing MUST fail
  • if a closing fence token appears at a different block margin, parsing MUST fail with the

corresponding bad-closing-margin error

No inline parsing, block parsing, or escape reinterpretation occurs inside raw payload.

7.1.4 Table recognition

Table recognition is the only block form that may inspect the immediately following line to confirm the current line's role.

A table starts only if:

  • the current line is block-open eligible
  • the line begins at the current block margin
  • the current line matches TableRow
  • the immediately following line matches TableSepRow

Otherwise the current line is parsed according to the normal block/paragraph rules.

Once a table has been recognized, subsequent candidate body rows are part of that table context. A row that does not conform to the established table shape MUST fail the parse; implementations MUST NOT terminate the table early and reinterpret that row as ordinary paragraph text.

7.1.5 Inline scanning

Inline-capable regions are scanned left-to-right.

At each position:

  • if the next bytes form a valid escape, emit the escaped literal character
  • else if the next bytes form a valid inline opener, parse that inline form
  • else emit plain text until the next candidate escape or inline opener

Unknown inline openers MUST fail immediately. Inside inline code, escape recognition occurs before closing-delimiter detection, and any unescaped ] closes the inline code region.

7.1.6 Failure points

Parsing MUST fail immediately on:

  • invalid escape
  • unknown inline tag
  • unclosed inline construct
  • unclosed raw block
  • invalid indentation where indentation is structural
  • table-shape mismatch
  • budget exhaustion

Strict mode produces no partial document after any such failure. Escaping a non-escapable character is a syntax error, not a literal fallback.

7.1.7 Worked edge cases

Case A: paragraph vs ordered list

Result:

  • one paragraph with two lines
  • the second line is paragraph text because the parser is not block-open eligible there

Case B: ordered list after blank line

Result:

  • one paragraph
  • one ordered list

Case C: horizontal rule vs paragraph text

Result:

  • invalid as a single paragraph continuation
  • if --- is intended as a horizontal rule, a blank line is required before it

Canonical valid form:

Case D: table requires separator confirmation

Result:

  • not a table
  • if the parser is block-open eligible there, the two lines form one paragraph
  • if the enclosing context does not allow paragraph text at that position, parsing fails according

to that enclosing context

Case E: escaped inline opener

Result:

  • literal text [* not strong]
  • not a strong node

Case F: unknown inline tag

Result:

  • fail with unknown_inline_type

Case G: raw block swallowing apparent syntax

Result:

  • one code block
  • all interior lines are opaque payload

Case H: unterminated extension block

Result:

  • fail with unclosed_extension_block

Result:

  • if the configured link-target budget is exceeded, fail with nd_budget_exceeded
  • implementations MUST stop scanning once the budget boundary is crossed

7.1.8 Conformance Test Seeds

The following seeds are normative conformance fixtures.

An implementation MAY add more tests, but it SHOULD include at least these cases in its conformance suite.

seed-paragraph-vs-ordered-list

Input:

Expected result:

  • parse success
  • one paragraph block
  • paragraph contains two lines of text

seed-header-standalone

Input:

Expected result:

  • parse success
  • header is recognized as a document declaration
  • header does not appear as an AST block
  • one heading block

seed-invalid-header-version

Input:

Expected result:

  • parse failure
  • reason: unsupported or malformed document header

seed-ordered-list-after-blank-line

Input:

Expected result:

  • parse success
  • one paragraph block
  • one ordered list block with one item

seed-ordered-list-renumber-canonical

Input:

Expected result:

  • parse success
  • one ordered list block
  • source numbers are a valid monotonic sequence
  • canonical emission renumbers ordered list items from 1

seed-ordered-list-skipped-number

Input:

Expected result:

  • parse failure
  • reason: ordered list item numbers must increase by exactly one within the source list

seed-horizontal-rule-needs-boundary

Input:

Expected result:

  • parse failure
  • reason: block opener attempted on a paragraph continuation line

seed-inline-line-break

Input:

Expected result:

  • parse failure
  • error code unknown_inline_type
  • [.] remains reserved in Core v1

seed-table-requires-separator

Input:

Expected result:

  • parse success when evaluated at top level
  • one paragraph block with two lines
  • no table block

seed-table-valid-top-level

Input:

A B
1 2

Expected result:

  • parse success
  • one table block
  • table has two columns and one body row

seed-table-inline-cells

Input:

Name Note
[* Ada] escaped \ pipe

Expected result:

  • parse success
  • one table block
  • first body cell contains one strong inline node
  • second body cell contains a literal pipe character

seed-table-mismatched-body-row

Input:

A B
1 2 3

Expected result:

  • parse failure
  • reason: table body row has a different cell count from the header row

seed-table-missing-body-row

Input:

Expected result:

  • parse failure
  • reason: table has no body rows

seed-table-unescaped-pipe-extra-cell

Input:

A B
escaped pipe ok

Expected result:

  • parse failure
  • reason: unescaped pipe creates an extra cell and violates table shape

seed-table-heading-instead-of-body-row

Input:

Expected result:

  • parse failure
  • error code invalid_table_shape
  • reason: once a table is recognized, a following heading line cannot terminate the table early

seed-escaped-inline-opener

Input:

Expected result:

  • parse success
  • plain text only
  • no strong inline node

seed-unknown-inline-tag

Input:

Expected result:

  • parse failure
  • error code unknown_inline_type

seed-raw-block-opaque

Input:

Expected result:

  • parse success
  • one code block
  • payload contains the interior lines exactly, subject only to line-ending normalization

seed-ordered-code-block

Input:

Expected result:

  • parse success
  • one code block
  • code block is ordered
  • payload contains the interior lines exactly, subject only to line-ending normalization

seed-ordered-code-block-wrong-closing-fence

Input:

Expected result:

  • parse failure
  • error code unclosed_code_block
  • reason: ordered code blocks require a matching quadruple-fence closer

seed-blockquote-ordered-code-block-wrong-closing-fence

Input:

Expected result:

  • parse failure
  • error code unclosed_code_block
  • reason: a quoted ordered code block still requires a matching quadruple-fence closer

seed-list-item-ordered-code-block-wrong-closing-fence

Input:

Expected result:

  • parse failure
  • error code unclosed_code_block
  • reason: a list-item-contained ordered code block still requires a matching quadruple-fence closer

seed-list-item-blockquote-ordered-code-block-wrong-closing-fence

Input:

Expected result:

  • parse failure
  • error code unclosed_code_block
  • reason: a doubly nested ordered code block still requires a matching quadruple-fence closer

seed-list-item-unclosed-extension-block

Input:

Expected result:

  • parse failure
  • error code unclosed_extension_block
  • reason: a list-item-contained extension block still requires a matching closer at the same nested

margin

seed-blockquote-unclosed-extension-block

Input:

Expected result:

  • parse failure
  • error code unclosed_extension_block
  • reason: a blockquote-contained extension block still requires a matching closer at the same

nested margin

seed-list-item-blockquote-unclosed-extension-block

Input:

Expected result:

  • parse failure
  • error code unclosed_extension_block
  • reason: a doubly nested extension block still requires a matching closer at the innermost

container margin

seed-extension-block-opaque

Input:

Expected result:

  • parse success
  • one extension block
  • extension name is chart/pie
  • payload is opaque and not parsed as inline or block content

seed-extension-block-fallback

Input:

Expected result:

  • parse success
  • one extension block
  • extension opener attaches the extension name directly to the +++ fence
  • primary extension payload is opaque
  • immediately adjacent +++fallback content is parsed as ordinary &ND blocks

seed-orphan-fallback-block

Input:

Expected result:

  • parse failure
  • error code orphan_fallback_block
  • reason: fallback is only valid when directly adjacent to the immediately preceding extension

block

seed-fallback-not-immediately-after-extension

Input:

Expected result:

  • parse failure
  • error code orphan_fallback_block
  • reason: any intervening block makes the fallback orphaned

seed-fallback-crosses-blockquote-boundary

Input:

Expected result:

  • parse failure
  • error code orphan_fallback_block
  • reason: fallback cannot attach across a blockquote container boundary

seed-fallback-crosses-into-blockquote-boundary

Input:

Expected result:

  • parse failure
  • error code orphan_fallback_block
  • reason: quoted fallback cannot attach to an extension block outside the quoted container

seed-fallback-crosses-list-item-boundary

Input:

Expected result:

  • parse failure
  • error code orphan_fallback_block
  • reason: fallback cannot attach across a list-item container boundary

seed-fallback-crosses-into-list-item-boundary

Input:

Expected result:

  • parse failure
  • error code orphan_fallback_block
  • reason: nested list-item fallback cannot attach to an extension block outside the list item

seed-list-item-blockquote-fallback-not-immediately-after-extension

Input:

Expected result:

  • parse failure
  • error code orphan_fallback_block
  • reason: fallback adjacency is local to the immediately preceding extension block even inside

nested blockquote/list-item containers

seed-blockquote-fallback-not-immediately-after-extension

Input:

Expected result:

  • parse failure
  • error code orphan_fallback_block
  • reason: fallback adjacency remains local to the immediately preceding extension block inside the

same blockquote container

seed-nested-fallback-block

Input:

Expected result:

  • parse failure
  • error code nested_fallback_block
  • reason: fallback content is parsed as ordinary &ND, but extension blocks parsed inside

fallback content cannot themselves receive fallback blocks

seed-unclosed-extension-block

Input:

Expected result:

  • parse failure
  • error code unclosed_extension_block

seed-extension-uppercase-name

Input:

Expected result:

  • parse failure
  • error code invalid_extension_name
  • reason: extension names are lowercase in strict mode

seed-invalid-escape

Input:

Expected result:

  • parse failure
  • error code invalid_escape

seed-budget-block-count-at-limit

Input:

Expected result:

  • parse success when whole-document block-count equals the configured budget
  • budget limit is inclusive

seed-budget-block-count

Input:

Expected result:

  • parse failure if configured whole-document block-count budget is exceeded
  • error code nd_budget_exceeded
  • budget failure occurs as blocks are created

seed-budget-code-block-size-at-limit

Input:

Expected result:

  • parse success when raw code block payload size equals the configured budget
  • budget limit is inclusive

seed-budget-code-block-size

Input:

Expected result:

  • parse failure if configured raw block payload budget is exceeded
  • error code nd_budget_exceeded
  • budget failure occurs while scanning code block payload

seed-budget-document-size-at-limit

Input:

Expected result:

  • parse success when normalized document size equals the configured budget
  • budget limit is inclusive

seed-budget-document-size

Input:

Expected result:

  • parse failure if configured document-size budget is exceeded
  • error code nd_budget_exceeded
  • scanner rejects before unrestricted block parsing

seed-budget-extension-block-size-at-limit

Input:

Expected result:

  • parse success when extension block payload size equals the configured budget
  • budget limit is inclusive

seed-budget-extension-block-size

Input:

Expected result:

  • parse failure if configured extension block payload budget is exceeded
  • error code nd_budget_exceeded
  • budget failure occurs while scanning opaque extension payload

seed-budget-inline-depth-at-limit

Input:

Expected result:

  • parse success when inline nesting depth equals the configured budget
  • budget limit is inclusive

seed-budget-inline-depth

Input:

Expected result:

  • parse failure if configured inline nesting-depth budget is exceeded
  • error code nd_budget_exceeded
  • budget failure occurs when entering nested inline content

seed-budget-line-length-at-limit

Input:

Expected result:

  • parse success when physical line length equals the configured budget
  • budget limit is inclusive

seed-budget-line-length

Input:

Expected result:

  • parse failure if configured physical-line-length budget is exceeded
  • error code nd_budget_exceeded
  • scanner rejects during line validation

Input:

Expected result:

  • parse success when link target length equals the configured budget
  • budget limit is inclusive

Input:

Expected result:

  • parse failure if configured link-target budget is exceeded
  • error code nd_budget_exceeded
  • scanner stops at the budget boundary rather than consuming unbounded input

seed-budget-list-item-count-at-limit

Input:

Expected result:

  • parse success when whole-document list-item count equals the configured budget
  • budget limit is inclusive

seed-budget-list-item-count

Input:

Expected result:

  • parse failure if configured list-item budget is exceeded
  • error code nd_budget_exceeded
  • budget failure occurs as list items are recognized

seed-budget-list-item-count-whole-document

Input:

Expected result:

  • parse failure if configured list-item budget is exceeded across separate lists
  • error code nd_budget_exceeded
  • list-item budget is a whole-document budget

seed-budget-nesting-depth-at-limit

Input:

Expected result:

  • parse success when block nesting depth equals the configured budget
  • budget limit is inclusive

seed-budget-nesting-depth

Input:

Expected result:

  • parse failure if configured block nesting-depth budget is exceeded
  • error code nd_budget_exceeded
  • budget failure occurs when entering a nested block context

seed-budget-table-columns-at-limit

Input:

A B
1 2

Expected result:

  • parse success when table column count equals the configured budget
  • budget limit is inclusive

seed-budget-table-columns

Input:

A B C
1 2 3

Expected result:

  • parse failure if configured table-column budget is exceeded
  • error code nd_budget_exceeded
  • budget failure occurs before unrestricted table cell parsing

seed-nested-list-two-space-indent

Input:

Expected result:

  • parse success
  • one unordered list block
  • first parent item contains a nested unordered list with two child items
  • second parent item is a sibling of the first parent item

seed-nested-list-deep

Input:

Expected result:

  • parse success
  • one unordered list block
  • nested lists recurse through all four levels
  • each nested level still depends on a blank line plus the exact two-space content margin

seed-list-item-paragraph-continuation-exact-margin

Input:

Expected result:

  • parse success
  • one unordered list block
  • one list item
  • item contains one paragraph with two source lines
  • continuation line starts at the exact two-space list-item content margin

seed-list-item-unindented-line-ends-list

Input:

Expected result:

  • parse success
  • one unordered list block
  • one following top-level paragraph
  • unindented text immediately after a list item ends the list

seed-list-item-nested-paragraph-exact-margin

Input:

Expected result:

  • parse success
  • one ordered list block
  • second item contains a nested paragraph
  • nested paragraph starts at the exact two-space list-item inner margin

seed-list-item-unindented-paragraph-after-blank

Input:

Expected result:

  • parse success
  • one ordered list block
  • one following top-level paragraph
  • unindented text after a blank line is not captured into the previous list item

seed-nested-list-invalid-indent-one-space

Input:

Expected result:

  • parse failure
  • reason: structural indentation is exactly two spaces in Core v1

seed-nested-list-invalid-indent-tab

Input:

Expected result:

  • parse failure
  • reason: tabs are invalid where indentation is structural

seed-nested-list-needs-blank-line

Input:

Expected result:

  • parse failure
  • reason: nested blocks require a blank line before the nested block in Core v1

seed-blockquote-two-paragraphs

Input:

Expected result:

  • parse success
  • one blockquote block
  • blockquote contains two paragraph children

seed-blockquote-extension-fallback

Input:

Expected result:

  • parse success
  • one blockquote block
  • blockquote contains one paragraph child followed by one nested extension block child
  • fallback belongs to that immediately preceding extension block within the quoted container

seed-blockquote-escaped-inline

Input:

Expected result:

  • parse success
  • one blockquote block
  • quoted paragraph contains plain text [* not strong]
  • no strong inline node

seed-list-item-raw-block

Input:

Expected result:

  • parse success
  • one unordered list block
  • first item contains one nested code block
  • code payload is raw

seed-list-item-extension-block

Input:

Expected result:

  • parse success
  • one unordered list block
  • first item contains one nested extension block
  • extension payload is apples: 3

seed-list-item-heading

Input:

Expected result:

  • parse success
  • one unordered list block
  • first item contains one nested heading block
  • heading level is 2

seed-list-item-horizontal-rule

Input:

Expected result:

  • parse success
  • one unordered list block
  • first item contains one nested horizontal rule block

seed-list-item-table

Input:

Expected result:

  • parse success
  • one unordered list block
  • first item contains one nested table block
  • table has two columns and one body row

seed-list-item-table-missing-separator

Input:

Expected result:

  • parse success
  • one unordered list block
  • first item contains one paragraph block with two lines
  • no nested table block

seed-list-item-table-mismatched-body-row

Input:

Expected result:

  • parse failure
  • error code invalid_table_shape
  • reason: nested table body row has a different cell count from the header row and MUST fail

rather than falling back to paragraph text

seed-list-item-table-crosses-boundary

Input:

Expected result:

  • parse failure
  • error code invalid_table_shape
  • reason: a nested table inside a list item cannot continue with a body row outside the list-item

container

seed-list-item-table-horizontal-rule-instead-of-body-row

Input:

Expected result:

  • parse failure
  • error code invalid_table_shape
  • reason: a nested table inside a list item cannot terminate early and reinterpret a following

horizontal rule as a sibling nested block

seed-list-item-raw-block-bad-closing-margin

Input:

Expected result:

  • parse failure
  • reason: raw block closer must appear at the same block margin as the opener

seed-list-item-extension-block-bad-closing-margin

Input:

Expected result:

  • parse failure
  • error code extension_block_bad_closing_margin
  • reason: extension block closer must appear at the same block margin as the opener

seed-list-item-blockquote-raw-block-bad-closing-margin

Input:

Expected result:

  • parse failure
  • error code raw_block_bad_closing_margin
  • reason: a raw block closer inside a list-item-contained blockquote must appear at the same inner

block margin as the opener

seed-list-item-blockquote-extension-block-bad-closing-margin

Input:

Expected result:

  • parse failure
  • error code extension_block_bad_closing_margin
  • reason: an extension block closer inside a list-item-contained blockquote must appear at the same

inner block margin as the opener

seed-list-item-blockquote

Input:

Expected result:

  • parse success
  • one unordered list block
  • first item contains one nested blockquote block
  • blockquote contains one paragraph child

seed-list-item-blockquote-two-paragraphs

Input:

Expected result:

  • parse success
  • one unordered list block
  • first item contains one nested blockquote block
  • blockquote contains two paragraph children

seed-list-item-blockquote-extension-fallback

Input:

Expected result:

  • parse success
  • one unordered list block
  • first item contains one nested blockquote block
  • blockquote contains one paragraph child followed by one nested extension block child
  • nested extension fallback content is parsed as ordinary &ND inside the same blockquote context

seed-blockquote-nested-list

Input:

Expected result:

  • parse success
  • one blockquote block
  • blockquote contains one paragraph child followed by one nested unordered list child

seed-blockquote-nested-list-needs-margin

Input:

Expected result:

  • parse success
  • one blockquote block containing one paragraph child
  • one top-level unordered list block
  • the list is not nested in the blockquote because it does not begin at the blockquote's current block margin

seed-blockquote-table-mismatched-body-row

Input:

Expected result:

  • parse failure
  • error code invalid_table_shape
  • reason: a nested table inside a blockquote uses the same recognized table shape rules and MUST

fail rather than falling back to quoted paragraph text

seed-blockquote-table-crosses-boundary

Input:

> A B
> --- ---
1 2

Expected result:

  • parse failure
  • error code invalid_table_shape
  • reason: a nested table inside a blockquote cannot continue with a body row outside the quoted

container

seed-blockquote-table-horizontal-rule-instead-of-body-row

Input:

Expected result:

  • parse failure
  • error code invalid_table_shape
  • reason: a nested table inside a blockquote cannot terminate early and reinterpret a following

quoted horizontal rule as a sibling block

seed-blockquote-nested-code-block

Input:

Expected result:

  • parse success
  • one blockquote block
  • blockquote contains one paragraph child followed by one nested code block child
  • code payload is raw

seed-blockquote-nested-extension-block

Input:

Expected result:

  • parse success
  • one blockquote block
  • blockquote contains one paragraph child followed by one nested extension block child
  • extension payload is apples: 3

seed-blockquote-raw-block-bad-closing-margin

Input:

Expected result:

  • parse failure
  • reason: raw block closer must appear at the same block margin as the opener inside the enclosing blockquote

seed-blockquote-extension-block-bad-closing-margin

Input:

Expected result:

  • parse failure
  • error code extension_block_bad_closing_margin
  • reason: extension block closer must appear at the same block margin as the opener inside the enclosing blockquote

seed-list-item-blockquote-then-sibling

Input:

Expected result:

  • parse success
  • one unordered list block
  • first item contains one nested blockquote child
  • second parent item is a sibling at the outer list level

seed-list-item-blockquote-table-crosses-boundary

Input:

Expected result:

  • parse failure
  • error code invalid_table_shape
  • reason: a doubly nested table inside a list-item-contained blockquote cannot continue at the

outer list-item margin

seed-list-item-blockquote-table-horizontal-rule-instead-of-body-row

Input:

Expected result:

  • parse failure
  • error code invalid_table_shape
  • reason: a doubly nested table inside a list-item-contained blockquote cannot terminate early and

reinterpret a quoted horizontal rule as a sibling block

seed-blockquote-escaped-inline-opener

Input:

Expected result:

  • parse success
  • one blockquote block
  • quoted paragraph contains plain text [@ https://example.com | label]
  • no link inline node

seed-inline-nested-strong-emphasis

Input:

Expected result:

  • parse success
  • one paragraph block
  • paragraph contains one strong node
  • the strong node contains text plus one nested emphasis node

seed-inline-unclosed-strong

Input:

Expected result:

  • parse failure
  • error code unclosed_inline

seed-inline-strong-newline

Input:

Expected result:

  • parse failure
  • error code unclosed_inline
  • reason: inline strong node crosses a line boundary before closing

seed-inline-unclosed-nested

Input:

Expected result:

  • parse failure
  • error code unclosed_inline

seed-inline-unexpected-closing

Input:

Expected result:

  • parse failure
  • error code unexpected_closing

seed-inline-overlap-attempt

Input:

Expected result:

  • parse success
  • one strong node containing one nested emphasis node and trailing text
  • nesting is strictly tree-structured, not overlapping

seed-inline-code-opaque-brackets

Input:

Expected result:

  • parse success
  • one inline code node
  • code payload is [* not strong] [@ x | y]
  • no nested strong or link nodes inside code

Input:

Expected result:

  • parse success
  • one link node with the given target
  • link label contains one nested strong node

Input:

Expected result:

  • parse failure
  • reason: link label is required in Core v1

Input:

Expected result:

  • parse failure
  • reason: link target is required in Core v1

Input:

Expected result:

  • parse success
  • one link node
  • target contains the literal pipe character |

seed-inline-nbsp

Input:

Expected result:

  • parse failure
  • error code unknown_inline_type
  • [_] is not assigned in Core v1

seed-inline-reserved-anchor-tag

Input:

Expected result:

  • parse failure
  • error code unknown_inline_type
  • [# ...] remains reserved in Core v1

seed-inline-reserved-reference-tag

Input:

Expected result:

  • parse failure
  • error code unknown_inline_type
  • [~ ...] remains reserved in Core v1

seed-inline-reserved-admonition-tag

Input:

Expected result:

  • parse failure
  • error code unknown_inline_type
  • [! ...] remains reserved in Core v1

seed-inline-reserved-footnote-tag

Input:

Expected result:

  • parse failure
  • error code unknown_inline_type
  • [^ ...] remains reserved in Core v1

seed-inline-reserved-todo-marker

Input:

Expected result:

  • parse failure
  • error code unknown_inline_type
  • [x] remains reserved in Core v1

seed-inline-reserved-todo-unchecked-marker

Input:

Expected result:

  • parse failure
  • error code unknown_inline_type
  • [ ] remains reserved in Core v1

seed-inline-reserved-todo-in-progress-marker

Input:

Expected result:

  • parse failure
  • error code unknown_inline_type
  • [=] remains reserved in Core v1

seed-inline-reserved-todo-cancelled-marker

Input:

Expected result:

  • parse failure
  • error code unknown_inline_type
  • [;] remains reserved in Core v1

seed-inline-invalid-escape-in-code

Input:

Expected result:

  • parse failure
  • error code invalid_escape

seed-source-spans-table-inline

Input:

Expected result:

  • parse success
  • optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
  • table block span covers the full table source range
  • inline spans inside table cells point at trimmed cell content
  • escaped pipes inside table cells remain part of the cell source span
  • nested inline spans inside table cells point at their exact source ranges

seed-source-spans-extension-fallback

Input:

Expected result:

  • parse success
  • optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
  • extension block span covers only the primary opaque extension block, not the attached fallback
  • fallback paragraph spans point at parsed fallback content after the reserved +++fallback opener
  • nested inline spans inside fallback content point at their exact source ranges

seed-source-spans-list-item-blockquote

Input:

Expected result:

  • parse success
  • optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
  • list item spans cover the full nested block range belonging to that item
  • nested blockquote spans include both quoted paragraphs but not the blank line before the nested block
  • quoted paragraph spans point at the trimmed inner content after the list and blockquote margins

seed-source-spans-list-item-table

Input:

Expected result:

  • parse success
  • optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
  • nested table span begins at the trimmed inner block margin inside the list item
  • inline spans inside nested table cells point at trimmed cell content after both list and table

margin stripping

  • nested inline spans inside list-contained table cells point at their exact source ranges

seed-source-spans-blockquote-table

Input:

Expected result:

  • parse success
  • optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
  • nested table span begins at the trimmed inner block margin inside the blockquote
  • inline spans inside nested quoted table cells point at trimmed cell content after both blockquote

and table margin stripping

  • nested inline spans inside blockquote-contained table cells point at their exact source ranges

seed-source-spans-list-item-extension-fallback

Input:

Expected result:

  • parse success
  • optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
  • nested extension block span covers only the primary opaque block inside the list item, not the

attached fallback

  • fallback paragraph spans begin at the trimmed inner block margin after the list item and reserved

fallback opener

  • nested inline spans inside list-contained fallback content point at their exact source ranges

seed-source-spans-blockquote-extension-fallback

Input:

Expected result:

  • parse success
  • optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
  • nested extension block span covers only the primary opaque block inside the blockquote, not the

attached fallback

  • fallback paragraph spans begin at the trimmed inner block margin after the blockquote and reserved

fallback opener

  • nested inline spans inside blockquote-contained fallback content point at their exact source ranges

seed-source-spans-list-item-blockquote-table

Input:

Expected result:

  • parse success
  • optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
  • nested blockquote span begins at the trimmed inner block margin inside the list item
  • nested table span begins at the trimmed inner block margin after both list-item and blockquote

stripping

  • inline spans inside doubly nested table cells point at their exact source ranges

seed-source-spans-list-item-blockquote-extension-fallback

Input:

Expected result:

  • parse success
  • optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
  • nested extension block span covers only the primary opaque block after both list-item and

blockquote stripping

  • fallback paragraph spans begin at the trimmed inner block margin after the list item, blockquote,

and reserved fallback opener

  • nested inline spans inside doubly nested fallback content point at their exact source ranges

seed-source-spans-list-item-blockquote-paragraph-then-table

Input:

Expected result:

  • parse success
  • optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
  • nested blockquote span covers both its leading paragraph and following structured child
  • the paragraph span ends before the blank quoted separator line while the nested table span begins

after it

  • inline spans inside the following nested table remain exact after both list-item and blockquote

margin stripping

seed-source-spans-list-item-blockquote-code-block

Input:

Expected result:

  • parse success
  • optional source-span CTS metadata checks may be evaluated separately from semantic AST equality
  • nested blockquote spans begin at the trimmed inner block margin inside the list item
  • nested code block spans begin at the trimmed inner block margin after both list-item and

blockquote stripping

  • raw block spans cover the full fenced source region rather than only the payload lines

seed-source-spans-ordered-code-block

Input:

Normative note:

Lists

List note:

Blockquotes

Blockquote note:

Code blocks

RawLines is implementation-defined scanning: consume all text until the first line matching the corresponding closing fence at the same block margin. Triple backticks produce a plain code block. Quadruple backticks produce an ordered code block whose payload lines retain their raw text while explicitly requesting line ordering in downstream projections.

Extension blocks

RawLines is opaque: no inline parsing, no block parsing, no nesting in v1. If present, FallbackBlock MUST be directly adjacent to the preceding extension close and is parsed as ordinary &ND block content. fallback is a reserved extension name and MUST NOT be interpreted as a general extension block.

Tables

Semantic table rule:

Inline content

Inline code rule:

Block margin note

Text and escaping

Identifiers

Normative parser rules not fully captured by EBNF