spec(v0.4): requiredCategories on required-composition — lifted from the §6 ceiling on T1 evidence (0.4.3) - #36
Merged
Conversation
…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>
Contributor
There was a problem hiding this comment.
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
requiredCategoriesforrequired-compositionwith normative semantics (AND across entries, OR within a category,mindefault 1). - Schema + harness: extend
dspack.v0.4JSON Schema andlib/validatecategory-consistency checks to supportrequiredCategories. - 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 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), | ||
| ); | ||
| } |
This was referenced Aug 7, 2026
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.
The narrow, additive rule-language amendment ratified from the T1 Build evidence: lint-clean surfaces whose
form-controlcarried literal text and no interactive control — structurally valid, semantically empty composition every gate passed. The invariant is an OR across a category's members;requiredSubComponentsANDs 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-emiteval/t1-build-matrix*.json).requiredCategories?: {id, min=1}[]— thirdanyOfarm;additionalProperties: falseintact.lib/validate: entry ids checked against the category registry — same finding class asforbiddenCategories(fixture-proven).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