Skip to content

spec: dspack v0.4 — required-props rule type + component categories (PR-12)#11

Merged
ryandmonk merged 2 commits into
mainfrom
spec/v0.4-governance-refinements
Jul 3, 2026
Merged

spec: dspack v0.4 — required-props rule type + component categories (PR-12)#11
ryandmonk merged 2 commits into
mainfrom
spec/v0.4-governance-refinements

Conversation

@ryandmonk

Copy link
Copy Markdown
Contributor

What

The dspack v0.4 delta — M3 plan Phase 1, implementing ADR-M3-1 (required-props as the fourth typed rule, THE v0.4 headline) and ADR-M3-2 (component categories, same revision, supporting cast) as confirmed at M3 planning (2026-07-03). Strictly additive: 48 added schema leaf paths, 0 removed; a valid v0.3 document with "dspack": "0.4" validates against the v0.4 schema.

Why (the evidence)

78/78 emitter-gate failures across three local model families share one signature: a governance-clean surface whose trigger-button label sits where no v0.3 rule can require it to be and no protocol projection can lift it from (docs/findings.md + gpt-oss addendum, dspack-gen#21; m2-report, dspack-gen#22). required-props makes that a lintable, repairable S3 finding. Categories lift the enumerate-ids ceiling (v0.3 §9) that the Astryx contract (ADR-M3-5, 160+ components) would otherwise hit immediately.

Hand-review surface (the milestone's core review)

  1. spec/dspack-v0.4.md — normative semantics, especially §4.1 (required-props): the within-existence clause (every within node must contain ≥1 component node — mirrors v0.3's requiredProps.on semantics; closes the trigger-with-no-button hole) and the deliberate distinction from required-composition.requiredProps (that field is on-scoped + oneOf-required; the new type checks the node's OWN fields, presence-only allowed, text reachable). Naming note for review: the field name requiredProps appears in both types with different entry shapes — spelled out in §4.1; happy to rename the new type's field (e.g. props) if you prefer.
  2. schema/dspack.v0.4.schema.json — generated as a programmatic transform of v0.3 (diff is exactly the additions): categoryEntry, top-level categories, categories[] on componentEntry/subComponentDescriptor, type enum + required-props if/then block, forbiddenCategories on forbidden-composition.
  3. examples/shadcn-ui.dspack.json (2.0.0 → 2.1.0) — surgical diff (63+/18−, no reformat churn): interactive/overlay registry; memberships on 5 components + 10 sub-components (incl. dropdown-menu-trigger et al. — the agenda's exact non-enumerable case); rule.trigger-carries-label (required-props, anchored button within alert-dialog-trigger; the existing worked example already complies — trigger button carries text: "Delete account" directly); rule.alertdialog-no-nested-overlays (forbiddenCategories, anchored on alert-dialog — forbidden-composition anchors accept component ids only, caught by the validator when I first anchored it on the sub-component).

⚠️ One addition beyond the phase table's letter — veto if unwanted

rule.alertdialog-no-nested-overlays is ADR-M3-3's second deletion-shaped rule for the repair-shape deconfound (and the category form used in anger in the shipped contract). It has independent design grounds (no stacked modals — focus containment) but is not named in the PR-12 line of the phase plan. Attribution note: the pre-registered v0.4-effect check (PR-15) is signature-based and robust to this rule; organic firings are expected to be rare.

Acceptance (in CI, validate.yml)

  • npm run validate — 5 schemas compile; shadcn example valid incl. new category/required-props consistency checks + the v0.4 back-compat strip
  • npm run validate -- --fixtures negative — 16/16 rejected (4 new v0.4 fixtures: empty required-props, unknown within, unregistered category in a rule, unregistered category on a component)

Consumer consequence (expected, scheduled)

The contract sha changes: byte-match drift checks in dspack-gen / dspack-emit / ds-mcp will fail on their next runs until the PR-13/14 syncs land (PR-14b also adds v0.4 to ds-mcp's loader version allowlist). This is the planned Phase 1 sequence, not a surprise.

🤖 Generated with Claude Code

…PR-12)

The v0.4 delta (M3 plan Phase 1; ADR-M3-1/ADR-M3-2 as confirmed 2026-07-03):

- required-props rule type (4th typed rule): 'a component must carry named
  content DIRECTLY' — requiredText for the node's own text field, requiredProps
  for directly-present props (oneOf optional); component accepts sub-component
  ids (the one type that does); optional 'within' scope with an existence
  clause mirroring v0.3's requiredProps.on semantics. Driven by the measured
  projection gap: 78/78 gate failures across three model families
  (dspack-gen docs/findings.md + addendum).
- component categories: contract-defined registry (no baked-in taxonomy),
  categories[] membership on componentEntry and subComponentDescriptor,
  forbiddenCategories on forbidden-composition. Categories are contract
  metadata, never surface vocabulary — S2 unchanged.
- shadcn contract v0.4 (2.1.0): interactive/overlay registry; memberships on
  5 components + 10 sub-components; rule.trigger-carries-label
  (required-props); rule.alertdialog-no-nested-overlays (forbiddenCategories —
  ADR-M3-3's second deletion-shaped rule, flagged in the PR for review).
- spec/dspack-v0.4.md (delta over v0.3, normative semantics),
  spec/migration-v0.3-to-v0.4.md, README updates.
- validate.mjs: v0.4 schema registration, category referential checks,
  required-props reference resolution (componentOrSub + within), back-compat
  strip for v0.4; 4 new negative fixtures.

Schema is strictly additive: 48 added leaf paths, 0 removed (v0.3 → v0.4
structural diff in the PR body). validate + negative fixtures green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 3, 2026 19:08

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

This PR introduces the dspack v0.4 spec delta and schema, adding a contract-defined component categories registry (plus membership metadata) and a new typed governance rule, required-props, along with validation-script updates, updated documentation, an updated shadcn/ui example, and new negative fixtures.

Changes:

  • Add v0.4 spec + migration guide documenting required-props semantics and category-based selection via forbiddenCategories.
  • Add v0.4 JSON Schema with new leaf paths for categories and the new rule type.
  • Update validation tooling + example/fixtures/docs to recognize and exercise v0.4.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
spec/README.md Updates spec index to point to v0.4 and the v0.3→v0.4 migration guide.
spec/migration-v0.3-to-v0.4.md New migration guide documenting v0.4’s additive changes and worked examples.
spec/dspack-v0.4.md New v0.4 delta spec defining categories + required-props and forbiddenCategories.
scripts/validate.mjs Extends validation to v0.4 (schema mapping + governance/category consistency checks).
schema/README.md Documents the new v0.4 schema as the current draft schema.
schema/dspack.v0.4.schema.json New v0.4 JSON Schema including categories and required-props.
README.md Updates project-level README to describe v0.4 as the current draft and adds milestone entry.
fixtures/negative/rule-unknown-category-ref.dspack.json New negative fixture: rule references an unregistered category id.
fixtures/negative/rule-required-props-unknown-within.dspack.json New negative fixture: required-props.within does not resolve.
fixtures/negative/rule-required-props-empty.dspack.json New negative fixture: required-props missing both requiredText and requiredProps.
fixtures/negative/component-unknown-category-ref.dspack.json New negative fixture: component membership references an unregistered category id.
examples/shadcn-ui.dspack.json Updates shadcn/ui example to v0.4 and demonstrates categories + new rules.
examples/README.md Updates examples docs to reflect v0.3 governance blocks and v0.4 additions.

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

Comment thread scripts/validate.mjs Outdated
Comment on lines 361 to 365
// Back-compat guarantee: a v0.3+ document minus governance blocks stays valid.
if (GOVERNANCE_VERSIONS.has(doc?.dspack) && errors.length === 0) {
const stripped = { ...doc };
delete stripped.intents;
delete stripped.rules;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 929338b: the 0.4 strip now also removes the categories registry and the membership fields on components and sub-component descriptors, so the stripped document is the pre-governance core shape and the check exercises the real additive guarantee. Comment label updated to "version's additive blocks removed".

Comment thread scripts/validate.mjs
Comment on lines 245 to 249
const ok =
kind === "requiredSubComponents" || kind === "on"
? resolvesToSub
: kind === "component"
? resolvesToComponent

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Declining this one, with reasoning: the require/forbid → component-or-sub resolution is pre-existing v0.3 validator behavior (byte-identical at the m2 tag), and v0.3 rule semantics are frozen (spec §5.5) — tightening it here would be a v0.3 behavior change smuggled into the v0.4 PR. It is also semantically coherent: §5.3 matching is by node id over the surface, sub-component nodes match, and the reference evaluator (dspack-gen rules.ts) implements exactly that, so forbidding e.g. a specific sub-component from ever appearing is a legitimate rule. The schema field description's "Component IDs" prose is loose against §5's referential-integrity text ("component and sub-component references … including composition.subComponents ids"); if that ambiguity should be settled, it belongs in a v0.3 errata/clarification note, not in this delta. Happy to file it as a follow-up issue if the maintainer wants it on the record.

Comment thread spec/dspack-v0.4.md
Comment on lines +80 to +81
- `components.<id>.categories: string[]` — on a component entry.
- `composition.subComponents[].categories: string[]` — on a sub-component descriptor.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 929338b: §3 now states membership arrays MUST be non-empty when present (omit rather than declare []), matching the schema's minItems: 1.

…ision

- validate.mjs: the back-compat strip for 0.4 documents now also removes the
  categories registry and the categories membership fields on components and
  sub-component descriptors, so the check exercises the actual 'v0.2 shape +
  newer dspack version is valid' guarantee instead of passing v0.4 features
  through (Copilot comment 1).
- spec v0.4 §3: membership arrays are non-empty when present (minItems: 1),
  stated in the spec, not only the schema (Copilot comment 3).
- Copilot comment 2 (require/forbid accepting sub-component ids) is declined
  in-thread: pre-existing v0.3 validator behavior (byte-identical at m2),
  frozen semantics, and the reference evaluator matches ids over all nodes —
  a sub-component id in require/forbid is semantically coherent.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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