Cutdown Diagnostics Policy

Goal

Diagnostics must be machine-readable, stable, and non-blocking for normal authoring workflows.

Output Contract

A parser returns:

  • ast
  • diagnostics[]

Each diagnostic includes:

  • code (stable identifier, for example CDN-XXXX)
  • level (error | warning | info)
  • message (human-readable)
  • loc ({ file?, start, end } — raw-file UTF-16 code-unit offsets, end-exclusive, per spec §14 Location Type; line/column presentation is derived by the reporting tool)
  • recovery (how parsing proceeded)

Severity Rules

  • error: malformed or invalid input requiring fallback/recovery, but parser still returns AST.
  • warning: valid parse with potentially unintended author input.
  • info: non-problem metadata signals.

The parser must not fail-fast on the first issue. It must continue with deterministic recovery and report all encountered diagnostics for the parse pass.

Recovery Rules

  • Recovery behavior must be deterministic and documented per diagnostic code.
  • If a construct is invalid/unclosed, parser follows the spec fallback rule and emits corresponding diagnostics.
  • Recovery must preserve as much surrounding valid structure as possible.

Stability Requirements

  • Diagnostic code values are part of the language contract.
  • Changing meaning of an existing diagnostic code requires at least a minor policy review and migration note.
  • Removing a diagnostic code requires major-version justification.

Diagnostic code definitions and lifecycle are governed by ./diagnostic-code-registry-policy.md.

Conformance Requirements

Conformance tests must assert:

  • expected AST output,
  • expected diagnostic codes and levels,
  • expected recovery mode for malformed cases.

Silent-Drop Warning Cases

The following spec-defined recovery behaviors MUST emit a warning-level diagnostic. See the Diagnostic Code Registry for full trigger/recovery definitions.

Situation Code Recovery
Unclosed CodeBlock fence (```) CDN-0001 Content runs to end of document
Unclosed MetaBlock fence (~~~) CDN-0002 Content runs to end of document
Unclosed MathBlock fence ($$$) CDN-0003 Content runs to end of document
Unclosed NamedBlock (:::name) CDN-0004 Content runs to end of document
PageBreaker tail dropped CDN-0016 Everything after the leading --- is discarded; page boundary takes effect
Page separator inside block container CDN-0017 --- line emitted as literal Paragraph; no page boundary
Excess scope-chain {...} orphaned CDN-0011 Excess {...} at front of chain discarded; no AST output
Heading level > 9 (10+ = signs) CDN-0012 Entire line emitted as literal Text
::: not followed by [ID_LITERAL] (nameless opener) CDN-0013 Block candidate parsed as Paragraph; ::: and {attrs} emitted as literal text
Duplicate id token (#id or id= after first claim) CDN-0020 Duplicate dropped; first value kept
class= alongside .class syntax CDN-0021 class= dropped; .class tokens kept
Duplicate custom attribute key CDN-0022 Duplicate dropped; first value kept
~~~ fence inside a block container CDN-0030 Raw span (including fence lines) emitted as literal Paragraph; no Meta node created
Crossed inline boundaries (** __ … ** … __) CDN-0014 Greedy parse unchanged; diagnostic only — span on the crossing closer
Mark opener beyond nesting depth 8 CDN-0031 Opener degrades to literal Text("::" + name); enclosing Mark nodes unaffected

Strict parser profiles (per ./parser-profile-policy.md) MAY upgrade any warning to error.