EARS requirement-grammar check (FR-042) - #7
Merged
Conversation
Add a deterministic requirement-grammar framework with EARS as its first grammar, bound to archetypes via the existing (previously unconsumed) `grammar_ref` manifest hook. Findings route into the validation result by severity — advisory `warning` by default (never blocks), promotable to `error` — so EARS can ship advisory during corpus conversion and enforce afterward without moving subsystems. - src/grammar/: framework (GrammarFinding/GrammarSeverity/dispatch) + EARS grammar (6-pattern classifier; non-singular/missing-subject/vague-response/ non-canonical-trigger/unclassifiable checks; FR/NFR/StR dialects; fence/quote/table-cell-aware statement segmentation). - validate_document: ValidationReason::Grammar + severity routing; runs on the resolved archetype so a --archetype override on a typeless doc is still checked. - python: check_grammar PyO3 binding surfaces findings directly. - spec: FR-042 + AC→TC (TC-657..666), matrix to 360/360, US-014 trace. Bound only to FR Description/Behavior/Constraints, NFR Statement, StR Stakeholder Need. Passive voice is a deliberate non-goal. Also sweeps two stale `artifact_type:` frontmatter fixtures to `type:` (type-rename cleanup). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review/calibration follow-up. The flat verb lexicon over-flagged: vagueness is a property of the OBJECT, not the verb — `provide an endpoint`, `process push events`, `handle X by Y` are concrete despite a "vague" verb. Across the corpus the old rule fired 791×, ~55% of them `provide` on concrete CRUD/API objects. vague-response is now object-aware: a verb is flagged only when its object is abstract (`provide flexibility`, `handle errors gracefully`) or absent. A concrete object surface or a mechanism/numeric qualifier suppresses it. `be able to` (capability-not-behavior) is the one verb-intrinsic case and is always flagged. Corpus: 791 → 216 vague-response findings (concrete CRUD/event uses no longer flagged), calibrated against the full corpus. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-042) Object-aware vague-response follow-up. A backticked identifier in the response object (`provide `CodeBlockEditor``, `support `ecvector` opclasses`) is the most concrete object there is — but `normalize()` was stripping backticks before the check, so named code objects fell back to the noun lexicon and leaked as false positives. Keep backticks; treat a backticked object as a concrete signal, so authors can reference things outside the fixed noun lexicon. Corpus: vague-response 216 → 173 (43 named-identifier leaks suppressed). The residual is be-able-to capability phrasing + genuinely-abstract objects. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… (FR-043) Concrete grammar vocabulary is now DATA, not engine (ADR 0009). The EARS object-aware vague-response check (FR-042) no longer carries a hardcoded ~60-term software-noun list (`re_concrete_noun` removed); concrete terms come from a mergeable manifest `lexicon:` registry, exactly like edge_types/roles (FR-040). - vocab/manifest/loader/registry: `lexicon` registry merged first-wins across modules (DuplicateLexiconTerm advisory; load_strict escalates); Registry::lexicon() + a precompiled lexicon_matcher() built once at load. - grammar: vague_verb consumes the matcher; the bounded vague-quality lexicon, mechanism/bound qualifiers, and backtick hatch stay in-engine (generic, not domain vocabulary). - run_grammar threads it: registry-backed validate_document_in_registry passes the merged lexicon; type-only validate_document passes empty (documented degradation). check_grammar PyO3 binding gains module_root. - spec: FR-043 + ADR 0009 + matrix to 367/367 (TC-667..673). High recall kept (flag-unless-concrete, NOT flipped). Baseline software terms land in the spec-objects-* modules next (distributed, per ADR 0009). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… (FR-043) - GrammarLexicon matches the regular plural (`endpoint`→`endpoints`); corpus objects are frequently plural (mirrors the old concrete-noun `s?` rule). - check_grammar's module_root accepts a search root (merging child modules) as well as a single module dir, like the CLI — so the merged project lexicon applies for the corpus report and review lens. Corpus re-baseline with the distributed spec-objects-* lexicons: vague-response 149 (vs 173 under the old hardcoded engine list) — a strict superset, so no regressions, and the soft-tail concretes (pagination/deployment/token) are now suppressed too. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GrammarLexicon now contributes both singular and plural forms per term, including irregular plurals (`policy`→`policies`, `box`→`boxes`, `endpoint`→`endpoints`) — corpus objects are frequently plural and the prior `s?` missed `-ies`/`-es`. Corpus re-baseline (with the module top-up): vague-response 149 → 122. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A repo's own authored glossary terms now suppress in its grammar check, alongside the merged module lexicon (FR-043). The immutable Registry is unchanged: project terms — which vary per repo — are composed into an ad-hoc GrammarLexicon at validation time and injected. - corpus::glossary_terms(&Spec): harvests the `Term` column of every `## Terms` table and the bold term of every `## Ubiquitous Language` bullet (reuses query::table_from_section + parse_bullet_list). - Registry::lexicon_with(extra): composes module keys ∪ project terms. - validate_document_in_registry_with_lexicon: injects an explicit lexicon (the existing entry point delegates to it with the module lexicon — one shared body). - corpus::validate_bundle harvests the Spec once and validates each doc with the combined lexicon; advisory (never a bundle error). - spec: FR-044 + integrity SpecReview + matrix to 374/374 (TC-674..680), authored via /specify → /spec-matrix → /spec-review. Also splits the FR-043 TC tests so each TC-id maps to its own test function. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…y (FR-044) Code-review follow-up: tc674_675 bundled two TCs (the one-test-per-TC convention missed in the earlier split) — now tc674 (Terms table) + tc675 (Ubiquitous Language) via a shared fixture. Add malformed_glossary_harvests_ nothing for CON-2's malformed half (## Terms heading, no table → zero terms, no panic), which TC-680 (absent glossary) did not cover. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The CLI per-validate path built a whole Spec (parse + graph-resolve every file under --scope) just to read a few glossary tables. Add glossary_terms_from_path: enumerate *.md, raw-read each, and parse ONLY the docs whose text carries a `## Terms` / `## Ubiquitous Language` heading (cheap has_glossary_heading line scan). The Spec-based glossary_terms stays for the corpus path (validate_bundle), where the Spec is already loaded. Tests: has_glossary_heading_gates_parsing (the parse-skip gate) + glossary_terms_from_path_scopes_to_glossary_docs (one glossary among many plain FRs → exactly the glossary terms, parity with the Spec-based harvester). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Completes the AGPL relicense (merged from main): cargo-deny rejected the crate's own AGPL-3.0-or-later license since the allow-list is permissive-only. Add a per-crate exception for quire-rs so the License Check passes WITHOUT permitting AGPL in third-party deps. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a deterministic requirement-grammar framework to quire-rs with EARS (Easy Approach to Requirements Syntax) as its first grammar, bound to archetypes via the existing (previously unconsumed)
grammar_refmanifest hook.Findings route into the validation result by severity — advisory
warningby default (never blocks), promotable toerror— so EARS ships advisory during corpus conversion and enforces afterward without moving subsystems.Surface
src/grammar/— framework (GrammarFinding/GrammarSeverity/dispatch) + EARS grammar (6-pattern classifier; non-singular / missing-subject / vague-response / non-canonical-trigger / unclassifiable checks; FR/NFR/StR dialects; fence/quote/table-cell-aware segmentation).validate_document—ValidationReason::Grammar+ severity routing; runs on the resolved archetype (so--archetypeon a typeless doc is still checked).python—check_grammarPyO3 binding surfaces findings directly.Bound only to FR Description/Behavior/Constraints, NFR Statement, StR Stakeholder Need. Passive voice is a deliberate non-goal. Also sweeps two stale
artifact_type:fixtures totype:.Verification
fmt-check,clippy -D warnings, 363 lib tests,audit-unsafe, and the Python binding test (fresh wheel) all green. Verified end-to-end on real corpus FRs.Ordering
Foundation of the FR-042 series. Downstream PRs (quire-cli
--summary, quoin lens/evals, spec-artifacts-process enum, spec-artifacts-iso skeletons) consume this; quire-cli is release-coupled to the tag bump of this engine.🤖 Generated with Claude Code