1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//! **The Newt Markup Markdown dialect — one matrix, one constructor.**
//!
//! Before A1 (epic #1803, #1825) the two Markdown parser sites each chose
//! their own undocumented option matrix inline. A1 named both but did not
//! unify them. **C3a (#1857) unified them: there is now exactly one
//! dialect, and no call site can choose otherwise** — [`parse`] is the only
//! way to obtain a parser, so a second matrix is unrepresentable rather
//! than merely discouraged.
//!
//! ## Which side moved, and why
//!
//! The **web narrowed to the canonical set**; `web_enhancement_options`
//! (`Options::all`) is deleted. Four reasons, in order of force:
//!
//! 1. **`Options::all` contained a second implementation of Newt Markup's
//! own envelope.** `ENABLE_PLUSES_DELIMITED_METADATA_BLOCKS` makes
//! pulldown treat a leading `+++` block as metadata, and `push_html`
//! writes metadata blocks as *nothing*. So the epic's own front-matter
//! format vanished silently from the web transcript while the terminal
//! showed it — a second grammar for the envelope
//! [`crate::markup::strip_newt_metadata`] already owns, disagreeing with
//! it, invisibly. `ENABLE_YAML_STYLE_METADATA_BLOCKS` did the same for
//! `---`. Deleting the matrix deletes both.
//! 2. **Byte fidelity.** A transcript is re-sent to the model verbatim.
//! `ENABLE_SMART_PUNCTUATION` rewrote `"` into `“`, `--` into `–`, and
//! `...` into `…` in the view only, so the operator read punctuation the
//! model never emitted and will never see.
//! 3. **Law 5 — unknown content falls back *visibly*.** Under the wide
//! matrix, `# Title {#anchor}` lost `{#anchor}` (consumed into an `id`
//! the sanitizer then stripped) and `$x^2$` lost its delimiters. Both
//! are now plain, readable text on both surfaces.
//! 4. **The other direction was not available.** Widening the canonical set
//! changes what every surface reads in every document and moves A0's
//! frozen terminal goldens; that is a dialect change and belongs to its
//! own reviewed slice, not to a unification.
//!
//! What the web gives up is real and worth naming: footnotes, definition
//! lists, math, heading anchors, and GFM alerts no longer get special
//! rendering there. Each now renders as the literal Markdown that produced
//! it — which is what the terminal has always shown.
//!
//! ## The dialect, specified
//!
//! - **Encoding:** UTF-8 in, UTF-8 out (`&str` end to end). Line endings are
//! not normalized by Newt; pulldown-cmark treats `\r\n` per CommonMark.
//! - **Extensions:** GFM strikethrough, task lists, and tables over
//! CommonMark — nothing else, on every surface. No smart punctuation, no
//! footnotes, no heading attributes, no metadata blocks (the `+++`
//! envelope is [`crate::markup`]'s, not the Markdown parser's).
//! - **Raw HTML:** parsed as `Event::Html` per CommonMark. What a view does
//! with it is a rendering decision, not a dialect one — see below.
//! - **Fenced code:** never interpreted — a fence's info string selects
//! syntax presentation only. The `mermaid` info string is a web-side
//! progressive enhancement (`newt-web/src/shell.rs`); E0 will move it
//! behind the extension registry with mandatory source fallback.
//! - **Unknown extensions:** there is no inline-extension syntax in v1;
//! unknown fenced-block info strings fall back to plain code
//! presentation, visibly (ADR law 5).
//!
//! ## The two sanctioned rendering-side divergences
//!
//! One dialect, two media. These are **not** dialect forks — both surfaces
//! see the same events and disagree only about how to draw them — and they
//! are exhaustive. `newt-web`'s `shell::tests::c3a` pins both, so a third
//! difference cannot appear on the excuse that the web already differs.
//!
//! 1. **Soft breaks.** CommonMark folds them. The ANSI scroller folds to a
//! space; the web emits `<br>`, because chat text uses single newlines
//! meaningfully and a page has no other way to keep them.
//! 2. **Raw HTML.** The scroller has no DOM, so it prints markup as literal
//! text and executes nothing. A page *has* a DOM, so it must remove what
//! it will not execute — everything is allowlist-sanitized through
//! `ammonia`. Same event, opposite correct handling.
use ;
/// The canonical Newt Markup dialect: CommonMark + GFM strikethrough, task
/// lists, and tables. The plain/TUI renderer parses with exactly this set.
/// Parse `src` in the Newt Markup dialect.
///
/// **The only parser constructor in the repository.** Options are not a
/// parameter, because a call site that can choose them is a call site that
/// can fork the dialect — which is exactly what happened before C3a, twice,
/// undocumented. Prefer making the bug unrepresentable over catching it in
/// review; `markup_sprawl_ratchet::c3a` holds the line mechanically.