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
{{ message }}
Repository navigation
ADR-0136 D4 is declared ahead of enforcement: 21 action/visibility gate slots still accept an ast-only or blank-source predicate #19000
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.
Closing as a duplicate of #15811, which is older, already ruled, already dispatched and already carried by a PR in flight.
What I got wrong. I filed this card as an open follow-up asking whether the gate-slot conversion "should be converted, and on whose authority". That question was already answered six days before I asked it: card #15811 carries the maintainer's ruling in comment 5644350409 (2026-09-12T07:02:53Z, decision batch #122 item 2, 「同意」 to 1A·2A·3A·4A·5A), whose item 1 rules that EvaluatedExpressionSchema composes into every evaluated slot the census found — naming the field, option and grid-column visibleWhen / readonlyWhen / requiredWhen, object visibleWhen / disabledWhen, action visibleWhen / visible / ActionConditionInputSchema, app visible, settings visibility and ui/bulk-action visibility, 「⛔ By identity on all 36 positions」. #15811 is pm:dispatched to os-litant, and PR #18638 (claude/issue-15811-evaluated-slot-narrowing, head 70407326463d08f8d0c2e6419037328ea1bf93fd) implements it.
So this card did not merely duplicate an open question — it re-opened a settled ruling. That is the more serious half of the error and it is the reason this closes rather than being narrowed and kept.
Scope comparison, since that decides which one closes.#15811 covers all 36 evaluated declaring positions under a standing ruling; this card covered 21 gate and inline-column slots and proposed three options for a direction already given. This card's scope is a strict subset. #15811 stays open; this one closes.
What was carried over, so closing loses nothing. The one measurement here that I did not find stated on #15811 — the declared fault directions, 4 of 21 slots declaring one and disagreeing 3-to-1 (fail-closed at data/object.zod.ts:1194, data/object.zod.ts:1387, ui/bulk-action.zod.ts:209; fail-soft at data/object.zod.ts:1390), plus the system/settings-manifest.zod.ts:424 and :686 spelling note — is now on #15811 as comment 5728924245, posted as evidence only with no change to that card's labels, assignee or state.
How the duplicate happened, recorded so the mechanism is visible rather than just the outcome. The filing protocol has the filer attach dedup words and the triage seat run them. I attached five — gate predicate, visibleWhen, ExpressionInputSchema, evaluated slot, blank predicate — and did not run them myself. #15811's own title contains ExpressionInputSchema, visibleWhen and the phrase "ast-only or blank-source envelope", so three of the five would have hit it on the first query. I also wrote in this card's own dedup section that a zero on those words would not be evidence of no duplicate, which was the right caution aimed at the wrong risk: the words were fine, the omission was running them at all.
Not reopening this under a narrowed scope. If the settings-manifest spelling turns out to fall outside #18638's census after all, that belongs on #15811 as a scope note, not as a second card.
Closed by the domain:spec seat 3 (Seat: domain:spec#3) that filed it, 2026-09-18T10:48Z.
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