spec: design-ledger-formal-links — corpus-grounded amendment (clause-5 fence-skip + closed enumeration)
Plan-time corpus verification surfaced a third distinct gap the brainstorm-sample missed: data-model.md:38/66/79/206/226 are 'see §"..."' annotations inside fenced jsonc code blocks (verified: fences at 30-87 and 203-228; the 5 refs fall inside), where a Markdown link is literal text — not a navigable link on any renderer. Per the "two+ surfaced ⇒ ground the spec properly once, don't iter-patch" discipline: amendment 2 is the definitive corpus-grounded pass, not a fourth-patch returning later. Every convert-set ref's prose-vs-fence status + exact bytes personally verified before amending. - clause-5 (Concrete a): adds a strip_fences helper toggling on triple-backtick / tilde lines; the per-file scan runs targets(&strip_fences(&raw)); the RED-first synthetic vector gains two fence-skip assertions (identity-stub of strip_fences ⇒ first assert fails ⇒ genuine RED). Contract grows from 3 to 4 predicates (+ fenced-code-not-scanned). - Scope section: third out-of-scope carve-out — a 'see §"..."' inside a fenced code block is schema-example documentation = the inline- annotation analog of the nominal-mention carve-out (a // comment in a code example is code documentation, not a 'go browse there' pointer). Clean extension of the converged navigational-vs-nominal principle. - Acceptance 3: convert-set CLOSED and exhaustive — 8 prose refs (float-semantics:69/100, embedding-abi:45, memory-model:44/105, scope-boundaries:48/88), 2 disposition-(b) homeless removals (pipeline:61, authoring-surface:180), 6 stay-prose (data-model in-fence x5, embedding-abi:51). Iter-provenance suffixes stripped on conversion. scope-boundaries:88 splits into a source link plus a Pipeline cross-file link. grounding-check third dispatch on the amended bytes: PASS, all 8 assumptions ratified (incl. corpus-state-verified A4-A7 per the rolesplit precedent for text-enumeration claims).
This commit is contained in:
@@ -148,6 +148,18 @@ every nominal mention of a file or symbol. The discriminator:
|
||||
\`ailang-core::desugar\`` converts to
|
||||
`[desugar](../../crates/ailang-core/src/desugar.rs)` — never for
|
||||
a nominal mention.
|
||||
- **Out of scope (schema-example documentation):** a `see §"…"`
|
||||
annotation *inside a fenced code block* — recon+corpus-confirmed:
|
||||
the `//`-comments in `data-model.md`'s fenced `jsonc` schema
|
||||
examples (lines 38/66/79 inside the 30–87 fence; 206/226 inside
|
||||
the 203–228 fence) — documents the schema example, not the
|
||||
browsable ledger. It is the inline-annotation analog of the
|
||||
nominal-mention carve-out: a `//` comment in a code example is
|
||||
code documentation, not a "go browse there" pointer. It stays as
|
||||
the informal `see §"…"` annotation, untouched. clause-5
|
||||
correspondingly does not scan fenced regions (a `](` inside a
|
||||
fence is literal text, not a navigable link — a gate-correctness
|
||||
requirement independent of this carve-out).
|
||||
|
||||
Two byte-policies the conversion applies (Boss-adjudicated from
|
||||
recon Open Questions; the planner does not re-open them):
|
||||
@@ -203,9 +215,33 @@ fn design_body_links_are_durable_and_resolve() {
|
||||
// structured registry tier resolved by clause-1, commitment 4)
|
||||
// resolves, relative to its CONTAINING file, to an existing
|
||||
// file under design/ or source (crates/** | runtime/**); never
|
||||
// under docs/; never an in-file #anchor. Composes with clause-3:
|
||||
// a surviving cross-reference is a resolving durable file-link
|
||||
// or it is clause-3-forbidden decision-record prose.
|
||||
// under docs/; never an in-file #anchor. Fenced code blocks are
|
||||
// not scanned (a `](` inside ``` is literal text, not a link).
|
||||
// Composes with clause-3: a surviving cross-reference is a
|
||||
// resolving durable file-link or it is clause-3-forbidden
|
||||
// decision-record prose.
|
||||
|
||||
// Fenced code (``` … ``` / ~~~ … ~~~) is literal text, not
|
||||
// Markdown — a `](` inside a fence is NOT a navigable link on
|
||||
// any renderer. Strip fenced regions before scanning (gate
|
||||
// correctness: prevents false extraction of code-example byte
|
||||
// sequences and of non-rendering in-fence links).
|
||||
fn strip_fences(md: &str) -> String {
|
||||
let mut out = String::new();
|
||||
let mut in_fence = false;
|
||||
for line in md.lines() {
|
||||
let t = line.trim_start();
|
||||
if t.starts_with("```") || t.starts_with("~~~") {
|
||||
in_fence = !in_fence;
|
||||
continue; // drop the fence marker line itself
|
||||
}
|
||||
if !in_fence {
|
||||
out.push_str(line);
|
||||
out.push('\n');
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
// link target := first capture of \]\(([^)]+)\)
|
||||
// (matches [x](y) and the ](y) of image  alike — every
|
||||
@@ -239,6 +275,9 @@ fn design_body_links_are_durable_and_resolve() {
|
||||
assert_eq!(t, vec!["../docs/specs/foo.md", "#sec", "./gone.md"]);
|
||||
assert!(!is_durable("docs/specs/foo.md")); // durable-tier reject
|
||||
assert!("#sec".starts_with('#')); // in-file anchor reject
|
||||
// fenced code is not a link surface (gate correctness)
|
||||
assert!(targets(&strip_fences("```\nsee [E](e.md)\n```\n")).is_empty());
|
||||
assert_eq!(targets(&strip_fences("[k](k.md)\n```\n[n](n.md)\n```")), vec!["k.md"]);
|
||||
}
|
||||
|
||||
let bases = ["design/contracts", "design/models"];
|
||||
@@ -251,7 +290,7 @@ fn design_body_links_are_durable_and_resolve() {
|
||||
}
|
||||
let raw = fs::read_to_string(&p).unwrap();
|
||||
let fname = format!("{base}/{}", p.file_name().unwrap().to_string_lossy());
|
||||
for tgt in targets(&raw) {
|
||||
for tgt in targets(&strip_fences(&raw)) {
|
||||
if tgt.starts_with("http://")
|
||||
|| tgt.starts_with("https://")
|
||||
|| tgt.starts_with("mailto:")
|
||||
@@ -291,8 +330,9 @@ fn design_body_links_are_durable_and_resolve() {
|
||||
`#[test]` count in the file goes 4 → 5; clauses 1–4 are byte-
|
||||
unchanged. The exact byte form of the extractor/regex is the
|
||||
planner's to finalise against this shape; the *contract* is the
|
||||
three predicates (resolves-from-containing-file; durable-tier;
|
||||
no-fragment) and the RED-first synthetic vector.
|
||||
four predicates (resolves-from-containing-file; durable-tier;
|
||||
no-fragment; fenced-code-not-scanned) and the RED-first synthetic
|
||||
vector.
|
||||
|
||||
### (b) Real prose-reference before → after
|
||||
|
||||
@@ -406,21 +446,35 @@ outcome, not a dodge).
|
||||
`design_body_links_are_durable_and_resolve`; clauses 1–4 are
|
||||
byte-identical to HEAD `2ee4087`.
|
||||
2. clause-5 is demonstrably RED-able (the synthetic vector fails if
|
||||
the durable classifier or the anchor check is wrong) and GREEN on
|
||||
the final tree.
|
||||
the durable classifier, the anchor check, or the fence-skip is
|
||||
wrong) and GREEN on the final tree.
|
||||
3. Every *navigational* cross-*file* reference (per §"Scope — what
|
||||
counts as a cross-reference") in `design/contracts/` and
|
||||
`design/models/` is a file-relative `[label](path)` Markdown
|
||||
link resolving (from its containing file) to an existing
|
||||
`design/`-or-source file — OR, for a homeless reference, is
|
||||
disposed per clause-6 (a)/(b)/(c). Nominal source-symbol
|
||||
mentions are NOT linkified (out of scope). Iter-provenance
|
||||
suffixes on converted refs are stripped. The recon enumeration
|
||||
is the authoritative touch-set: float-semantics:100,
|
||||
float-semantics:69, embedding-abi:45, data-model:38/66/79/206/226,
|
||||
memory-model:44/105, scope-boundaries:48/88 convert; the two
|
||||
PROSE_ROUNDTRIP refs take disposition (b); embedding-abi:51 and
|
||||
every intra-file "above/below" stay prose.
|
||||
disposed per clause-6 (a)/(b)/(c). Nominal source-symbol mentions
|
||||
and in-fence schema annotations are NOT linkified (out of scope).
|
||||
Iter-provenance suffixes on converted refs are stripped. The
|
||||
recon-and-corpus-verified touch-set is **closed and exhaustive**:
|
||||
- **convert (8 prose refs):** `float-semantics.md:69`
|
||||
(§"Prelude (built-in) classes" → `typeclasses.md`),
|
||||
`float-semantics.md:100` (bare path → `str-abi.md`),
|
||||
`embedding-abi.md:45` ("Frozen value layout" →
|
||||
`frozen-value-layout.md`), `memory-model.md:44` (§"Data model"
|
||||
→ `data-model.md`), `memory-model.md:105` (§"Method dispatch"
|
||||
→ `typeclasses.md`), `scope-boundaries.md:48` ("Str ABI" →
|
||||
`str-abi.md`), `scope-boundaries.md:88` (splits:
|
||||
`ailang-core::desugar` →
|
||||
`../../crates/ailang-core/src/desugar.rs` AND "Pipeline" →
|
||||
`../models/pipeline.md`).
|
||||
- **disposition (b):** `pipeline.md:61`,
|
||||
`authoring-surface.md:180` — the `(see docs/PROSE_ROUNDTRIP.md)`
|
||||
pointer removed, surrounding `ail merge-prose` prose kept.
|
||||
- **stay prose (out of scope):** `data-model.md`
|
||||
38/66/79/206/226 (in-fence `jsonc` schema annotations),
|
||||
`embedding-abi.md:51` (no quoted title / no `(see)` form),
|
||||
every intra-file "above/below".
|
||||
4. No reference in `design/` targets `docs/` (any subtree) or
|
||||
carries a `#fragment`.
|
||||
5. `design/INDEX.md` and clause-1 are byte-unchanged; the
|
||||
|
||||
Reference in New Issue
Block a user