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:
@@ -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}`"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user