Repository navigation
refactor(sections): gate every schema rule with zod's when instead of cloning its checks - #440
Merged
Merged
Conversation
… cloning its checks
Every section refinement is now authored as `.check(rule(fn))`, where rule() in src/sections/shared/schema-helpers.ts is `z.superRefine(wrapped, { when })` through zod's public API. src/sections/contract/module.ts no longer clones zod's private check objects (reportingBesideFailures), cloneWith no longer remaps checks, validate.ts parses the section shape directly, and the swallowed-throw path with its module-level test seam (observeSwallowedThrows) is gone.
Behaviors kept, each pinned by a test (test/sections/rule.test.ts, test/engine/validate.test.ts, test/sections/raw-sibling.test.ts):
1. a node's own rule runs although a nested value failed, and its finding keeps its path
2. a node refused as a whole (an aborting issue with an empty path) runs no rule
3. a rule's finding under a failed path is dropped; the shape's issue stands there
5. a rule's own `when` still decides, ANDed with the gate
6. a rule composed after loosen() (the routed list shape) reports beside a failed entry, in both container forms
7. a rule's own finding (continue: true) is not a failure; a list-level rule still runs
8. a leaf's length bound is suppressed when the leaf's type failed, so a wrong-typed value reports exactly one issue
Deleted on purpose (behavior 4): a rule that throws beside a nested failure is no longer swallowed. No registered rule throws on a raw value (the raw-sibling sweep proves it across the registry), so a throw is a bug that must surface; the sweep's control now expects the throw.
Counts: 26 rule() sites (22 in the section schemas and branches/index.ts, 2 in repository/schema.ts, 1 in webhooks/schema.ts, requirePlainMapping in module.ts, boundedString in schema-helpers.ts) and 3 length bounds (environments reviewers .max, secret_scanning start/end_delimiter .min) rebuilt as gated $ZodCheckMaxLength/$ZodCheckMinLength through minLength()/maxLength(). Numeric bounds stay zod's own: greater_than/less_than carry no default `when`, so a type failure already skips them. rulesets/schema.ts imports the helper as gatedRule, since its local rule() builds GitHub ruleset rules.
Lint: lint/rule-gate.grit, registered in biome.json for src/** except schema-helpers.ts (where rule() itself is the one superRefine), fails on any bare .refine()/.superRefine(), with or without params or type arguments. The module.ts exemption first proposed was dropped; requirePlainMapping is converted too. test/lint/rule-gate.test.ts pins the verdicts on a fixture tree, and test/lint/fixture-lint.ts (shared with never-throw.test.ts) copies every plugin biome.json names, so the next plugin cannot break the fixture.
Controls: lib/settings.schema.json built here is byte-identical (cmp) to the build at origin/main 674e8c9 and to the pre-change build; every user-visible refusal message is unchanged (test/sections pins them all).
The header named reportingBesideFailures in ../contract/module.ts as the owner of the raw-sibling contract; the previous commit deleted it, and rule() in ./schema-helpers.ts now states the contract. zod 4.6.5 still omits `when` from $ZodCheckMinLengthParams and $ZodCheckMaxLengthParams, so minLength()/maxLength() keep constructing the check objects directly.
Contributor
File size check0 over a hard cap (fails), 36 warning(s).
Split the file, wrap the line, shorten or exempt the comment, or list the path in 5 managed file(s) skipped; repo-platform owns them. |
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The refactor preserves validation semantics with comprehensive focused tests and lint enforcement.
Review effort: Balanced
Findings: None
What changed in this PR
Replaces private Zod check cloning with a shared, gated rule() helper while preserving validation behavior.
Changes:
- Migrates section refinements to gated checks.
- Removes check cloning and swallowed-error handling.
- Adds lint enforcement and focused validation tests.
| File | Description |
|---|---|
biome.json |
Enables the rule-gate plugin. |
lint/rule-gate.grit |
Rejects bare Zod refinements. |
src/engine/validate.ts |
Parses section shapes directly. |
src/sections/actions/schema.ts |
Migrates action rules. |
src/sections/branches/index.ts |
Gates wildcard validation. |
src/sections/branches/schema.ts |
Migrates branch rules. |
src/sections/check_suite_preferences/schema.ts |
Gates duplicate-app validation. |
src/sections/code_quality_setup/schema.ts |
Gates setup validation. |
src/sections/code_scanning_default_setup/schema.ts |
Gates setup validation. |
src/sections/contract/module.ts |
Removes check cloning and throw swallowing. |
src/sections/deploy_keys/schema.ts |
Gates key validation. |
src/sections/environments/schema.ts |
Migrates environment rules and bounds. |
src/sections/interaction_limits/schema.ts |
Gates interaction-limit validation. |
src/sections/pages/schema.ts |
Gates read-only-field validation. |
src/sections/repository/schema.ts |
Migrates repository rules. |
src/sections/rulesets/schema.ts |
Migrates ruleset refinements. |
src/sections/secret_scanning_custom_patterns/schema.ts |
Gates regex and length checks. |
src/sections/shared/raw-values.ts |
Updates rule contract documentation. |
src/sections/shared/schema-helpers.ts |
Adds gated rule and length helpers. |
src/sections/shared/setup-schema.ts |
Updates setup-rule documentation. |
src/sections/webhooks/schema.ts |
Gates webhook validation. |
test/lint/fixture-lint.ts |
Centralizes fixture lint execution. |
test/lint/never-throw.test.ts |
Reuses the lint fixture helper. |
test/lint/rule-gate.test.ts |
Tests refinement lint enforcement. |
test/sections/loosen.test.ts |
Removes obsolete cloning tests. |
test/sections/raw-sibling.test.ts |
Verifies rule exceptions propagate. |
test/sections/rule.test.ts |
Tests gated rule semantics. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
The platform's typos checker reads "ANDed" as a misspelling of "Ded" and fails standard-checks; the title now says the same thing without that word.
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.
Before
After
How
when; the check-cloning, the swallowed-throw path, and its mutable test seam are deleted..check(rule(...)); three length bounds become gated checks of zod's own class, so the published schema and issue codes are unchanged..superRefineor.refineoutside the helper, with a test pinning its verdicts and the fixture list derived from biome.json.Proof
lib/settings.schema.jsonbyte-identical to a build at main.Technical details
whenANDs with the gate; a rule composed after loosen() reports beside failures; a rule's own continue-true finding is not a failure; a leaf bound is suppressed when the leaf's type failed.when, so a type failure already skips them; only the length checks defaulted to "when has length"..min/.maxparams still omitwhen(a probe fails TS2353), so the length checks are constructed directly, the form rulesets already used.schema-helpers.ts, whererule()itself callssuperRefine.superRefinecalled bare is outside the syntactic lint.open()replacingloosen()) follow.src/sections/shared/schema-helpers.ts,src/sections/contract/module.ts,src/engine/validate.ts, 20 section schemas,lint/rule-gate.grit,biome.json,test/lint/{rule-gate.test.ts,fixture-lint.ts,never-throw.test.ts},test/sections/{rule,loosen,raw-sibling}.test.ts,src/sections/shared/raw-values.ts.