From c8c30d5682e46c8db34973e6d2f94551782bddc2 Mon Sep 17 00:00:00 2001 From: Brummel Date: Wed, 20 May 2026 18:11:21 +0200 Subject: [PATCH] =?UTF-8?q?test(drift):=20RED=20=E2=80=94=20anchor-presenc?= =?UTF-8?q?e=20must=20scope=20to=20jsonc=20fenced=20blocks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../ailang-core/tests/design_schema_drift.rs | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/crates/ailang-core/tests/design_schema_drift.rs b/crates/ailang-core/tests/design_schema_drift.rs index 0a6596b..4315df3 100644 --- a/crates/ailang-core/tests/design_schema_drift.rs +++ b/crates/ailang-core/tests/design_schema_drift.rs @@ -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. /// These keys appear inside `Suppress`, `ClassMethod`, `InstanceMethod`, /// and `Type::Forall` — they are not discriminators but they ARE part