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
Commit a1840d7
Browse filesBrowse the repository at this point in the historyBrowse files
docs(adr): renumber to ADR-0137; cite the rulings the record was silent about
`0136` is claimed by PR #18480's `0136-declared-journeys-as-priority-anchor.md`,
added ~42 hours earlier. `scripts/check-adr-anchors.mjs` prescribes the NEW
record taking the next free number, and renumbering an already-accepted record
was ruled out — before it is referenced is the only cheap moment. `0137`
re-verified free: absent from `docs/adr/` on `origin/main` (which tops out at
0135) and claimed by none of the 31 open PRs, scanned through the added-file
list of each. The scan lit twice on `0136`, so the zero is a reading.
Three corrections the record owed:
- **Status**: this record declares and implements nothing. D1's authoring
refusal is decision batch #122 item 2's, carried by PR #18638 under one
ADR-0087 id; D2–D4 are consumer-delivered in objectui#8069.
- **Scope boundary**: the gate-slot conversion is RULED and IN FLIGHT, not
"filed as a follow-up" — the dangling sentence is gone. The record's claim
that converting them "would bake a direction the ruling did not give" is true
only of batch #119, and is now stated as what it is: a statement about which
ruling authorizes what, not a reason the conversion should wait.
- **The hand enumeration is replaced by a citation of #15811's census**, because
the hand list had already rotted: it omitted
`system/settings-manifest.zod.ts:424` and `:686`, both
`visible: SettingsVisibilityInputSchema`. Measured through
`SettingsManifestSchema.safeParse` on the built dist: all six refused
spellings (`ast`-only, blank `source`, blank bare string × both slots) are
ACCEPTED, while a grammar-violating source is REFUSED with `custom@visible`
and `custom@specifiers.0.visible` — so the refinement is live at both slots
and narrows neither arm.
Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh
Co-authored-by: Claude <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/adr/0089-unify-visibility-predicate-naming.md
+7-7Lines changed: 7 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,13 +8,13 @@
8
8
9
9
---
10
10
11
-
> **Addendum (2026-09-18, #17778) — what the `*When` family does when it cannot RUN is decided by [ADR-0136](./0136-predicate-fault-semantics-are-contract.md), not here.**
12
-
> This record unified the family under one NAME and is unchanged by that one. ADR-0136 D1 holds a
13
-
> predicate slot to what the engine can actually run (the `FieldSchema` field-rule triad composes
14
-
> `PredicateInputSchema`, so an `ast`-only envelope and a blank `source` are refused at authoring);
15
-
> D2 refuses the SUBMIT loudly on a fault, naming the field and the rule; D3 keeps visibility
16
-
> fail-OPEN at RENDER. A reader who arrived here asking what a broken `visibleWhen` does should
17
-
> read that record.
11
+
> **Addendum (2026-09-18, #17778) — what the `*When` family does when it cannot RUN is decided by [ADR-0137](./0137-predicate-fault-semantics-are-contract.md), not here.**
12
+
> This record unified the family under one NAME and is unchanged by that one. ADR-0137 D1 holds a
13
+
> predicate slot to what the engine can actually run — an `ast`-only envelope and a `source` blank
14
+
> after trimming are refused at authoring, which is the field-rule row of the rule decision batch
15
+
> #122 item 2 gave across every evaluated slot; D2 refuses the SUBMIT loudly on a fault, naming the
16
+
> field and the rule; D3 keeps visibility fail-OPEN at RENDER. A reader who arrived here asking what
17
+
> a broken `visibleWhen` does should read that record.
Copy file name to clipboardExpand all lines: docs/adr/0137-predicate-fault-semantics-are-contract.md
+99-51Lines changed: 99 additions & 51 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
-
# ADR-0136: A predicate's FAULT semantics are part of the protocol — loud at submit, fail-open at render, and a blank predicate is a declared third state
1
+
# ADR-0137: A predicate's FAULT semantics are part of the protocol — loud at submit, fail-open at render, and a blank predicate is a declared third state
2
2
3
-
**Status**: Accepted (2026-09-18) — **D1 implemented here**(`PredicateSchema` / `PredicateInputSchema` compose the evaluated rule; the `FieldSchema` field-rule triad binds them; ADR-0087 D3 entry `field-rule-predicate-evaluated-slot-source-required`). **D2 / D3 / D4 are declared here and delivered by consumers** — the renderer and submit path in objectui#8069, which is `pm:blocked` on this record. D4's spec-side authoring refusal for the action / visibility GATE slots is deliberately **not** in this record's PR; see [Scope boundary](#scope-boundary-what-this-record-does-not-land).
3
+
**Status**: Accepted (2026-09-18) — **this record declares; it implements nothing.**D1's authoring refusal is ruled by decision batch #122 item 2 (card #15811, [`5644350409`](https://github.com/objectstack-ai/objectstack/issues/15811#issuecomment-5644350409), 2026-09-12) and lands in PR #18638, which owns the accept-set narrowing across every evaluated slot under one ADR-0087 id — this record's own PR carries **no** schema change, by decision batch #160 item 1 (2026-09-18: 「同意」 to **A**). **D2 / D3 / D4 are declared here and delivered by consumers** — the renderer and submit path in objectui#8069, which is `pm:blocked` on this record. See [Scope boundary](#scope-boundary-what-this-record-does-not-land) for what this record does not land and who lands it.
4
4
**Deciders**: ObjectStack Protocol Architects (maintainer ruling on objectui#8069, decision batch #119 item 3, 2026-09-12: 「同意」 to **A**, with **Q2 yes** and **Q3 yes**), filed as objectstack#17778 by the director seat
5
5
**Builds on**: [ADR-0058](./0058-expression-and-predicate-surface.md) (the expression & predicate surface — its D5 predicate failure tiers are the table this record writes the field-rule row of), [ADR-0089](./0089-unify-visibility-predicate-naming.md) (unified the `*When` family under one NAME; this record decides what that family does when it cannot RUN), [ADR-0087](./0087-metadata-protocol-upgrade-contract.md) (conversion-over-notification — D1 lands as a D3 semantic entry because no D2 conversion exists), [ADR-0124](./0124-server-enforces-client-is-courtesy.md) (D1 server-enforces — why a render-side direction is never the whole answer), [ADR-0078](./0078-no-silently-inert-metadata.md) (no silently-inert metadata — a predicate that cannot run is the purest case), [ADR-0049](./0049-no-unenforced-security-properties.md) (enforce-or-remove), [ADR-0032](./0032-unified-expression-layer.md) (the CEL layer these predicates are written in)
6
6
**Consumers**: `@objectstack/spec` (`shared/expression.zod.ts` — the predicate contract; `data/field.zod.ts` — the field-rule triad), `@objectstack/objectql` (`validation/rule-validator.ts` — the server-side enforcer whose three fault directions this record measured), `@objectstack/lint` (`validate-expressions.ts`, `validate-visibility-predicates.ts` — the author-time reporters), and the ObjectUI form renderer + submit path (objectui#8069)
@@ -15,13 +15,17 @@ named two. The missing state is **"authored, but the engine cannot run it"** —
15
15
because nothing named it, every layer resolved it to its own local fallback and
16
16
none of those fallbacks was the author's.
17
17
18
-
| state | before | after this record|
18
+
| state | before | after this decision|
19
19
|---|---|---|
20
20
| absent | no rule | no rule (unchanged) |
21
21
| authored and evaluable | the author's verdict | the author's verdict (unchanged) |
22
22
|**authored, blank**| parsed, then silently no-op'd |**refused at authoring** (D1) |
23
23
|**authored, not evaluable**| each layer's own fallback, silently |**refused at submit, loudly** (D2) |
24
24
25
+
"After this decision", not "after this PR": every row of the right-hand column is
26
+
carried by someone else — D1 by PR #18638 under decision batch #122 item 2, D2–D4
27
+
by the consumers in objectui#8069. This record is the contract, not the landing.
28
+
25
29
**Decision:** a predicate's fault semantics are **protocol**, not renderer choice.
26
30
A predicate slot accepts only what the engine can actually run (D1). A fault at
27
31
**submit** refuses the write and names the field and the rule (D2). A fault at
@@ -68,39 +72,64 @@ migration prescription: removing such a key is behaviour-preserving, which makes
68
72
it a safe default — and a dishonest one to reach for without noticing that it
69
73
records a rule that never ran.
70
74
71
-
### The narrowing mechanism already existed
72
-
73
-
This record introduces no new validation machinery. `EvaluatedExpressionSchema`
74
-
and `EvaluatedExpressionInputSchema` already spell "an evaluated slot is held to
75
-
what the engine can actually run", already publish one sentence for it
76
-
(`EVALUATED_EXPRESSION_SOURCE_REQUIRED`), and `FlowEdgeSchema.condition` already
77
-
composes them for exactly this defect one family over. What was missing is that
78
-
`PredicateSchema` / `PredicateInputSchema` — the aliases whose entire purpose is
79
-
to mark a slot as a predicate — composed the **persistence** contract, whose rule
80
-
is "`source` OR `ast`". The alias that means "this will be evaluated" pointed at
81
-
the schema that does not require evaluability.
75
+
### The narrowing mechanism already existed, and it already has an owner
76
+
77
+
This record introduces no validation machinery and carries none.
78
+
`EvaluatedExpressionSchema` and `EvaluatedExpressionInputSchema` already spell
79
+
"an evaluated slot is held to what the engine can actually run", already publish
80
+
one sentence for it (`EVALUATED_EXPRESSION_SOURCE_REQUIRED`), and
81
+
`FlowEdgeSchema.condition` already composes them for exactly this defect one
82
+
family over.
83
+
84
+
Generalising that composition to the rest of the evaluated slots was ruled six
85
+
days before this record, on its own card: **decision batch #122 item 2**
0 commit comments