feat(cxf): tag composite-subset rejections with stable rule ids and name all offenders#172
Merged
Merged
Conversation
…ame all offenders (#165) Every composite-subset CONTRACT rejection now carries a stable composite/<rule-id>: message prefix and names the offending composite node id, so an external CXF generator can map a rejection back to its source graph and the violated rule without any Diagnostic struct change. - New shared rule table (resolve/composite_rules.rs) drives both emission prefixes and the published machine-readable catalog tools/reference-catalog/oce-cxf.composite-rules.json, guarded by a regenerate-and-diff byte golden with UPDATE_EXPECT re-bless. - composite/root-count enumerates all candidate roots in @graph order (subject = first candidate; the zero-candidate case says so explicitly and carries no subject). - composite/contains-cycle names every cycle participant in traversal path order, closing with the re-entered id (still the subject). - composite/replaceable, composite/banned-modelica-key, and composite/array-parameter gain the prefix tag; messages otherwise unchanged. Generic lookup-miss and grounding diagnostics stay untagged by design. - Integration goldens pin the exact (code, subject, message) triple per rule, enumeration determinism, the emitted-prefix tie to the checked-in catalog, and the post-sort order of co-located rejections. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e-rules header (#165) The module //! header linked [COMPOSITE_RULES], but that const is #[cfg(test)]-gated, so the CI doc gate (cargo doc under the normal cfg with -D warnings) failed on an unresolved link. Demote it to plain code formatting. The remaining bracketed links in the file target non-test items, and the render_catalog doc link only renders under cfg(test) where its target exists; both doc gates pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… rejection edges (#165) Three reviewer-verified edge behaviors now carry checked-in pins: - A self-looping composite (R contains A, A contains A) rejects with participant list A -> A, locking the single-element reconstruction slice where start == path.len() - 1. - Two disjoint cycles under one root report exactly one rejection per re-entry, pinned as the full post-finalize_diags vector (subject order) and byte-identical across repeated imports. - A replaceable TOP composite is rejected with the root id as subject while still classifying as the single root: the pinned full vector is that one rejection alone, so no root-count rejection accompanies it on an otherwise-clean document. Test-only; no production changes. Co-Authored-By: Claude Fable 5 <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.
Rung 2 of 7 of the interchange-trio ladder (owner-ratified 2026-07-21) — the diagnostics portion of #165 (the normative doc + conformance corpus is Rung 3; #165 stays open).
What
Every composite-subset contract rejection now names the offending node (subject) and carries a stable, machine-readable rule identity, per the ratified no-struct-change convention:
composite/<rule-id>:message prefixes on the five contract rules —root-count,contains-cycle,replaceable,banned-modelica-key,array-parameter— rendered from a single shared table (resolve/composite_rules.rs);composite.rscontains no prefix literals, so emission and catalog cannot drift.root-count(previously the only subject-less rejection) now enumerates candidate roots in@graphdocument order with the first as subject; the zero-root case says so explicitly.contains-cyclenow names ALL participants in path order (ordered path vector threaded beside theHashSetstack), e.g.[A, B, C, A].tools/reference-catalog/oce-cxf.composite-rules.json(rule id → diag_code + message_prefix + summary), generated from the same table with a regenerate-and-diff byte golden — the same pattern as the R1 registry manifest (feat(blocks): publish a machine-readable registry manifest with a regenerate-and-diff guard #171).UnresolvedReference/GroundingFaileddiagnostics deliberately stay untagged (shared machinery, not contract rules — documented in the README boundary note).Red-team-derived fixtures worth noting
A pure
containsBlockcycle never reaches the cycle detector — it classifies as zero roots andlower()early-returns first. That's now pinned by its own test; the cycle golden uses a root→cycle shape (R→A→B→C→A) with ≥3 distinct participants so the pinned list is non-palindromic (making the order-reversal mutation check meaningful).Tests
13 new (workspace 1189 → 1202): per-rule exact
(DiagCode, subject, message)goldens (multi-root, zero-root, reachable cycle, all six banned keys across bare/S231:/absolute-IRI spellings, array-param, replaceable), enumeration determinism across repeated imports, catalog byte golden + prefix-tie test reading the checked-in artifact, tag uniqueness, and a post-finalize_diags-sort co-location golden.Mutation self-checks (run → caught → reverted): reversed participant order → cycle golden FAIL; swapped rule ids in the table → catalog golden AND prefix-tie test both FAIL.
Gates
Implementer + independent lead re-run, both green: fmt · clippy
-D warnings· nextest 1202/1202 · doctests · file-size cap · determinism-matrix mirror 448/448 release. Per-PR CI compiles oce-cxf tests; they execute in local nextest and at the development→main release gate.🤖 Generated with Claude Code