test(drift): RED — anchor-presence must scope to jsonc fenced blocks

Adds `anchor_presence_check_is_scoped_to_jsonc_blocks` to
`design_schema_drift.rs`. The test pins the property in two
directions: an anchor mentioned only in prose must report ABSENT
under the scoped helper; an anchor inside a ```jsonc``` block must
report PRESENT; the live `design/contracts/data-model.md` must
remain PRESENT (anti-over-narrowing guard).

The helper `anchor_in_jsonc_block` does not yet exist — this test
compile-blocks the entire `design_schema_drift` file, which is the
intended contract pressure: the GREEN side must introduce the
helper for any drift test to run.

Pre-rolesplit the audit framed this as "anchors live in Decision 11
instead of §Data model"; the role-split iter (176821c) made
`data-model.md` its own file, but the per-match scope was never
narrowed — `.contains()` still cannot tell jsonc-block anchors
(load-bearing) from prose anchors (incidental). This RED pins the
remaining surface.

refs #10
This commit is contained in:
2026-05-20 18:11:21 +02:00
parent 5627cc362c
commit c8c30d5682
@@ -350,6 +350,82 @@ fn design_md_anchors_every_parammode_variant() {
} }
} }
/// The anchor-presence check must scope its substring match to ```jsonc```
/// (and ``` / ```json) fenced code blocks. The whole-file `.contains()` is
/// fidelity-widened: a future edit can delete the canonical schema entry for
/// a variant from inside a fenced block while leaving the anchor string
/// mentioned in surrounding prose (e.g. a "design rationale" footnote, an
/// inline reference like `the "t": "lit" form`, or a historical-note
/// section). The drift test would silently pass, and downstream LLM authors
/// would lose the canonical schema for a variant the AST still produces.
///
/// This test pins the property in two directions:
/// 1. A markdown string with the anchor present ONLY in prose (no fenced
/// block) must report ABSENT under the scoped helper. The unscoped
/// `.contains()` reports PRESENT for the same input — that mismatch is
/// the bug surface.
/// 2. A markdown string with the anchor present ONLY inside a ```jsonc```
/// block must report PRESENT under the scoped helper.
///
/// Gitea issue #10. The helper `anchor_in_jsonc_block` does not yet exist —
/// this test is RED until `skills/implement` mini-mode adds it and re-routes
/// the six existing call sites onto it.
#[test]
fn anchor_presence_check_is_scoped_to_jsonc_blocks() {
// Anchor mentioned only in prose — the false-pass surface. The whole
// sentence after the heading is plain markdown body text; no fenced
// block exists in this fixture at all.
let prose_only = r#"# Data model
The `"t": "lit"` form used to be the canonical literal anchor. See
historical note below.
## Historical note
Older drafts pinned the literal schema as `"t": "lit"`; the current
schema uses a different shape (see commit log).
"#;
// Anchor present only inside a fenced jsonc block — the true-positive
// surface. Prose outside the block does not mention the anchor.
let jsonc_only = r#"# Data model
The literal form is canonical.
```jsonc
{ "t": "lit", "lit": Literal }
```
End.
"#;
// Unscoped substring match — what the file does today. Both inputs
// report PRESENT, and that is the bug: prose-only must not count.
assert!(prose_only.contains(r#""t": "lit""#));
assert!(jsonc_only.contains(r#""t": "lit""#));
// Scoped helper — what the file must do. Prose-only is ABSENT,
// jsonc-only is PRESENT. This call is the RED: the helper does not
// yet exist, so this test fails to compile until `implement` mini-mode
// factors it out and re-routes the six call sites onto it.
assert!(
!anchor_in_jsonc_block(prose_only, r#""t": "lit""#),
"scoped helper must NOT count an anchor that lives only in prose"
);
assert!(
anchor_in_jsonc_block(jsonc_only, r#""t": "lit""#),
"scoped helper MUST count an anchor that lives inside a ```jsonc``` block"
);
// Live document: the helper must also report PRESENT for the live
// data-model.md (proves the helper does not over-narrow and break
// the existing six checks).
assert!(
anchor_in_jsonc_block(DATA_MODEL, r#""t": "lit""#),
"scoped helper must find live anchors inside data-model.md ```jsonc``` blocks"
);
}
/// Nested struct key anchors must be present in design/contracts/data-model.md. /// Nested struct key anchors must be present in design/contracts/data-model.md.
/// These keys appear inside `Suppress`, `ClassMethod`, `InstanceMethod`, /// These keys appear inside `Suppress`, `ClassMethod`, `InstanceMethod`,
/// and `Type::Forall` — they are not discriminators but they ARE part /// and `Type::Forall` — they are not discriminators but they ARE part