You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ADR-0136 (docs/adr/0136-predicate-fault-semantics-are-contract.md, landing in PR #18985) declares D4 — "A blank or faulting GATE predicate is diagnosed, never a silent true" — and its own Scope boundary section states that D4's spec-side authoring refusal is deliberately not applied in that PR:
That conversion is filed as a follow-up with the slot inventory, and it is the one place where D4 is currently declared ahead of its authoring-side enforcement.
This card is that follow-up, and it carries the inventory.
Class
Class (c) — a metadata trap. ExpressionInputSchema accepts an envelope carrying ast with a blank or absent source. On the gate slots below that value passes the schema door, and then passes packages/lint too, because validate-visibility-predicates.ts only judges a non-blank source:
and the same function's docblock (:668) records the deliberate opposite position:
A blank predicate is not a fault at all. The front end answers empty for a whitespace-only source, and without this the rule would report visibleWhen: ' ' — which is "no predicate", exactly what the author meant
So an authored gate predicate can be accepted, linted clean, and carry no evaluable source. What the runtime then does on that specific input is not measured here — the adjacent documented failure mode is the over-budget case in the same file, where a gate the front end refuses makes the element "render unconditionally and look exactly like one with no predicate at all (#5149)". Treat the runtime half as the reading this card still owes, not as an established consequence.
Inventory
Instrument: git grep for slot definitions composing ExpressionInputSchema (directly, or via the derived SettingsVisibilityInputSchema / ActionConditionInputSchema) in packages/spec/src, excluding *.test.ts, then a 15-line window per slot for a declared fault direction. Read on origin/main at f347c793e16322a4befc77651d1ab8760bf36874 and on origin/claude/issue-17778-field-rule-predicate-fault-semantics at e1978a0f3b02608d6137ac4daab928540bb52b6b, taken 2026-09-18T10:36Z.
21 slots sit on the wide schema on a gate or inline-column rule path after #18985 narrows the FieldSchema triad. Of those, 19 are named by ADR-0136's Scope boundary and 2 are not:
slot
name
declared fault direction
ui/view.zod.ts:2891
visibleWhen
none
ui/view.zod.ts:2893
visibleOn
none
ui/view.zod.ts:3098
visibleWhen
none
ui/view.zod.ts:3100
visibleOn
none
ui/page.zod.ts:390
visibleWhen
none
ui/page.zod.ts:392
visibility
none
ui/action.zod.ts:336
visibleWhen
none
ui/action.zod.ts:430
visible
none
ui/action.zod.ts:1378
visible
none
ui/action.zod.ts:1393
disabled
none
ui/bulk-action.zod.ts:209
visible
fail-closed
ui/component.zod.ts:741
visibleWhen
none
ui/app.zod.ts:319
visible
none
data/object.zod.ts:1194
visibleWhen
fail-closed
data/object.zod.ts:1387
visibleWhen
fail-closed
data/object.zod.ts:1390
disabledWhen
fail-soft
data/field.zod.ts:387
visibleWhen
none
data/field.zod.ts:884
readonlyWhen
none
data/field.zod.ts:885
requiredWhen
none
system/settings-manifest.zod.ts:424
visible
none — NOT named by ADR-0136
system/settings-manifest.zod.ts:686
visible
none — NOT named by ADR-0136
Paths are relative to packages/spec/src. The two unnamed slots compose SettingsVisibilityInputSchema, which is ExpressionInputSchema.superRefine(...): the refinement adds a check but does not narrow the ast-only or blank-source arm, so the exposure is the same as the named 19. Control for that claim: ADR-0136 mentions settings-manifest or SettingsVisibility 0 times against 10 hits for visibleWhen in the same file, so the zero is read on a lit instrument.
4 of 21 declare a fault direction, and they disagree with each other — 3 fail-closed against 1 fail-soft. 17 declare none at all. This is the load-bearing number for the options below, and it is sharper than the "several" the implementing report carried: one rule cannot serve a 3-to-1 split, and 17 slots have no declared direction to inherit.
Options
A — convert in a follow-up, re-measuring each slot's declared direction first, and rebind packages/lint's validate-visibility-predicates.tscelRefusal, which records the opposite position today. Cost: the 17 direction-less slots each need a direction chosen, which is per-slot product judgment, not a mechanical sweep.
B — leave the gate slots on ExpressionInputSchema permanently and narrow ADR-0136 D4 to the consumer-side diagnostic only (the objectui half), so nothing is declared beyond what is enforced.
Recommendation: A, which is also the implementing report's. ADR-0136's Status line and its Scope boundary both state this half is pending, so the declaration is declared-as-pending rather than claimed-as-done. Note for whoever takes it: A is not one change but two packages — a schema narrowing in packages/spec and a behaviour reversal in packages/lint — and the 17 direction-less slots make it a decision card's worth of product judgment, not a refactor.
I did not run dedup — filing seat attaches words, triage searches. Limits of those words: they are the vocabulary of the fix, not of a symptom an author would report. A card filed from the authoring side ("my visibility rule is ignored", "action button always shows") would share none of these terms, so a zero on all five is not evidence of no duplicate. #15807 and #7327 are the nearest prior art on the same narrowing family and are worth reading before this is priced.
Filed by domain:spec seat 3 as an execution-seat finding: finding only, no domain:* and no priority — those are triage's to set.
What this is
ADR-0136 (
docs/adr/0136-predicate-fault-semantics-are-contract.md, landing in PR #18985) declares D4 — "A blank or faulting GATE predicate is diagnosed, never a silenttrue" — and its own Scope boundary section states that D4's spec-side authoring refusal is deliberately not applied in that PR:This card is that follow-up, and it carries the inventory.
Class
Class (c) — a metadata trap.
ExpressionInputSchemaaccepts an envelope carryingastwith a blank or absentsource. On the gate slots below that value passes the schema door, and then passespackages/linttoo, becausevalidate-visibility-predicates.tsonly judges a non-blank source:origin/main:packages/lint/src/validate-visibility-predicates.ts:988and the same function's docblock (
:668) records the deliberate opposite position:So an authored gate predicate can be accepted, linted clean, and carry no evaluable source. What the runtime then does on that specific input is not measured here — the adjacent documented failure mode is the over-budget case in the same file, where a gate the front end refuses makes the element "render unconditionally and look exactly like one with no predicate at all (#5149)". Treat the runtime half as the reading this card still owes, not as an established consequence.
Inventory
Instrument:
git grepfor slot definitions composingExpressionInputSchema(directly, or via the derivedSettingsVisibilityInputSchema/ActionConditionInputSchema) inpackages/spec/src, excluding*.test.ts, then a 15-line window per slot for a declared fault direction. Read onorigin/mainatf347c793e16322a4befc77651d1ab8760bf36874and onorigin/claude/issue-17778-field-rule-predicate-fault-semanticsate1978a0f3b02608d6137ac4daab928540bb52b6b, taken 2026-09-18T10:36Z.21 slots sit on the wide schema on a gate or inline-column rule path after #18985 narrows the
FieldSchematriad. Of those, 19 are named by ADR-0136's Scope boundary and 2 are not:ui/view.zod.ts:2891visibleWhenui/view.zod.ts:2893visibleOnui/view.zod.ts:3098visibleWhenui/view.zod.ts:3100visibleOnui/page.zod.ts:390visibleWhenui/page.zod.ts:392visibilityui/action.zod.ts:336visibleWhenui/action.zod.ts:430visibleui/action.zod.ts:1378visibleui/action.zod.ts:1393disabledui/bulk-action.zod.ts:209visibleui/component.zod.ts:741visibleWhenui/app.zod.ts:319visibledata/object.zod.ts:1194visibleWhendata/object.zod.ts:1387visibleWhendata/object.zod.ts:1390disabledWhendata/field.zod.ts:387visibleWhendata/field.zod.ts:884readonlyWhendata/field.zod.ts:885requiredWhensystem/settings-manifest.zod.ts:424visiblesystem/settings-manifest.zod.ts:686visiblePaths are relative to
packages/spec/src. The two unnamed slots composeSettingsVisibilityInputSchema, which isExpressionInputSchema.superRefine(...): the refinement adds a check but does not narrow theast-only or blank-sourcearm, so the exposure is the same as the named 19. Control for that claim: ADR-0136 mentionssettings-manifestorSettingsVisibility0 times against 10 hits forvisibleWhenin the same file, so the zero is read on a lit instrument.4 of 21 declare a fault direction, and they disagree with each other — 3 fail-closed against 1 fail-soft. 17 declare none at all. This is the load-bearing number for the options below, and it is sharper than the "several" the implementing report carried: one rule cannot serve a 3-to-1 split, and 17 slots have no declared direction to inherit.
Options
packages/lint'svalidate-visibility-predicates.tscelRefusal, which records the opposite position today. Cost: the 17 direction-less slots each need a direction chosen, which is per-slot product judgment, not a mechanical sweep.ExpressionInputSchemapermanently and narrow ADR-0136 D4 to the consumer-side diagnostic only (the objectui half), so nothing is declared beyond what is enforced.Recommendation: A, which is also the implementing report's. ADR-0136's Status line and its Scope boundary both state this half is pending, so the declaration is declared-as-pending rather than claimed-as-done. Note for whoever takes it: A is not one change but two packages — a schema narrowing in
packages/specand a behaviour reversal inpackages/lint— and the 17 direction-less slots make it a decision card's worth of product judgment, not a refactor.Dedup words
gate predicate,visibleWhen,ExpressionInputSchema,evaluated slot,blank predicateI did not run dedup — filing seat attaches words, triage searches. Limits of those words: they are the vocabulary of the fix, not of a symptom an author would report. A card filed from the authoring side ("my visibility rule is ignored", "action button always shows") would share none of these terms, so a zero on all five is not evidence of no duplicate.
#15807and#7327are the nearest prior art on the same narrowing family and are worth reading before this is priced.Filed by
domain:specseat 3 as an execution-seat finding:findingonly, nodomain:*and no priority — those are triage's to set.Generated by Claude Code