Skip to content

spec/ADR-0089: a form field-rule predicate that faults refuses the submit loudly; visibility stays fail-open at render; a blank predicate is refused at authoring — fault semantics become part of the contract (objectui#8069 ruling A) #17778

Description

@os-tesla

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:

  1. 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.
  2. 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.
  3. 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).
  4. 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.
  5. 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions