Filed by the director seat on the maintainer's ruling for objectui#8069 (decision batch #119 item 3, 2026-09-12: 「同意」 to A — Q2 yes, Q3 yes). ⛔ packages/spec and docs/adr/** are the domain:spec seat's and a governed surface; the director seat writes no repo file.
Governing text
- Maintainer standing ruling (2026-09-10): 「我们的项目以objectstack 协议为准…协议不正确的应该先修改协议。」
- SKILL.md four-axis ③: 「声明即强制,绝不让 AI 声明一个运行时不兑现的能力」; objectui#4051 / objectstack#5149: a predicate fault is never silent.
- objectui#8069 — measured on objectui
main: resolveFieldRuleState evaluates visibleWhen / readonlyWhen / requiredWhen with fallbacks true / false / false when a predicate cannot be evaluated, so one misspelled column in one predicate produces a form that shows more, locks less and demands less, all at once, with no state that says "this rule did not run". PR objectui#8904 (merged) named the three directions, made a BLANK predicate warn on the field-rule path, and documented the five fault strategies that share evalFieldPredicate. The direction itself was argued once, per key, at introduction (objectui#1578: "faults are safe") and never for the composite.
- Two hard constraints from that thread, carried verbatim into this card: (1) objectui#6958 deliberately relies on visibility fail-open — a broken predicate must never silently null a stored column; (2) fault strategies 4 (fault → flag,
listConditional.ts asking true and false and diffing) and 5 (fault → throw, evaluateCelCondition under throwOnError) exist only because the helper's fallback is freely specifiable — ⛔ no ruling may bake a direction into the helper.
- A second silence, higher up:
ExpressionEvaluator.evaluateCelCondition returns true for a blank source before calling the helper (action / visibility gates) with no diagnostic.
What the protocol must say (Q2 = yes)
The fault semantics of a form-view field-rule predicate are part of the contract (ADR-0089 territory, the expression / predicate contract), ⛔ not an objectui implementation detail:
- A field-rule predicate that faults at evaluation refuses the SUBMIT, loudly, naming the field and the rule — the "loud but safe" middle state (Q3 = yes). Nothing is written; the author (and the user, in the form) sees which rule could not run. This is the mainstream shape: a validation formula that errors blocks the save with the error, rather than passing.
- At RENDER, visibility stays fail-open — a faulting
visibleWhen shows the field (never hides content, never nulls a stored column — the objectui#6958 constraint); readonlyWhen / requiredWhen at render keep today's directions for display, and the submit-time refusal is what makes the composite safe.
- A blank predicate is a third state, declared: neither "no rule" nor "engine fault"; it is refused at authoring (
os validate / publish) with the field named, and at runtime it takes the fault path above. The producer-side tightening (ExpressionWireSchema .min(1)/.trim()) that objectui#8904 declined is decided HERE, as the protocol's answer, and is an accept-set narrowing (major, ADR-0087 entry).
- The same declaration covers the action / visibility gate path (
evaluateCelCondition's blank guard): a blank or faulting gate predicate is diagnosed, never a silent true.
- The helper-level fallback stays free (constraint 2): the contract is declared at the field-rule and gate layer; strategies 4 and 5 keep their mechanism.
Deliverables
- ADR-0089 amendment (or a new ADR if the spec seat judges the scope larger) recording the four points with the objectui#8069 measurements and the two constraints verbatim.
packages/spec: the predicate contract's docblock states the fault semantics; the ExpressionWireSchema narrowing with its ADR-0087 semantic migration entry (a blank predicate → remove the key; structured TODO for stored metadata).
- Then objectui implements the submit-time refusal and the in-form diagnostic (objectui#8069,
pm:blocked on this card).
⛔ Confidence gaps carried
How many stored predicates in production fault silently today is unmeasured; the loud state will surface all of them — that is the purpose, and it is user-visible, so the objectui half ships with a changeset banner saying so.
Refs
objectui#8069 (thread: 5608155923, 5608240551, 5608851498, 5609465532, 5609504505, 5634112572) · objectui#8904 · objectui#6958 · objectui#4051 / objectstack#5149 · ADR-0089
Filed by the director seat, session_01QsCVSivtpwT6ZXs5Rtvqxe, 2026-09-12T02:5xZ, with Claude Code.
Unblocked-by: #19003 (ruled, closed not_planned) — the line below is the historical record of the block, kept deliberately; the machine-readable Blocked-by: is removed because its target is closed.
Blocked on the decision in #19003, not on code. PR #18985 is complete and its isolated at-tier clause-② review returned PASS (tier verified 88/88 against CONTRACT_REVIEW_TIER), but the review declined to clear ADR-0136 for its approver: the record is silent about ruling batch #122 / card #15811 / PR #18638, which rule and already implement an overlapping narrowing of the same triad from the same base, with 12 files in common. #19003 asks which PR owns the triad and whether the surface gets one ADR-0087 notified id or two. The answer changes how much of PR #18985 survives, so no further dev work is dispatched until it lands.
The PR stays draft, both needs:contract-review carriers stay on, and the assignee stays: the work is done and the PR is held behind a decision, which is what this state is for. Seat: domain:spec#3.
Generated by Claude Code
Filed by the director seat on the maintainer's ruling for objectui#8069 (decision batch #119 item 3, 2026-09-12: 「同意」 to A — Q2 yes, Q3 yes). ⛔
packages/specanddocs/adr/**are thedomain:specseat's and a governed surface; the director seat writes no repo file.Governing text
main:resolveFieldRuleStateevaluatesvisibleWhen/readonlyWhen/requiredWhenwith fallbackstrue/false/falsewhen a predicate cannot be evaluated, so one misspelled column in one predicate produces a form that shows more, locks less and demands less, all at once, with no state that says "this rule did not run". PR objectui#8904 (merged) named the three directions, made a BLANK predicate warn on the field-rule path, and documented the five fault strategies that shareevalFieldPredicate. The direction itself was argued once, per key, at introduction (objectui#1578: "faults are safe") and never for the composite.listConditional.tsaskingtrueandfalseand diffing) and 5 (fault → throw,evaluateCelConditionunderthrowOnError) exist only because the helper's fallback is freely specifiable — ⛔ no ruling may bake a direction into the helper.ExpressionEvaluator.evaluateCelConditionreturnstruefor a blank source before calling the helper (action / visibility gates) with no diagnostic.What the protocol must say (Q2 = yes)
The fault semantics of a form-view field-rule predicate are part of the contract (ADR-0089 territory, the expression / predicate contract), ⛔ not an objectui implementation detail:
visibleWhenshows the field (never hides content, never nulls a stored column — the objectui#6958 constraint);readonlyWhen/requiredWhenat render keep today's directions for display, and the submit-time refusal is what makes the composite safe.os validate/ publish) with the field named, and at runtime it takes the fault path above. The producer-side tightening (ExpressionWireSchema.min(1)/.trim()) that objectui#8904 declined is decided HERE, as the protocol's answer, and is an accept-set narrowing (major, ADR-0087 entry).evaluateCelCondition's blank guard): a blank or faulting gate predicate is diagnosed, never a silenttrue.Deliverables
packages/spec: the predicate contract's docblock states the fault semantics; theExpressionWireSchemanarrowing with its ADR-0087 semantic migration entry (a blank predicate → remove the key; structured TODO for stored metadata).pm:blockedon this card).⛔ Confidence gaps carried
How many stored predicates in production fault silently today is unmeasured; the loud state will surface all of them — that is the purpose, and it is user-visible, so the objectui half ships with a changeset banner saying so.
Refs
objectui#8069 (thread: 5608155923, 5608240551, 5608851498, 5609465532, 5609504505, 5634112572) · objectui#8904 · objectui#6958 · objectui#4051 / objectstack#5149 · ADR-0089
Filed by the director seat,
session_01QsCVSivtpwT6ZXs5Rtvqxe, 2026-09-12T02:5xZ, with Claude Code.Unblocked-by: #19003 (ruled, closed not_planned) — the line below is the historical record of the block, kept deliberately; the machine-readable
Blocked-by:is removed because its target is closed.Blocked on the decision in #19003, not on code. PR #18985 is complete and its isolated at-tier clause-② review returned PASS (tier verified 88/88 against
CONTRACT_REVIEW_TIER), but the review declined to clear ADR-0136 for its approver: the record is silent about ruling batch #122 / card #15811 / PR #18638, which rule and already implement an overlapping narrowing of the same triad from the same base, with 12 files in common. #19003 asks which PR owns the triad and whether the surface gets one ADR-0087 notified id or two. The answer changes how much of PR #18985 survives, so no further dev work is dispatched until it lands.The PR stays draft, both
needs:contract-reviewcarriers stay on, and the assignee stays: the work is done and the PR is held behind a decision, which is what this state is for. Seat:domain:spec#3.Generated by Claude Code