fix(drift): scope anchor-presence check to jsonc fenced blocks

Adds `anchor_in_jsonc_block(md, anchor) -> bool` to
`design_schema_drift.rs` — a line-walking helper that toggles on
``` / ~~~ fence markers (any info-string: jsonc, json, or
unspecified — all count) and returns true only if the anchor
appears inside fenced content. Re-routes the seven
`DATA_MODEL.contains(anchor)` assertion sites in the file
(Term, Pattern, Type, Literal, Def, ParamMode, nested struct
keys) onto it.

The carrier from `debug` named six sites; in the implement phase
the orchestrator flagged a seventh — `design_md_anchors_nested_
struct_keys` — that exhibited the identical scope-widening bug and
was structurally identical to the named six. Routing it uniformly
is the cohesive shape of the fix; leaving it as `.contains()`
would have preserved a known false-positive surface inside the
same file the fix targets. The "minimal fix, no surrounding
cleanup" constraint blocks opportunistic refactor, not the
uniform application of the named one-line substitution.

Verified: 8/8 in `design_schema_drift` green (was 7 + 1 compile-
blocking RED at c8c30d5); full ailang-core suite green; full
workspace `cargo test` green, no regressions.

Mirrors the inverse `strip_fences` pattern in
`crates/ailang-core/tests/design_index_pin.rs`.

closes #10
This commit is contained in:
2026-05-20 18:15:44 +02:00
parent c8c30d5682
commit a355fd861e
2 changed files with 44 additions and 7 deletions
@@ -0,0 +1,12 @@
{
"iter_id": "bugfix-drift-anchor-scope",
"date": "2026-05-20",
"mode": "mini",
"outcome": "DONE",
"tasks_total": 1,
"tasks_completed": 1,
"reloops_per_task": { "1": 0 },
"review_loops_spec": 0,
"review_loops_quality": 0,
"blocked_reason": null
}
@@ -21,6 +21,31 @@ use ailang_core::ast::{
const DATA_MODEL: &str = include_str!("../../../design/contracts/data-model.md"); const DATA_MODEL: &str = include_str!("../../../design/contracts/data-model.md");
/// Scoped substring match: returns `true` iff `anchor` appears inside a
/// fenced code block (``` or ~~~) of `md`. Info-string `jsonc` / `json` /
/// unspecified all count — fence-toggling is by fence-marker line alone,
/// mirroring the inverse `strip_fences` pattern in
/// `crates/ailang-core/tests/design_index_pin.rs`. The whole-file
/// `.contains()` was fidelity-widened: anchors mentioned in surrounding
/// prose (footnotes, inline references, historical notes) counted as
/// present, so a future edit could delete the canonical schema entry for
/// a variant from inside a fenced block and the drift test would still
/// pass. This helper closes that surface.
fn anchor_in_jsonc_block(md: &str, anchor: &str) -> bool {
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;
}
if in_fence && line.contains(anchor) {
return true;
}
}
false
}
/// Every `Term` variant must have its JSON-schema anchor present in /// Every `Term` variant must have its JSON-schema anchor present in
/// design/contracts/data-model.md. An LLM author cannot produce a term variant /// design/contracts/data-model.md. An LLM author cannot produce a term variant
/// whose `"t"` tag is absent from the canonical schema document. /// whose `"t"` tag is absent from the canonical schema document.
@@ -156,7 +181,7 @@ fn design_md_anchors_every_term_variant() {
Term::Recur { .. } => "recur", Term::Recur { .. } => "recur",
}; };
assert!( assert!(
DATA_MODEL.contains(anchor), anchor_in_jsonc_block(DATA_MODEL, anchor),
"design/contracts/data-model.md is missing anchor `{anchor}` for a Term variant — \ "design/contracts/data-model.md is missing anchor `{anchor}` for a Term variant — \
add it to design/contracts/data-model.md" add it to design/contracts/data-model.md"
); );
@@ -186,7 +211,7 @@ fn design_md_anchors_every_pattern_variant() {
Pattern::Ctor { .. } => "ctor", Pattern::Ctor { .. } => "ctor",
}; };
assert!( assert!(
DATA_MODEL.contains(anchor), anchor_in_jsonc_block(DATA_MODEL, anchor),
"design/contracts/data-model.md is missing anchor `{anchor}` for a Pattern variant" "design/contracts/data-model.md is missing anchor `{anchor}` for a Pattern variant"
); );
} }
@@ -219,7 +244,7 @@ fn design_md_anchors_every_type_variant() {
Type::Forall { .. } => "forall", Type::Forall { .. } => "forall",
}; };
assert!( assert!(
DATA_MODEL.contains(anchor), anchor_in_jsonc_block(DATA_MODEL, anchor),
"design/contracts/data-model.md is missing anchor `{anchor}` for a Type variant" "design/contracts/data-model.md is missing anchor `{anchor}` for a Type variant"
); );
} }
@@ -248,7 +273,7 @@ fn design_md_anchors_every_literal_variant() {
Literal::Float { .. } => "float", Literal::Float { .. } => "float",
}; };
assert!( assert!(
DATA_MODEL.contains(anchor), anchor_in_jsonc_block(DATA_MODEL, anchor),
"design/contracts/data-model.md is missing anchor `{anchor}` for a Literal variant" "design/contracts/data-model.md is missing anchor `{anchor}` for a Literal variant"
); );
} }
@@ -320,7 +345,7 @@ fn design_md_anchors_every_def_kind() {
Def::Instance(_) => "instance", Def::Instance(_) => "instance",
}; };
assert!( assert!(
DATA_MODEL.contains(anchor), anchor_in_jsonc_block(DATA_MODEL, anchor),
"design/contracts/data-model.md is missing anchor `{anchor}` for a Def kind" "design/contracts/data-model.md is missing anchor `{anchor}` for a Def kind"
); );
} }
@@ -344,7 +369,7 @@ fn design_md_anchors_every_parammode_variant() {
ParamMode::Borrow => "borrow", ParamMode::Borrow => "borrow",
}; };
assert!( assert!(
DATA_MODEL.contains(anchor), anchor_in_jsonc_block(DATA_MODEL, anchor),
"design/contracts/data-model.md is missing anchor `{anchor}` for a ParamMode variant" "design/contracts/data-model.md is missing anchor `{anchor}` for a ParamMode variant"
); );
} }
@@ -457,7 +482,7 @@ fn design_md_anchors_nested_struct_keys() {
for anchor in anchors { for anchor in anchors {
assert!( assert!(
DATA_MODEL.contains(anchor), anchor_in_jsonc_block(DATA_MODEL, anchor),
"design/contracts/data-model.md is missing nested-struct-key anchor `{anchor}`" "design/contracts/data-model.md is missing nested-struct-key anchor `{anchor}`"
); );
} }