Skip to content

spec(v0.4): requiredCategories on required-composition — lifted from the §6 ceiling on T1 evidence (0.4.3) - #36

Merged
ryandmonk merged 1 commit into
mainfrom
feat/required-categories
Aug 7, 2026
Merged

spec(v0.4): requiredCategories on required-composition — lifted from the §6 ceiling on T1 evidence (0.4.3)#36
ryandmonk merged 1 commit into
mainfrom
feat/required-categories

Conversation

@ryandmonk

Copy link
Copy Markdown
Contributor

The narrow, additive rule-language amendment ratified from the T1 Build evidence: lint-clean surfaces whose form-control carried literal text and no interactive control — structurally valid, semantically empty composition every gate passed. The invariant is an OR across a category's members; requiredSubComponents ANDs exact ids, and category vocabulary existed only on the forbidden side. §6's ceiling anticipated exactly this addition "when a real contract needs them, not before" — this is that need (evidence: dspack-emit eval/t1-build-matrix*.json).

  • Schema: requiredCategories?: {id, min=1}[] — third anyOf arm; additionalProperties: false intact.
  • lib/validate: entry ids checked against the category registry — same finding class as forbiddenCategories (fixture-proven).
  • Spec §4.3: semantics mirror §4.2 — local to each matching node's descendants, min default 1 (the existing convention), AND across entries, OR within a category, findings name category and count. No expressions/oneOf/predicates.
  • Two negative fixtures; both examples green; all existing fixtures still reject; version → 0.4.3.

Evaluation lands in dspack-gen S3 next (separate PR, ten pinned cases per the ratified list). The production contract rule follows once both support it.

🤖 Generated with Claude Code

…iling item lifted by measurement (0.4.3)

The T1 representation milestone's Build evaluation (dspack-emit#31) produced
lint-clean surfaces whose form-control nodes carried literal text and no
interactive control at all — structurally valid, semantically empty form
composition that every gate passed and the emitter correctly refused. The
invariant "must contain an approved interactive control" is an OR across a
category's members; requiredSubComponents can only AND exact ids, and
category vocabulary existed only on the forbidden side (§4.2).

§6's Deliberate Ceiling anticipated exactly this addition "when a real
contract needs them, not before". This is that need, with the probe surfaces
committed as evidence in dspack-emit's eval/.

The amendment, additive and narrow:

- schema: required-composition gains requiredCategories?: {id, min=1}[]
  (third arm of the existing anyOf; additionalProperties stays false; x-*
  preserved on entries);
- lib/validate: each entry's id must be registered in categories — the same
  consistency check forbiddenCategories carries, producing the same class of
  finding (proven by fixture: a typo'd id is rejected against the registry,
  never silently never-matching);
- spec §4.3: normative semantics mirroring §4.2 — per matching node, LOCAL to
  its descendants (a member elsewhere satisfies nothing), min default 1,
  entries AND like requiredSubComponents, membership OR within a category,
  findings name the category and count. No boolean expressions, oneOf, or
  predicates: a requirement a category cannot express is a missing category
  or a different rule, not a grammar extension;
- two negative fixtures (malformed entry refused by schema; unknown category
  refused by consistency);
- §6 ceiling updated: the first item lifted by measurement, recorded as such.

Existing documents remain valid and behaviourally unchanged (both examples
green; all negative fixtures still reject; evaluation of the new field lands
in dspack-gen S3, which per v0.3 §5.4-§5.5 fails loudly on fields it does
not yet understand only for unknown TYPES — an unknown FIELD on a known type
is refused by this schema until tools update, preserving fail-closed).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 7, 2026 16:22
@ryandmonk
ryandmonk merged commit b1763e4 into main Aug 7, 2026
2 checks passed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new category-based requirement arm to the v0.4 required-composition rule family, allowing contracts to require “at least N descendants from category X” to close a demonstrated gap in semantic composition validation.

Changes:

  • Spec: define requiredCategories for required-composition with normative semantics (AND across entries, OR within a category, min default 1).
  • Schema + harness: extend dspack.v0.4 JSON Schema and lib/validate category-consistency checks to support requiredCategories.
  • Validation fixtures + release: add negative fixtures for malformed/unknown category references and bump package version to 0.4.3.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
spec/dspack-v0.4.md Documents the new requiredCategories field and its evaluation semantics.
schema/dspack.v0.4.schema.json Adds requiredCategories as a third valid arm for required-composition rules.
lib/validate.mjs Ensures requiredCategories[].id values are registered in the contract’s categories.
fixtures/negative/rule-required-categories-unknown-category.dspack.json Verifies unknown category ids in requiredCategories are rejected via consistency checks.
fixtures/negative/rule-required-categories-malformed.dspack.json Verifies malformed requiredCategories entries are rejected by schema validation.
package.json Bumps published version to 0.4.3.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/validate.mjs
Comment on lines 180 to 186
for (const rule of doc.rules ?? []) {
checkMember(rule.id ?? "(rule without id)", rule.forbiddenCategories);
checkMember(
rule.id ?? "(rule without id)",
(rule.requiredCategories ?? []).map((r) => r.id),
);
}
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.

2 participants