Skip to content

feat(cxf): tag composite-subset rejections with stable rule ids and name all offenders#172

Merged
jscott3201 merged 3 commits into
developmentfrom
claude/cxf-composite-diagnostics
Jul 22, 2026
Merged

feat(cxf): tag composite-subset rejections with stable rule ids and name all offenders#172
jscott3201 merged 3 commits into
developmentfrom
claude/cxf-composite-diagnostics

Conversation

@jscott3201

Copy link
Copy Markdown
Owner

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.rs contains no prefix literals, so emission and catalog cannot drift.
  • root-count (previously the only subject-less rejection) now enumerates candidate roots in @graph document order with the first as subject; the zero-root case says so explicitly.
  • contains-cycle now names ALL participants in path order (ordered path vector threaded beside the HashSet stack), e.g. [A, B, C, A].
  • Checked-in rule catalog 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).
  • Generic UnresolvedReference/GroundingFailed diagnostics deliberately stay untagged (shared machinery, not contract rules — documented in the README boundary note).
  • No behavior changes: same inputs accept/reject identically; acceptance goldens byte-identical (no re-bless).

Red-team-derived fixtures worth noting

A pure containsBlock cycle never reaches the cycle detector — it classifies as zero roots and lower() 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

jscott3201 and others added 3 commits July 21, 2026 19:52
…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>
@jscott3201
jscott3201 merged commit eb29e67 into development Jul 22, 2026
14 checks passed
@jscott3201
jscott3201 deleted the claude/cxf-composite-diagnostics branch July 22, 2026 00:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant