feat: contract-declared required props reach the grammar as required#43
Merged
Conversation
Grammar-constrained decoders skip optional heavy branches. Measured in dspack-studio's record-collection scenario work (2026-07-21): across ~20 live generations on gpt-oss:latest and qwen3-coder:30b, the table's nested `data` rows were NEVER emitted while optional scalar props were, and bounded repair could not recover — the grammar never demanded the prop, so every run exhausted its repairs against "Required prop 'data' is not present". Direct Ollama structured-output test: with `data` required in the schema, the same model fills correct rows first try. A prop descriptor may now declare `required: true`. componentProps() lists flagged props in the props schema's `required`, and the node branch requires `props` itself (an inner requirement never binds when the grammar can drop `props` wholesale). Contracts without flags produce byte-identical schemas. Fail-first: the three new grammar-alignment assertions fail on the previous implementation (props.required undefined; node required ["component"]; a data-less table validates). Full suite: 105 passed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds support for contract-authored “required” component props by ensuring those requirements are represented in the generated JSON Schema in a way that grammar-constrained decoders can’t bypass (by omitting props entirely).
Changes:
- Extend
ContractPropto allowrequired: trueto be declared in contracts. - Update generation-schema construction to (1) add required prop names to the props object’s JSON Schema
requiredlist, and (2) requirepropsat the node level when any required props exist. - Add grammar-alignment tests that fail on the previous behavior and assert both schema shape and validation outcomes.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| src/core/grammar-alignment.test.ts | Adds regression tests proving required props are enforced by the generation schema (including requiring props itself). |
| src/core/generation-schema.ts | Propagates contract required: true into JSON Schema required and makes node-level props required when needed. |
| src/core/contract.ts | Documents and types the new required?: boolean flag on contract prop descriptors. |
| CHANGELOG.md | Documents the new Unreleased behavior and rationale. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
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.
What
A contract prop descriptor may now declare
required: true.componentProps()lists flagged props in the props schema'srequired, and the node branch then requirespropsitself — an inner requirement never binds when the grammar can droppropswholesale. Contracts without flags produce byte-identical schemas.Why
Grammar-constrained decoders skip optional heavy branches. Measured in dspack-studio's record-collection scenario work (2026-07-21): across ~20 live generations on gpt-oss:latest and qwen3-coder:30b, the table's nested
datarows were never emitted while optional scalar props were — and bounded repair could not recover, exhausting every run against "Required prop 'data' is not present". The grammar never demanded the prop, so the sampler never planned it.Direct Ollama structured-output test: with
datamarkedrequiredin the schema, the same model fills correct rows first try.With this fix (linked into dspack-studio) the same prompts produce: a clean first-pass five-row triage table, and a repair run where a prose-list violation is caught by the intent rule and repaired into a filled table — the recordings now shipping in dspack-studio #17, which is draft until this publishes as 0.1.2.
Fail-first evidence
Three new grammar-alignment assertions against the previous implementation:
Full suite after: 105 passed (105);
tscclean. The fourth new assertion (unflagged components keep the fully optional shape) is the no-regression guard.CHANGELOG entry added under Unreleased.
🤖 Generated with Claude Code