diff --git a/crates/grund-cli/tests/e2e.rs b/crates/grund-cli/tests/e2e.rs index aaa094631..d65122173 100644 --- a/crates/grund-cli/tests/e2e.rs +++ b/crates/grund-cli/tests/e2e.rs @@ -8,6 +8,9 @@ mod case_runner; #[path = "support/chapter_rule_workflow.rs"] mod chapter_rule_workflow; +#[path = "support/chapter_diagnostics.rs"] +mod chapter_diagnostics; + use case_runner::CaseKind::{E2e, Example}; use case_runner::{ assert_case_is_deterministic, assert_every_case_passed, discover_e2e_cases, discover_examples, diff --git a/crates/grund-cli/tests/rules_contract/authority.rs b/crates/grund-cli/tests/rules_contract/authority.rs index ba422e394..89ca66e13 100644 --- a/crates/grund-cli/tests/rules_contract/authority.rs +++ b/crates/grund-cli/tests/rules_contract/authority.rs @@ -216,7 +216,7 @@ fn a_should_level_sentence_needs_suggestions_to_be_seen() { ), 0, "docs/fs/FS-demo.md:1: suggestion: FS-demo has 0 security chapters; \ - --rule requires exactly one\n", + --rule requires exactly one; expected display name \"security\" (case-insensitive, not section handle); observed direct chapters: \"goals: Goals\", \"requirements: Requirements\"\n", "", ); } diff --git a/crates/grund-cli/tests/rules_contract/refusals.rs b/crates/grund-cli/tests/rules_contract/refusals.rs index ae1ca45e8..351c13aee 100644 --- a/crates/grund-cli/tests/rules_contract/refusals.rs +++ b/crates/grund-cli/tests/rules_contract/refusals.rs @@ -63,7 +63,7 @@ fn every_listed_refusal_has_its_exact_rewrite_and_exit_two() { ), ( "Each FS must have exactly one chapter.", - "chapter name must be a non-empty NAME with no surrounding whitespace; accepted form: Each FS must have exactly one requirements chapter.", + "chapter name must be a non-empty NAME with no surrounding whitespace; accepted form: Each FS must have exactly one requirements chapter. NAME forbids whitespace anywhere.", ), ( "Each FS must cite at least one GOAL", diff --git a/crates/grund-cli/tests/rules_contract/regressions.rs b/crates/grund-cli/tests/rules_contract/regressions.rs index 6a9c23ca8..9adeb8cf0 100644 --- a/crates/grund-cli/tests/rules_contract/regressions.rs +++ b/crates/grund-cli/tests/rules_contract/regressions.rs @@ -26,7 +26,7 @@ fn chapter_handles_and_display_names_have_distinct_meanings() { assert_run( &output, 1, - "docs/fs/FS-demo.md:1: error: FS-demo has 0 requirements chapters; --rule requires exactly one\n", + "docs/fs/FS-demo.md:1: error: FS-demo has 0 requirements chapters; --rule requires exactly one; expected display name \"requirements\" (case-insensitive, not section handle); observed direct chapters: \"requirements: Acceptance criteria\"\n", "", ); let output = run(&root, &["list", ".", "--selector", "FS.requirements"]); diff --git a/crates/grund-cli/tests/rules_contract/surfaces.rs b/crates/grund-cli/tests/rules_contract/surfaces.rs index 73b80d3cd..7377fdda2 100644 --- a/crates/grund-cli/tests/rules_contract/surfaces.rs +++ b/crates/grund-cli/tests/rules_contract/surfaces.rs @@ -53,7 +53,7 @@ fn ad_hoc_rule_is_additive_and_suggestions_do_not_change_the_exit() { &["check", ".", "--rule", sentence, "--suggestions"], ); let expected = format!( - "{TEXT_FINDINGS}docs/fs/FS-demo.md:1: suggestion: FS-demo has 0 security chapters; --rule requires exactly one\n" + "{TEXT_FINDINGS}docs/fs/FS-demo.md:1: suggestion: FS-demo has 0 security chapters; --rule requires exactly one; expected display name \"security\" (case-insensitive, not section handle); observed direct chapters: \"goals: Goals\", \"requirements: Requirements\"\n" ); assert_run(&output, 1, &expected, ""); } diff --git a/crates/grund-cli/tests/support/chapter_diagnostics.rs b/crates/grund-cli/tests/support/chapter_diagnostics.rs new file mode 100644 index 000000000..44392f293 --- /dev/null +++ b/crates/grund-cli/tests/support/chapter_diagnostics.rs @@ -0,0 +1,36 @@ +//! Chapter diagnostic regressions for §FS-rules.7.2.1 and §FS-rules.3.5.1. +//! The unchanged-selector controls prove the goal chapter is actually reached. + +use super::case_runner::{CaseKind::E2e, assert_every_case_passed, run_case}; +use super::repo_root; + +fn case(name: &str) { + let root = repo_root(); + let path = root.join("tests/e2e/cases").join(name); + assert_every_case_passed(name, &[run_case(&root, &path, E2e)]); +} + +/// §FS-rules.7.2.1, §FS-errors.3: old bytes plus display-name context in text. +#[test] +fn chapter_diagnostic_display_name_text() { + case("check-chapter-diagnostic-display-name-text"); +} + +/// §FS-rules.7.2.1: preserve every JSON field while explaining the zero count. +#[test] +fn chapter_diagnostic_display_name_json() { + case("check-chapter-diagnostic-display-name-json"); +} + +/// §FS-rules.3.5.1: keep the full refusal prefix and explain internal whitespace. +#[test] +fn chapter_diagnostic_whitespace_refusal() { + case("check-chapter-diagnostic-whitespace-refusal"); +} + +/// §FS-rules.2: a chapter subject selects the handle despite its display title. +#[test] +fn chapter_diagnostic_handle_subject_reaches_citation() { + case("check-chapter-diagnostic-handle-subject"); + case("check-chapter-diagnostic-handle-subject-missing-citation"); +} diff --git a/crates/grund-core/assets/skills/grund-init/SKILL.md b/crates/grund-core/assets/skills/grund-init/SKILL.md index 09990cc73..10d0ab86b 100644 --- a/crates/grund-core/assets/skills/grund-init/SKILL.md +++ b/crates/grund-core/assets/skills/grund-init/SKILL.md @@ -618,6 +618,8 @@ Subjects select local units only: - `The requirements chapter of each FS` selects that named chapter in every FS. - `FS-login.requirements` selects one exact named chapter. +Named chapter subjects select handles: `The goal chapter of each FS` reaches `## goal: Goal and hypothesis` by `goal` ([chapter subjects](https://github.com/agent-grounds/grund/blob/main/docs/functional-spec/FS-rules.md#2-subject-selectors)). Presence rules compare direct chapter display names case-insensitively: `Each FS must have exactly one goal chapter.` counts zero for that heading and one for `## goal: Goal` ([presence matching](https://github.com/agent-grounds/grund/blob/main/docs/functional-spec/FS-rules.md#31-chapter-presence)). The finding appends the expected display name and observed pairs such as `"goal: Goal and hypothesis"`, or `none` ([comparison context](https://github.com/agent-grounds/grund/blob/main/docs/functional-spec/FS-rules.md#721-display-name-comparison-context)). Presence `NAME` must be one non-empty token: `Goal and hypothesis` is refused with `NAME forbids whitespace anywhere.` appended to the existing guidance; authored display titles may still contain spaces ([whitespace refusal](https://github.com/agent-grounds/grund/blob/main/docs/functional-spec/FS-rules.md#351-presence-name-whitespace-refusal)). + Named-chapter subjects require `[id] named_sections = true`. Phase 1 has no paths, files, folders, wildcards, subject namespaces, numbered-chapter subjects, exceptions, definitions, derived terms, settings, or source-code symbols. @@ -704,7 +706,7 @@ Common refusals are intentional and name the exact accepted rewrite: - `Each FS must cite at least 1 GOAL.` → `numeric "at least 1" is not canonical; accepted form: Each FS must cite at least one GOAL.` - `Each FS must cite exactly 1 GOAL.` → `numeric "exactly 1" is not canonical; accepted form: Each FS must cite exactly one GOAL.` - `Each FS must cite at least one GOAL and must not cite any AR.` → `conjunctions are not accepted; accepted forms: "Each FS must cite at least one GOAL." and "Each FS must not cite any AR."` -- `Each FS must have exactly one chapter.` → `chapter name must be a non-empty NAME with no surrounding whitespace; accepted form: Each FS must have exactly one requirements chapter.` +- `Each FS must have exactly one chapter.` → `chapter name must be a non-empty NAME with no surrounding whitespace; accepted form: Each FS must have exactly one requirements chapter. NAME forbids whitespace anywhere.` - `Each FS must cite at least one GOAL` → `rule must end with "."; accepted form: Each FS must cite at least one GOAL.` - `each FS must cite at least one GOAL.` → `fixed word "Each" is case-sensitive; accepted form: Each FS must cite at least one GOAL.` - `Each file in vendor/ must cite at least one FS.` → `path subjects are not accepted in phase 1; accepted form: Each FS must cite at least one GOAL.` diff --git a/crates/grund-core/src/rules/engine.rs b/crates/grund-core/src/rules/engine.rs index 0c2a2be87..c0eae4784 100644 --- a/crates/grund-core/src/rules/engine.rs +++ b/crates/grund-core/src/rules/engine.rs @@ -137,19 +137,40 @@ fn evaluate_one( return; }; for subject in subjects { - let count = index - .chapters_of(&subject) + let chapters = index.chapters_of(&subject); + let count = chapters .iter() .filter(|row| facts.chapter[**row].2.eq_ignore_ascii_case(name)) .count(); if !rule.cardinality.contains(count) { + // §FS-rules.7.2.1: show accepted direct chapters in source order, + // retaining the complete released prefix (§FS-errors.3). + let mut observed = chapters.to_vec(); + observed.sort_by_key(|row| { + facts + .nodes + .get(&facts.chapter[*row].0) + .map(|meta| meta.anchor.line) + }); + let observed = if observed.is_empty() { + "none".to_string() + } else { + observed + .iter() + .map(|row| { + let (_, coordinate, display_name) = &facts.chapter[*row]; + format!("\"{coordinate}: {display_name}\"") + }) + .collect::>() + .join(", ") + }; push_node( out, facts, &subject, "chapter-cardinality", format!( - "{} has {count} {name} chapters; {authority} requires {}", + "{} has {count} {name} chapters; {authority} requires {}; expected display name \"{name}\" (case-insensitive, not section handle); observed direct chapters: {observed}", label(facts, &subject), rule.cardinality.wording() ), diff --git a/crates/grund-core/src/rules/sentence.rs b/crates/grund-core/src/rules/sentence.rs index d2b2eb654..7b6b3e793 100644 --- a/crates/grund-core/src/rules/sentence.rs +++ b/crates/grund-core/src/rules/sentence.rs @@ -267,8 +267,9 @@ fn parse_predicate( )); }; if name.is_empty() || name.trim() != name || name.chars().any(char::is_whitespace) { + // §FS-rules.3.5.1: append whitespace guidance to the released refusal prefix. return Err(error( - "chapter name must be a non-empty NAME with no surrounding whitespace; accepted form: Each FS must have exactly one requirements chapter.", + "chapter name must be a non-empty NAME with no surrounding whitespace; accepted form: Each FS must have exactly one requirements chapter. NAME forbids whitespace anywhere.", )); } let expects_plural = match spelling { diff --git a/crates/grund-core/src/rules/tests_boundaries.rs b/crates/grund-core/src/rules/tests_boundaries.rs index 1640390d8..1392d5368 100644 --- a/crates/grund-core/src/rules/tests_boundaries.rs +++ b/crates/grund-core/src/rules/tests_boundaries.rs @@ -35,6 +35,43 @@ fn rule() -> ParsedRule { } } +/// §FS-rules.3.5.1, §FS-errors.3: whitespace at any position remains refused, +/// with the entire released reason followed by actionable guidance. +#[test] +fn chapter_diagnostic_parser_refuses_whitespace_anywhere_in_presence_name() { + let vocabulary = RuleVocabulary { + kinds: BTreeSet::from(["FS".into()]), + target_kinds: BTreeSet::from(["FS".into()]), + target_namespaces: BTreeMap::new(), + named_sections: true, + id_grammars: Vec::new(), + section_separators: vec![".".into()], + }; + let expected = concat!( + "chapter name must be a non-empty NAME with no surrounding whitespace; ", + "accepted form: Each FS must have exactly one requirements chapter.", + " NAME forbids whitespace anywhere." + ); + for name in [ + "Goal and hypothesis", + "Goal\tand", + "Goal\u{a0}and", + " goal", + "goal ", + "", + ] { + let sentence = format!("Each FS must have exactly one {name} chapter."); + let refusal = parse_rule( + &sentence, + "RULE-presence".into(), + anchor("docs/rules.md", 1), + &vocabulary, + ) + .expect_err("presence NAME with whitespace or no token stays refused"); + assert_eq!(refusal.message, expected, "presence NAME {name:?}"); + } +} + fn facts(completeness: Completeness) -> RuleFacts { let fs = NodeKey("opaque-fs".into()); let goal = NodeKey("opaque-goal".into()); diff --git a/docs/functional-spec/FS-rules.md b/docs/functional-spec/FS-rules.md index 0232f29f3..159db8037 100644 --- a/docs/functional-spec/FS-rules.md +++ b/docs/functional-spec/FS-rules.md @@ -73,6 +73,11 @@ The four accepted subject spellings are: | `The NAME chapter of each KIND` | that named chapter of every local declaration of the kind that has one | | `ID.NAME[.NAME…]` | the one local named chapter with that exact coordinate | +In chapter subjects, `NAME` selects the section handle, not its display name. +For `## goal: Goal and hypothesis`, `The goal chapter of each BENCH` selects +the `goal` coordinate even though its display name is `Goal and hypothesis`. +Presence rules compare the other field ([§FS-rules.3.1](FS-rules.md#31-chapter-presence)). + A token exactly equal to a configured citable kind name is the quantified kind selector; any longer token must parse as a full ID under that kind's effective grammar. Named components require `[id] named_sections = true` and obey the @@ -135,6 +140,12 @@ spelling of a ceiling of one. The only accepted families are the following. The subject may be a kind or exact declaration, not a chapter. The rule counts the subject declaration's accepted direct chapters whose display name is `NAME`. A count outside the stated interval produces `chapter-cardinality`. +The comparison is case-insensitive and does not match section handles: a direct +`## goal: Goal and hypothesis` chapter contributes zero to a presence rule for +`goal`, while `## goal: Goal` contributes one. Presence `NAME` is a non-empty +single token with no whitespace anywhere; the multi-word display name `Goal +and hypothesis` cannot be used as a presence `NAME`. This restriction does not +restrict authored display titles or change chapter-subject selection. This family is how a repository states the *count* of a chapter — `exactly one`, `at most N` — which no citation rule checks. A chapter-scoped citation rule reaches a declaration that has no such chapter on its own @@ -226,6 +237,21 @@ least these exact rows: is not a pre-scan refusal. After scanning it produces the exact resolution message `literal subject FS-missing does not resolve` ([§FS-rules.4](FS-rules.md#4-validation-lifecycle)). +#### 3.5.1 Presence-name whitespace refusal + +A presence `NAME` containing whitespace anywhere, including internal spaces, +tabs, and Unicode whitespace, remains refused. The whole released reason, +including its accepted-form example, remains a verbatim contiguous prefix under +[§FS-errors.3](FS-errors.md#3-message-text); append the explanation: + +```text +chapter name must be a non-empty NAME with no surrounding whitespace; accepted form: Each FS must have exactly one requirements chapter. NAME forbids whitespace anywhere. +``` + +An empty `NAME` uses the same refusal. This adds guidance without admitting any +new grammar. An ad-hoc refusal still writes nothing to stdout and exits 2 +([§FS-rules.4](FS-rules.md#4-validation-lifecycle)). + ## 4. Validation lifecycle `config validate` validates only the optional `rules` key and its relationship @@ -526,6 +552,28 @@ zero and surplus chapters: has chapters; requires ``` +#### 7.2.1 Display-name comparison context + +Keep the complete message above as a verbatim contiguous prefix +([§FS-errors.3](FS-errors.md#3-message-text)) and append: + +```text +; expected display name "" (case-insensitive, not section handle); observed direct chapters: +``` + +`` lists every accepted direct chapter in source order, each quoted +as `": "`, separated by `, `; when there are none it +is `none`. Named coordinates expose the handle alongside the authored display +name, so the reported example ends in `"goal: Goal and hypothesis"` rather +than implying that the accepted chapter was not scanned. Nested descendants +and rejected headings do not enter this direct-chapter list or the count. + +Text output and JSON `message` carry the same complete explanation. The code, +subject-title location, authority, severity/channel, other finding fields and +exit verdict stay unchanged. The rule guide states this display-name/handle +distinction beside its presence and chapter-subject examples and explains the +single-token presence `NAME` restriction using `goal: Goal and hypothesis`. + ### 7.3 Outbound citation cardinality An ordinary hard `at least one` rule with zero matches reuses diff --git a/docs/user-facing/rules.md b/docs/user-facing/rules.md index f932bad53..6004bce4e 100644 --- a/docs/user-facing/rules.md +++ b/docs/user-facing/rules.md @@ -28,6 +28,12 @@ This keeps requirements tied to authority, because §GOAL-grounded-rules. The title is a grammar island: formatting never inserts markers, links, or expanded shorthand into it. The rationale remains ordinary grounded Markdown. +The shared guide below explains handle selection ([§FS-rules.2](../functional-spec/FS-rules.md#2-subject-selectors)), +display-name matching ([§FS-rules.3.1](../functional-spec/FS-rules.md#31-chapter-presence)), +comparison diagnostics ([§FS-rules.7.2.1](../functional-spec/FS-rules.md#721-display-name-comparison-context)) +and whitespace guidance ([§FS-rules.3.5.1](../functional-spec/FS-rules.md#351-presence-name-whitespace-refusal)). +Its shipped skill copies use public links ([§REQ-shipped-surfaces.2](../requirements/REQ-shipped-surfaces.md#2-the-grounding-moves-it-is-not-deleted)). + ### Chapter rules @@ -44,6 +50,8 @@ Subjects select local units only: - `The requirements chapter of each FS` selects that named chapter in every FS. - `FS-login.requirements` selects one exact named chapter. +Named chapter subjects select handles: `The goal chapter of each FS` reaches `## goal: Goal and hypothesis` by `goal` ([chapter subjects](https://github.com/agent-grounds/grund/blob/main/docs/functional-spec/FS-rules.md#2-subject-selectors)). Presence rules compare direct chapter display names case-insensitively: `Each FS must have exactly one goal chapter.` counts zero for that heading and one for `## goal: Goal` ([presence matching](https://github.com/agent-grounds/grund/blob/main/docs/functional-spec/FS-rules.md#31-chapter-presence)). The finding appends the expected display name and observed pairs such as `"goal: Goal and hypothesis"`, or `none` ([comparison context](https://github.com/agent-grounds/grund/blob/main/docs/functional-spec/FS-rules.md#721-display-name-comparison-context)). Presence `NAME` must be one non-empty token: `Goal and hypothesis` is refused with `NAME forbids whitespace anywhere.` appended to the existing guidance; authored display titles may still contain spaces ([whitespace refusal](https://github.com/agent-grounds/grund/blob/main/docs/functional-spec/FS-rules.md#351-presence-name-whitespace-refusal)). + Named-chapter subjects require `[id] named_sections = true`. Phase 1 has no paths, files, folders, wildcards, subject namespaces, numbered-chapter subjects, exceptions, definitions, derived terms, settings, or source-code symbols. @@ -130,7 +138,7 @@ Common refusals are intentional and name the exact accepted rewrite: - `Each FS must cite at least 1 GOAL.` → `numeric "at least 1" is not canonical; accepted form: Each FS must cite at least one GOAL.` - `Each FS must cite exactly 1 GOAL.` → `numeric "exactly 1" is not canonical; accepted form: Each FS must cite exactly one GOAL.` - `Each FS must cite at least one GOAL and must not cite any AR.` → `conjunctions are not accepted; accepted forms: "Each FS must cite at least one GOAL." and "Each FS must not cite any AR."` -- `Each FS must have exactly one chapter.` → `chapter name must be a non-empty NAME with no surrounding whitespace; accepted form: Each FS must have exactly one requirements chapter.` +- `Each FS must have exactly one chapter.` → `chapter name must be a non-empty NAME with no surrounding whitespace; accepted form: Each FS must have exactly one requirements chapter. NAME forbids whitespace anywhere.` - `Each FS must cite at least one GOAL` → `rule must end with "."; accepted form: Each FS must cite at least one GOAL.` - `each FS must cite at least one GOAL.` → `fixed word "Each" is case-sensitive; accepted form: Each FS must cite at least one GOAL.` - `Each file in vendor/ must cite at least one FS.` → `path subjects are not accepted in phase 1; accepted form: Each FS must cite at least one GOAL.` diff --git a/examples/rules/expected.stdout b/examples/rules/expected.stdout index f2625f994..7993b29b1 100644 --- a/examples/rules/expected.stdout +++ b/examples/rules/expected.stdout @@ -1,6 +1,6 @@ docs/ar/AR-overview.md:3: error: AR-overview.system-overview cites AR-one 2 times; RULE-overview requires exactly once docs/ar/AR-overview.md:3: error: AR-overview.system-overview cites AR-one-more 0 times; RULE-overview requires exactly once -docs/fs/FS-demo.md:1: error: FS-demo has 0 security chapters; RULE-security requires exactly one +docs/fs/FS-demo.md:1: error: FS-demo has 0 security chapters; RULE-security requires exactly one; expected display name "security" (case-insensitive, not section handle); observed direct chapters: "goals: Goals", "requirements: Requirements" docs/fs/FS-demo.md:1: error: FS-demo must cite GOAL (citation direction) docs/fs/FS-demo.md:6: error: FS-demo.requirements cites REQ 0 times; RULE-requirements-count requires exactly 2 docs/fs/FS-demo.md:6: error: FS-demo.requirements cites REQ 0 times; RULE-requirements-floor requires at least 2 diff --git a/skills/grund-init/SKILL.md b/skills/grund-init/SKILL.md index 09990cc73..10d0ab86b 100644 --- a/skills/grund-init/SKILL.md +++ b/skills/grund-init/SKILL.md @@ -618,6 +618,8 @@ Subjects select local units only: - `The requirements chapter of each FS` selects that named chapter in every FS. - `FS-login.requirements` selects one exact named chapter. +Named chapter subjects select handles: `The goal chapter of each FS` reaches `## goal: Goal and hypothesis` by `goal` ([chapter subjects](https://github.com/agent-grounds/grund/blob/main/docs/functional-spec/FS-rules.md#2-subject-selectors)). Presence rules compare direct chapter display names case-insensitively: `Each FS must have exactly one goal chapter.` counts zero for that heading and one for `## goal: Goal` ([presence matching](https://github.com/agent-grounds/grund/blob/main/docs/functional-spec/FS-rules.md#31-chapter-presence)). The finding appends the expected display name and observed pairs such as `"goal: Goal and hypothesis"`, or `none` ([comparison context](https://github.com/agent-grounds/grund/blob/main/docs/functional-spec/FS-rules.md#721-display-name-comparison-context)). Presence `NAME` must be one non-empty token: `Goal and hypothesis` is refused with `NAME forbids whitespace anywhere.` appended to the existing guidance; authored display titles may still contain spaces ([whitespace refusal](https://github.com/agent-grounds/grund/blob/main/docs/functional-spec/FS-rules.md#351-presence-name-whitespace-refusal)). + Named-chapter subjects require `[id] named_sections = true`. Phase 1 has no paths, files, folders, wildcards, subject namespaces, numbered-chapter subjects, exceptions, definitions, derived terms, settings, or source-code symbols. @@ -704,7 +706,7 @@ Common refusals are intentional and name the exact accepted rewrite: - `Each FS must cite at least 1 GOAL.` → `numeric "at least 1" is not canonical; accepted form: Each FS must cite at least one GOAL.` - `Each FS must cite exactly 1 GOAL.` → `numeric "exactly 1" is not canonical; accepted form: Each FS must cite exactly one GOAL.` - `Each FS must cite at least one GOAL and must not cite any AR.` → `conjunctions are not accepted; accepted forms: "Each FS must cite at least one GOAL." and "Each FS must not cite any AR."` -- `Each FS must have exactly one chapter.` → `chapter name must be a non-empty NAME with no surrounding whitespace; accepted form: Each FS must have exactly one requirements chapter.` +- `Each FS must have exactly one chapter.` → `chapter name must be a non-empty NAME with no surrounding whitespace; accepted form: Each FS must have exactly one requirements chapter. NAME forbids whitespace anywhere.` - `Each FS must cite at least one GOAL` → `rule must end with "."; accepted form: Each FS must cite at least one GOAL.` - `each FS must cite at least one GOAL.` → `fixed word "Each" is case-sensitive; accepted form: Each FS must cite at least one GOAL.` - `Each file in vendor/ must cite at least one FS.` → `path subjects are not accepted in phase 1; accepted form: Each FS must cite at least one GOAL.` diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-json/command.args b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/command.args new file mode 100644 index 000000000..990bd30d8 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/command.args @@ -0,0 +1 @@ +check {repo} --format json diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-json/expected.exit b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/expected.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/expected.exit @@ -0,0 +1 @@ +1 diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-json/expected.stderr b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/expected.stderr new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/expected.stderr @@ -0,0 +1 @@ + diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-json/expected.stdout b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/expected.stdout new file mode 100644 index 000000000..694285d19 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/expected.stdout @@ -0,0 +1 @@ +{"severity":"error","path":"docs/bench/BENCH-001-example-benchmark.md","line":1,"code":"chapter-cardinality","message":"BENCH-001-example-benchmark has 0 goal chapters; RULE-012-bench-goal requires exactly one; expected display name \"goal\" (case-insensitive, not section handle); observed direct chapters: \"goal: Goal and hypothesis\"","sites":null,"authority":["RULE-012-bench-goal"]} diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/docs/bench/BENCH-001-example-benchmark.md b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/docs/bench/BENCH-001-example-benchmark.md new file mode 100644 index 000000000..1e51e50cb --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/docs/bench/BENCH-001-example-benchmark.md @@ -0,0 +1,5 @@ +# BENCH-001-example-benchmark: Example benchmark + +## goal: Goal and hypothesis + +This chapter cites §GOAL-001-example-goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/docs/goals/GOAL-001-example-goal.md b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/docs/goals/GOAL-001-example-goal.md new file mode 100644 index 000000000..2bb2dee49 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/docs/goals/GOAL-001-example-goal.md @@ -0,0 +1,3 @@ +# GOAL-001-example-goal: Example goal + +The benchmark explains its goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/docs/references.md b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/docs/references.md new file mode 100644 index 000000000..2e744b6b8 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/docs/references.md @@ -0,0 +1 @@ +The scenario exercises §BENCH-001-example-benchmark, §RULE-012-bench-goal and §RULE-013-bench-goal-citation. diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/docs/rules/RULE-012-bench-goal.md b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/docs/rules/RULE-012-bench-goal.md new file mode 100644 index 000000000..2a38ed4b8 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/docs/rules/RULE-012-bench-goal.md @@ -0,0 +1,3 @@ +# RULE-012-bench-goal: Each BENCH must have exactly one goal chapter. + +Keep the benchmark grounded in §GOAL-001-example-goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/docs/rules/RULE-013-bench-goal-citation.md b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/docs/rules/RULE-013-bench-goal-citation.md new file mode 100644 index 000000000..4c5780762 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/docs/rules/RULE-013-bench-goal-citation.md @@ -0,0 +1,3 @@ +# RULE-013-bench-goal-citation: The goal chapter of each BENCH must cite at least one GOAL. + +Make the goal explicit with §GOAL-001-example-goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/grund.toml b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/grund.toml new file mode 100644 index 000000000..c06fef45b --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/repo/grund.toml @@ -0,0 +1,22 @@ +grund_config_version = 1 +project_name = "chapter-diagnostic" +[reference] +strict = true +[id] +format = "{kind}-{slug}" +named_sections = true +[[kinds]] +kind = "BENCH" +folder = "docs/bench" +index = false +[[kinds]] +kind = "GOAL" +folder = "docs/goals" +index = false +[[kinds]] +kind = "RULE" +folder = "docs/rules" +index = false +rules = true +[scan] +include = ["docs"] diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-json/spec.refs b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/spec.refs new file mode 100644 index 000000000..faa6a2b36 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-json/spec.refs @@ -0,0 +1,2 @@ +FS-rules.7.2.1 +FS-errors.3 diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-text/command.args b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/command.args new file mode 100644 index 000000000..9c39f98bb --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/command.args @@ -0,0 +1 @@ +check {repo} diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-text/expected.exit b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/expected.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/expected.exit @@ -0,0 +1 @@ +1 diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-text/expected.stderr b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/expected.stderr new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/expected.stderr @@ -0,0 +1 @@ + diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-text/expected.stdout b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/expected.stdout new file mode 100644 index 000000000..d12d6b570 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/expected.stdout @@ -0,0 +1 @@ +docs/bench/BENCH-001-example-benchmark.md:1: error: BENCH-001-example-benchmark has 0 goal chapters; RULE-012-bench-goal requires exactly one; expected display name "goal" (case-insensitive, not section handle); observed direct chapters: "goal: Goal and hypothesis" diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/docs/bench/BENCH-001-example-benchmark.md b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/docs/bench/BENCH-001-example-benchmark.md new file mode 100644 index 000000000..1e51e50cb --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/docs/bench/BENCH-001-example-benchmark.md @@ -0,0 +1,5 @@ +# BENCH-001-example-benchmark: Example benchmark + +## goal: Goal and hypothesis + +This chapter cites §GOAL-001-example-goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/docs/goals/GOAL-001-example-goal.md b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/docs/goals/GOAL-001-example-goal.md new file mode 100644 index 000000000..2bb2dee49 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/docs/goals/GOAL-001-example-goal.md @@ -0,0 +1,3 @@ +# GOAL-001-example-goal: Example goal + +The benchmark explains its goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/docs/references.md b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/docs/references.md new file mode 100644 index 000000000..2e744b6b8 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/docs/references.md @@ -0,0 +1 @@ +The scenario exercises §BENCH-001-example-benchmark, §RULE-012-bench-goal and §RULE-013-bench-goal-citation. diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/docs/rules/RULE-012-bench-goal.md b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/docs/rules/RULE-012-bench-goal.md new file mode 100644 index 000000000..2a38ed4b8 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/docs/rules/RULE-012-bench-goal.md @@ -0,0 +1,3 @@ +# RULE-012-bench-goal: Each BENCH must have exactly one goal chapter. + +Keep the benchmark grounded in §GOAL-001-example-goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/docs/rules/RULE-013-bench-goal-citation.md b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/docs/rules/RULE-013-bench-goal-citation.md new file mode 100644 index 000000000..4c5780762 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/docs/rules/RULE-013-bench-goal-citation.md @@ -0,0 +1,3 @@ +# RULE-013-bench-goal-citation: The goal chapter of each BENCH must cite at least one GOAL. + +Make the goal explicit with §GOAL-001-example-goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/grund.toml b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/grund.toml new file mode 100644 index 000000000..c06fef45b --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/repo/grund.toml @@ -0,0 +1,22 @@ +grund_config_version = 1 +project_name = "chapter-diagnostic" +[reference] +strict = true +[id] +format = "{kind}-{slug}" +named_sections = true +[[kinds]] +kind = "BENCH" +folder = "docs/bench" +index = false +[[kinds]] +kind = "GOAL" +folder = "docs/goals" +index = false +[[kinds]] +kind = "RULE" +folder = "docs/rules" +index = false +rules = true +[scan] +include = ["docs"] diff --git a/tests/e2e/cases/check-chapter-diagnostic-display-name-text/spec.refs b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/spec.refs new file mode 100644 index 000000000..faa6a2b36 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-display-name-text/spec.refs @@ -0,0 +1,2 @@ +FS-rules.7.2.1 +FS-errors.3 diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/command.args b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/command.args new file mode 100644 index 000000000..6000aa334 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/command.args @@ -0,0 +1 @@ +check {repo} --rule "The goal chapter of each BENCH must cite at least one GOAL." --only-rule diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/expected.exit b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/expected.exit new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/expected.exit @@ -0,0 +1 @@ +1 diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/expected.stderr b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/expected.stderr new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/expected.stderr @@ -0,0 +1 @@ + diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/expected.stdout b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/expected.stdout new file mode 100644 index 000000000..9d30210b7 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/expected.stdout @@ -0,0 +1 @@ +docs/bench/BENCH-001-example-benchmark.md:3: error: BENCH-001-example-benchmark.goal must cite GOAL (--rule, RULE-013-bench-goal-citation) diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/docs/bench/BENCH-001-example-benchmark.md b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/docs/bench/BENCH-001-example-benchmark.md new file mode 100644 index 000000000..41cb867bf --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/docs/bench/BENCH-001-example-benchmark.md @@ -0,0 +1,5 @@ +# BENCH-001-example-benchmark: Example benchmark + +## goal: Goal and hypothesis + +This chapter has no citation. diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/docs/goals/GOAL-001-example-goal.md b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/docs/goals/GOAL-001-example-goal.md new file mode 100644 index 000000000..2bb2dee49 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/docs/goals/GOAL-001-example-goal.md @@ -0,0 +1,3 @@ +# GOAL-001-example-goal: Example goal + +The benchmark explains its goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/docs/references.md b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/docs/references.md new file mode 100644 index 000000000..2e744b6b8 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/docs/references.md @@ -0,0 +1 @@ +The scenario exercises §BENCH-001-example-benchmark, §RULE-012-bench-goal and §RULE-013-bench-goal-citation. diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/docs/rules/RULE-012-bench-goal.md b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/docs/rules/RULE-012-bench-goal.md new file mode 100644 index 000000000..2a38ed4b8 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/docs/rules/RULE-012-bench-goal.md @@ -0,0 +1,3 @@ +# RULE-012-bench-goal: Each BENCH must have exactly one goal chapter. + +Keep the benchmark grounded in §GOAL-001-example-goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/docs/rules/RULE-013-bench-goal-citation.md b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/docs/rules/RULE-013-bench-goal-citation.md new file mode 100644 index 000000000..4c5780762 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/docs/rules/RULE-013-bench-goal-citation.md @@ -0,0 +1,3 @@ +# RULE-013-bench-goal-citation: The goal chapter of each BENCH must cite at least one GOAL. + +Make the goal explicit with §GOAL-001-example-goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/grund.toml b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/grund.toml new file mode 100644 index 000000000..c06fef45b --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/repo/grund.toml @@ -0,0 +1,22 @@ +grund_config_version = 1 +project_name = "chapter-diagnostic" +[reference] +strict = true +[id] +format = "{kind}-{slug}" +named_sections = true +[[kinds]] +kind = "BENCH" +folder = "docs/bench" +index = false +[[kinds]] +kind = "GOAL" +folder = "docs/goals" +index = false +[[kinds]] +kind = "RULE" +folder = "docs/rules" +index = false +rules = true +[scan] +include = ["docs"] diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/spec.refs b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/spec.refs new file mode 100644 index 000000000..4f45549b5 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject-missing-citation/spec.refs @@ -0,0 +1,2 @@ +FS-rules.2 +FS-rules.7.3 diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject/command.args b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/command.args new file mode 100644 index 000000000..6000aa334 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/command.args @@ -0,0 +1 @@ +check {repo} --rule "The goal chapter of each BENCH must cite at least one GOAL." --only-rule diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject/expected.exit b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/expected.exit new file mode 100644 index 000000000..573541ac9 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/expected.exit @@ -0,0 +1 @@ +0 diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject/expected.stderr b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/expected.stderr new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/expected.stderr @@ -0,0 +1 @@ + diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject/expected.stdout b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/expected.stdout new file mode 100644 index 000000000..2e9ba477f --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/expected.stdout @@ -0,0 +1 @@ +success diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/docs/bench/BENCH-001-example-benchmark.md b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/docs/bench/BENCH-001-example-benchmark.md new file mode 100644 index 000000000..1e51e50cb --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/docs/bench/BENCH-001-example-benchmark.md @@ -0,0 +1,5 @@ +# BENCH-001-example-benchmark: Example benchmark + +## goal: Goal and hypothesis + +This chapter cites §GOAL-001-example-goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/docs/goals/GOAL-001-example-goal.md b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/docs/goals/GOAL-001-example-goal.md new file mode 100644 index 000000000..2bb2dee49 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/docs/goals/GOAL-001-example-goal.md @@ -0,0 +1,3 @@ +# GOAL-001-example-goal: Example goal + +The benchmark explains its goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/docs/references.md b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/docs/references.md new file mode 100644 index 000000000..2e744b6b8 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/docs/references.md @@ -0,0 +1 @@ +The scenario exercises §BENCH-001-example-benchmark, §RULE-012-bench-goal and §RULE-013-bench-goal-citation. diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/docs/rules/RULE-012-bench-goal.md b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/docs/rules/RULE-012-bench-goal.md new file mode 100644 index 000000000..2a38ed4b8 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/docs/rules/RULE-012-bench-goal.md @@ -0,0 +1,3 @@ +# RULE-012-bench-goal: Each BENCH must have exactly one goal chapter. + +Keep the benchmark grounded in §GOAL-001-example-goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/docs/rules/RULE-013-bench-goal-citation.md b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/docs/rules/RULE-013-bench-goal-citation.md new file mode 100644 index 000000000..4c5780762 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/docs/rules/RULE-013-bench-goal-citation.md @@ -0,0 +1,3 @@ +# RULE-013-bench-goal-citation: The goal chapter of each BENCH must cite at least one GOAL. + +Make the goal explicit with §GOAL-001-example-goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/grund.toml b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/grund.toml new file mode 100644 index 000000000..c06fef45b --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/repo/grund.toml @@ -0,0 +1,22 @@ +grund_config_version = 1 +project_name = "chapter-diagnostic" +[reference] +strict = true +[id] +format = "{kind}-{slug}" +named_sections = true +[[kinds]] +kind = "BENCH" +folder = "docs/bench" +index = false +[[kinds]] +kind = "GOAL" +folder = "docs/goals" +index = false +[[kinds]] +kind = "RULE" +folder = "docs/rules" +index = false +rules = true +[scan] +include = ["docs"] diff --git a/tests/e2e/cases/check-chapter-diagnostic-handle-subject/spec.refs b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/spec.refs new file mode 100644 index 000000000..dafd77de9 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-handle-subject/spec.refs @@ -0,0 +1,2 @@ +FS-rules.2 +FS-rules.3.1 diff --git a/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/command.args b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/command.args new file mode 100644 index 000000000..9c5b5857c --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/command.args @@ -0,0 +1 @@ +check {repo} --rule "Each BENCH must have exactly one Goal and hypothesis chapter." --only-rule diff --git a/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/expected.exit b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/expected.exit new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/expected.exit @@ -0,0 +1 @@ +2 diff --git a/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/expected.stderr b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/expected.stderr new file mode 100644 index 000000000..776524909 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/expected.stderr @@ -0,0 +1 @@ +error: chapter name must be a non-empty NAME with no surrounding whitespace; accepted form: Each FS must have exactly one requirements chapter. NAME forbids whitespace anywhere. diff --git a/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/expected.stdout b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/expected.stdout new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/expected.stdout @@ -0,0 +1 @@ + diff --git a/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/docs/bench/BENCH-001-example-benchmark.md b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/docs/bench/BENCH-001-example-benchmark.md new file mode 100644 index 000000000..1e51e50cb --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/docs/bench/BENCH-001-example-benchmark.md @@ -0,0 +1,5 @@ +# BENCH-001-example-benchmark: Example benchmark + +## goal: Goal and hypothesis + +This chapter cites §GOAL-001-example-goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/docs/goals/GOAL-001-example-goal.md b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/docs/goals/GOAL-001-example-goal.md new file mode 100644 index 000000000..2bb2dee49 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/docs/goals/GOAL-001-example-goal.md @@ -0,0 +1,3 @@ +# GOAL-001-example-goal: Example goal + +The benchmark explains its goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/docs/references.md b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/docs/references.md new file mode 100644 index 000000000..2e744b6b8 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/docs/references.md @@ -0,0 +1 @@ +The scenario exercises §BENCH-001-example-benchmark, §RULE-012-bench-goal and §RULE-013-bench-goal-citation. diff --git a/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/docs/rules/RULE-012-bench-goal.md b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/docs/rules/RULE-012-bench-goal.md new file mode 100644 index 000000000..2a38ed4b8 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/docs/rules/RULE-012-bench-goal.md @@ -0,0 +1,3 @@ +# RULE-012-bench-goal: Each BENCH must have exactly one goal chapter. + +Keep the benchmark grounded in §GOAL-001-example-goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/docs/rules/RULE-013-bench-goal-citation.md b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/docs/rules/RULE-013-bench-goal-citation.md new file mode 100644 index 000000000..4c5780762 --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/docs/rules/RULE-013-bench-goal-citation.md @@ -0,0 +1,3 @@ +# RULE-013-bench-goal-citation: The goal chapter of each BENCH must cite at least one GOAL. + +Make the goal explicit with §GOAL-001-example-goal. diff --git a/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/grund.toml b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/grund.toml new file mode 100644 index 000000000..c06fef45b --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/repo/grund.toml @@ -0,0 +1,22 @@ +grund_config_version = 1 +project_name = "chapter-diagnostic" +[reference] +strict = true +[id] +format = "{kind}-{slug}" +named_sections = true +[[kinds]] +kind = "BENCH" +folder = "docs/bench" +index = false +[[kinds]] +kind = "GOAL" +folder = "docs/goals" +index = false +[[kinds]] +kind = "RULE" +folder = "docs/rules" +index = false +rules = true +[scan] +include = ["docs"] diff --git a/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/spec.refs b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/spec.refs new file mode 100644 index 000000000..dd25eedbd --- /dev/null +++ b/tests/e2e/cases/check-chapter-diagnostic-whitespace-refusal/spec.refs @@ -0,0 +1,3 @@ +FS-rules.3.5.1 +FS-rules.4 +FS-errors.3 diff --git a/tests/e2e/cases/check-rules-chapter-absent-paired/expected.stdout b/tests/e2e/cases/check-rules-chapter-absent-paired/expected.stdout index dcf9fad37..7090ff6c6 100644 --- a/tests/e2e/cases/check-rules-chapter-absent-paired/expected.stdout +++ b/tests/e2e/cases/check-rules-chapter-absent-paired/expected.stdout @@ -1,2 +1,2 @@ -docs/fs/FS-silent.md:1: error: FS-silent has 0 requirements chapters; RULE-presence requires at least one +docs/fs/FS-silent.md:1: error: FS-silent has 0 requirements chapters; RULE-presence requires at least one; expected display name "requirements" (case-insensitive, not section handle); observed direct chapters: none docs/fs/FS-silent.md:1: error: FS-silent has no requirements chapter, so RULE-requirements cannot reach it; add the chapter, or narrow the rule to the declarations that have one; this became an error in grund 0.16.0 diff --git a/tests/e2e/cases/check-rules-chapter-block-closer-live/expected.stdout b/tests/e2e/cases/check-rules-chapter-block-closer-live/expected.stdout index 55cd60159..45f69b63c 100644 --- a/tests/e2e/cases/check-rules-chapter-block-closer-live/expected.stdout +++ b/tests/e2e/cases/check-rules-chapter-block-closer-live/expected.stdout @@ -1 +1 @@ -docs/ar/AR-none.md:1: error: AR-none has 0 Terms chapters; RULE-ar-terms requires exactly one +docs/ar/AR-none.md:1: error: AR-none has 0 Terms chapters; RULE-ar-terms requires exactly one; expected display name "Terms" (case-insensitive, not section handle); observed direct chapters: none diff --git a/tests/e2e/cases/check-rules-chapter-source-declaration-reached/expected.stdout b/tests/e2e/cases/check-rules-chapter-source-declaration-reached/expected.stdout index 00600aaf8..4f70d1b7f 100644 --- a/tests/e2e/cases/check-rules-chapter-source-declaration-reached/expected.stdout +++ b/tests/e2e/cases/check-rules-chapter-source-declaration-reached/expected.stdout @@ -1,2 +1,2 @@ -docs/ar/AR-markdown.md:1: error: AR-markdown has 0 Nonexistent chapters; RULE-nonexistent requires exactly one -src/inline.rs:1: error: AR-inline has 0 Nonexistent chapters; RULE-nonexistent requires exactly one +docs/ar/AR-markdown.md:1: error: AR-markdown has 0 Nonexistent chapters; RULE-nonexistent requires exactly one; expected display name "Nonexistent" (case-insensitive, not section handle); observed direct chapters: "terms: Terms" +src/inline.rs:1: error: AR-inline has 0 Nonexistent chapters; RULE-nonexistent requires exactly one; expected display name "Nonexistent" (case-insensitive, not section handle); observed direct chapters: "terms: Terms"