spec: hold a predicate to what the engine can run; declare its fault semantics (ADR-0136) - #18985
os-elon-musk wants to merge 16 commits into
Conversation
… fault semantics `PredicateSchema` / `PredicateInputSchema` composed the PERSISTENCE contract (`source` OR `ast`) while a predicate exists to be evaluated and the CEL engine reads `source` alone. An `ast`-only envelope and a `source` blank after trimming parsed, registered, passed `objectstack validate`, then faulted or short-circuited at evaluation time. They now compose the evaluated rule, and the field-rule triad on `FieldSchema` binds to them. The predicate contract's docblock states the fault semantics the consumers are held to: a faulting field-rule predicate refuses the SUBMIT naming the field and the rule; visibility stays fail-open at RENDER; a blank or faulting gate predicate is diagnosed, never a silent true. The evaluation helper's fallback stays freely specifiable. Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh Co-authored-by: Claude <noreply@anthropic.com>
The ADR-0087 D3 semantic entry for the D1 narrowing, the pin test for all three field-rule slots, and the two ADR anchors. ADR-0089 gains a pointer: it unified the `*When` family's NAME, this record decides what the family does when it cannot run. Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh Co-authored-by: Claude <noreply@anthropic.com>
…ed predicate `gen:docs` was the one artifact `check:generated` proved stale; the published reference now states `source` as required on `Predicate` / `PredicateInput`. Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check
What this run could not see
Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
|
`expression()` and `cel()` (hence `F` and `P`) always set a non-blank `source`, but were declared as returning `Expression`, whose `source` is optional. Harmless while no slot required `source`; once the field-rule triad did, a `P` template written straight into `visibleWhen` became a TS2322 — the recommended authoring form for a predicate stopped type-checking in the one place predicates are written (caught by `check:skill-examples` on the docs and skills corpus, and by the examples typecheck on app-showcase). Fixed at the producer, not the call sites: the return type now states what the helper emits. Narrowing a return type removes nothing from a caller — `EvaluatedExpression` is assignable to `Expression`. Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh Co-authored-by: Claude <noreply@anthropic.com>
…eld-rule-predicate-fault-semantics
|
…/main The declaration-text pins main brought in #18971 move with this branch's two facts: `source` becomes required wherever the predicate contract composes, and `cel` / `expression` now declare the `EvaluatedExpression` they always emitted. Exactly 8 distinct changed lines across all six files; the line count is those two facts repeated at every composing site. Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh Co-authored-by: Claude <noreply@anthropic.com>
… schema ADR-0136 D1 binds the `FieldSchema` field-rule triad to `PredicateInputSchema` from another file, and alias resolution in the D7 discovery scan is FILE-LOCAL — so all three surfaces dropped out of discovery and their covers went STALE, 34 positions found against a `head` floor of 37. The ledger header had written this trap down as limitation 2, naming this exact alias as latent; this change makes it live, so the roster gains it and the limitation is corrected to say so. ⛔ No floor was lowered and no ledger row was deleted: the three surfaces still exist and are still exactly what the scan looks for. The roster docblock already prescribed this repair for a new alias. Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh Co-authored-by: Claude <noreply@anthropic.com>
|
PM note — D4's carrier is filed, two numbers in the record are off, and the PR body is stale. None of this is a verdict; the at-tier clause-② review is still running. 1. The follow-up ADR-0136 promises now exists: #19000. The Scope boundary section says of the gate-slot conversion: "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." #19000 is that follow-up and carries the inventory. It is The sentence in the ADR does not cite a number, so the reference is still dangling in the text. Adding 2. Two numbers in the Scope boundary are off, both in the direction that strengthens its own argument. Instrument:
Both readings went to the reviewer as evidence to check, explicitly not as a verdict and explicitly not as grounds to move its PASS or FAIL. 3. The PR body was written once and now predates three developments. It has not been rewritten since it was opened, and three things landed on the branch after it: the DEBT producer fix, the D7 roster entry in 4. Landing posture, restated so it is on the record and not only in a seat post. This PR touches Generated by Claude Code |
Clause-② review returned — PASS with findings, and this PR is HELD. Carriers stay on.Read this as the seat's disposition, not as the record of record. No Review admissibility. Isolated at-tier review, dispatched because this seat serves below Verdict: CLAUSE-② REVIEW: PASS. Accept-set narrowing on the Why it is held anyway — one blocking finding, and it is not a clause-② matter. The reviewer found, and I have independently confirmed against primary sources, that this record is silent about a standing ruling that covers part of its own subject:
So the record's Scope-boundary rationale — that converting the gate slots "would bake a direction the ruling did not give" — is true of batch #119 and silent about batch #122, which gave exactly that direction for those slots. A governed record must not reach its approver with an incomplete account of the standing rulings on its own subject. That is the hold. Also established, separately from the review. This record's ADR number collides: Why no record and no carrier strip on this head. The gate reads the PR's live head ( Not mine to decide, and going to the maintainer. Which binding the record should describe, whether this PR's ADR-0087 entry folds into or cross-references Disposition of the follow-up card I filed earlier today. #19000 is closed as a duplicate of #15811. It re-opened a question batch #122 had already settled. My error, recorded on both cards. Non-blocking, carried for whoever edits the record: the migration entry's Seat: Generated by Claude Code |
…ted slot Decision batch #160 item 1 (letter A, maintainer 「同意」 2026-09-18T11:58Z) on #19003: this PR drops its triad-slot edits, the `PredicateSchema` / `PredicateInputSchema` rebinding, the `field-rule-predicate-evaluated-slot-source-required` ledger entry and every api-surface / reference-page row that existed only because of them. Decision batch #122 item 2 (card #15811) had already ruled the same narrowing across all 36 evaluated slots — the field-rule triad named in its own census — and PR #18638 lands it under ONE ADR-0087 id. Card #17778 ruled fault semantics, not the carrier symbol; nothing ruled is lost. Reverted to the merged-main content byte-for-byte (`git checkout d8b12fc --` for the three sources and the field.zod anchor; empty `git diff` against that tree for each), so the aliases are again plain aliases of the persistence contract, wide, with zero slot users. KEPT, per the same ruling: the producer fix. `cel()` / `expression()` still declare the `EvaluatedExpression` they always emitted — narrowing a return type removes nothing from a caller — with the docblock rewritten so it no longer rests on a triad requirement this commit removes. ⛔ No gate weakened. The pin test and the ADR-0087 entry are removed because the behaviour they recorded no longer happens in this PR, not to get green: the entry file is deleted and `gen:migration-registry` re-emitted `registry.ts`, which is byte-identical to merged main. `check:generated` proved exactly two artifacts stale and `--fix` regenerated only those; the residue against merged main is 4 lines in api-surface-declarations, all of them the kept producer fix, and `content/docs/references/**` is byte-identical. Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh Co-authored-by: Claude <noreply@anthropic.com>
…nt 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>
…erived The roster lists schemas that DECLARE an expression surface — "a slot whose accepted grammar is narrower gets its own schema and must be listed here too". `PredicateInputSchema` earned its place only while ADR-0136 D1 made it `= EvaluatedExpressionInputSchema` and bound the field-rule triad to it from another file. With that reverted it is again a plain alias of `ExpressionInputSchema` typing no slot, so it declares nothing and belongs nowhere on this roster; the ledger header's limitation 2 names it as the standing LATENT example, and leaving it rostered would make that paragraph false. Re-derived rather than assumed, twice, on this branch with the narrowing already reverted, by raising the `head` floor to 9999 through `ablation-replace.mjs` so the assertion prints the count (mutation landed and restore verified both times, `git diff HEAD` empty): roster entry PRESENT -> discovery found 37 position(s) via 'head' roster entry ABSENT -> discovery found 37 position(s) via 'head' Identical, because the three triad positions are head-matched by `ExpressionInputSchema` again. Identity grep over `packages/spec/src/**/*.zod.ts` agrees: `PredicateInputSchema` has 2 hits, its own definition and its `z.input` companion — zero slots — against a lit control of 19 files for `ExpressionInputSchema` and a dark control of 0. ⛔ Not a gate weakening. The `head` floor stays 37 and is met at 37, no ledger row is deleted and no test is skipped: the same three surfaces are discovered through the schema that types them. Both files are byte-identical to merged main. Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh Co-authored-by: Claude <noreply@anthropic.com>
The entry described an accept-set narrowing this PR no longer carries. What publishes now is one producer fix: `cel` / `expression` (and the `F` / `P` aliases) declare the `EvaluatedExpression` they always emitted. No zod schema moves, `api-surface/` is unchanged, no export is added, removed or renamed, and `EvaluatedExpression` is assignable to `Expression`, so no call site changes. ⇒ `patch`, not `minor`; the BREAKING banner and the ADR-0087 disposition marker go with the narrowing that owned them. `Clause-②` is re-judged to `no`: this diff neither widens an accept set nor expands the public surface — measured, `check:api-surface` green with no removed-or-narrowed report. The record's fault semantics (ADR-0137 D2–D5) are restated as what they are: declared here, delivered by consumers, with D1's authoring refusal shipping with #18638 under that change's own ADR-0087 entry. Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh Co-authored-by: Claude <noreply@anthropic.com>
…mber `check-adr-symbol-anchors` refuses a line number as an anchor form — a line is not an identity, and an unrelated edit rots it silently. The two slots this record names are now cited as `packages/spec/src/system/settings-manifest.zod.ts#SpecifierSchema` and `#SettingsManifestSchema`, with the refinement itself as `#SettingsVisibilityInputSchema`. Two readings, both from the gate, in order: `2 finding(s)` (line-anchor) on the first run; then `3 finding(s)` (unresolved-path) when the symbol form was written package-relative rather than repo-relative — the gate distinguishing a bad form from a bad path, which is how the second fix was aimed. Green now: `2105 anchors across 140 records resolve … 0 line anchors survive`. Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh Co-authored-by: Claude <noreply@anthropic.com>
|
PM note — Live head
The gate derives "clause-② YES" from the carrier label, not only from the body line. So this red is the carrier and the body disagreeing, and the gate's own route 2 names the owner: 「the Why the carrier is still hung, deliberately. This PR was reworked under decision batch #160 item 1 (card #19003, letter A): the field-rule triad slot edits, the That means the head that was reviewed no longer exists. The clause-② PASS on record (comment 5729578584) judged ⛔ What will NOT be done to clear it. Raising
No re-run has been spent, and none is warranted: this is not a flake and not another PR's failure. Seat: Generated by Claude Code |
Contract reviewServed-tier: 209/209 ① Derived judgmentsIsolated at-tier re-review. The earlier PASS on this PR judged Ruling A's REMOVE list is measured removed, byte-exactly: 16 non-diff paths of the former 22-file set carry blob SHAs identical to the pinned parent The renumber holds on an independently widened instrument: ② Semver level
③ Boundary flagsGate-weakening: none. Implemented-by: VERDICT: PASS Landing is additionally gated twice over: Non-blocking, carried: ADR-0137's Generated by Claude Code |
…eld-rule-predicate-fault-semantics # Conflicts: # packages/spec/src/shared/expression.zod.ts
…eld-rule-predicate-fault-semantics
…/main Discharges the os-regen deferral from the prior merge commit. root.txt and shared.txt now reflect main's #18638 evaluated-expression-slot narrowing (source becomes required across the composing slots) plus the drift picked up while catching this branch up to main's current tip. No hand edits.
Main shipped the identical /`expression` return-type narrowing to EvaluatedExpression first, under #18638 (card #15811) -- confirmed by the merge: both sides made the same change independently, and ADR-0137's own status line says its PR carries no schema change. Re-announcing that narrowing here would duplicate #18638's own changeset entry in the same release. Drop the redundant paragraphs and keep only what #17778 alone ships: the ADR-0137 predicate fault-semantics contract and its ADR-0089 addendum.
Fixes #17778
Clause-②: no
The
domain:spechalf of the maintainer ruling on objectui#8069 (decision batch #119 item 3, 2026-09-12: 「同意」 to A, with Q2 yes and Q3 yes), reworked under decision batch #160 item 1 on #19003 (letter A, maintainer 「同意」 2026-09-18T11:58Z). The renderer half is objectui#8069 and is not in this PR.What this PR is now — a record, one producer fix, and nothing else
Ruling A removed the schema change from this PR. Decision batch #122 item 2 (card #15811, comment
5644350409, 2026-09-12) had already ruled the evaluated-slot narrowing across all 36 declaring positions — its own census names 「field / option / grid-columnvisibleWhen/readonlyWhen/requiredWhen」 — and PR #18638 owns it under one ADR-0087 id and lands first. Card #17778 ruled fault semantics, not the carrier symbol, so nothing ruled is lost.Removed here (commit
9f30a18a9): the threeFieldSchematriad-slot edits; thePredicateSchema/PredicateInputSchemarebinding, which go back to composing the persistence schemas, wide; thefield-rule-predicate-evaluated-slot-source-requiredADR-0087 entry (the entry file and, throughgen:migration-registry, its registry rows) and the changeset marker; the triad pin test; the two ADR anchors; and every api-surface / reference-page row that existed only because of those.Kept, per the same ruling: ADR-0137 (the fault-semantics record, renumbered — see below), the ADR-0089 pointer addendum, and the producer fix.
Re-derived and removed (see the measurement below): the ADR-0058 D7 roster entry.
The revert is byte-exact, not "close enough"
The three sources and the
field.zodanchor were restored withgit checkout d8b12fca97 --(the merged-main parent of this branch's merge commit, a pinned sha, not a moving ref);git diff d8b12fca97is empty for each.registry.tswas not hand-edited — the entry FILE was deleted andgen:migration-registryre-emitted the generated regions; the result is byte-identical to merged main andcheck:migration-registryis green.After regenerating, the whole diff against merged main is 6 files, and the generated half of it is 4 lines:
content/docs/references/**packages/spec/api-surface-declarations/root.txtandshared.txt, all of them the producer fixpackages/spec/api-surface/,export-origins/,declaration-map/check:api-surfacegreenThe producer fix, on its own terms
cel()andexpression()(henceFandP) always write a non-blanksource, but were declared as returningExpression, whosesourceis optional — a declaration of a shape neither function can produce. The fix is at the PRODUCER (Prime Directive #12): the return type now states what the helper emits. The docblock was rewritten so it no longer rests on the triad requirement this rework removes; what it now says is the general fact plus the live consumer,FlowEdgeSchema.condition.Narrowing a return type removes nothing from a caller —
EvaluatedExpressionis assignable toExpression— so no call site changes.Clause-②re-judged, and the changeset level follows itThe previous body declared
Clause-②: yes (narrowing). With the accept-set narrowing gone that declared something this diff no longer does, andCheck Changesetread it and reddened. Re-judged against the six files that remain:Clause-②: no. The judgement is measured, not asserted:packages/spec/srcchanges are two return types and two docblocksgit diff d8b12fca97 -- packages/spec/src9f30a18a9check:api-surfacegreen, with no removed-or-narrowed report⇒ the changeset is
patch, the BREAKING banner is gone and so is the ADR-0087 disposition marker — both belonged to the narrowing that owned them. Something published still moves (two return types in the shipped.d.ts), soskip-changesetwould be wrong; a producer-side fix in a released package is exactly what apatchentry is for.Re-run locally against a
pull_requestpayload carrying this body, before pushing:GITHUB_EVENT_NAME=pull_request node scripts/check-changeset-no-major.mjs --base origin/main --event PAYLOAD_PATH→ exit 0,NOT DECLARED — the clause-② declaration reads 'no'. The same command against the OLD body reproduces the CI red, so the local run is a measurement and not a hopeful one.ADR-0136 is renumbered to ADR-0137
PR #18480 added
docs/adr/0136-declared-journeys-as-priority-anchor.mdabout 42 hours earlier.scripts/check-adr-anchors.mjsprescribes exactly this — the NEW record takes the next free number, and renumbering an already-accepted record was ruled out, so before it is referenced is the only cheap moment.0137re-verified free on this rework, not inherited from the earlier sweep:git ls-tree origin/main docs/adr/maindocs/adr/*files across all 31 open PRs (GET /pulls/{n}/files)0136: #18480 and this PR. Zero on 0137The scan lit twice on
0136, so the zero on0137is a reading and not a dead query.Three corrections the record owed
1. Its Status line claimed an implementation it no longer has. It now says what is true: this record declares and implements nothing. D1's authoring refusal is batch #122 item 2's, carried by #18638; D2 / D3 / D4 are consumer-delivered in objectui#8069.
2. 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 the gate slots "would bake a direction the ruling did not give" is true of batch #119 and was silent about batch #122 item 2, which gave exactly that direction six days earlier, and about #18638 which implements it. The claim is now stated as what it is — a statement about which ruling authorizes what, not a reason the conversion should wait — and the lint-side cost (
validate-visibility-predicates.ts'scelRefusalrecords the opposite position today) is named as a cost #18638 carries, not as an objection.3. The hand enumeration is replaced by a citation of #15811's census, because the hand list had already rotted. It omitted
packages/spec/src/system/settings-manifest.zod.ts:424and:686, bothvisible: SettingsVisibilityInputSchema, which isExpressionInputSchema.superRefine(...). Measured throughSettingsManifestSchema.safeParseon the builtdist:visible:686):424){ dialect: 'cel', ast: … }{ dialect: 'cel', source: ' ' }' ''data.provider.toUpperCase()'custom@visiblecustom@specifiers.0.visible"data.provider === 'smtp'"The refinement is live at both slots and narrows neither the
ast-only nor the blank-sourcearm —if (!source) return;is the line, and the lit control is what makes the six ACCEPTEDs a reading.The ADR-0058 D7 roster entry — re-derived, then removed
The ruling's KEEP list names it, and carries NO re-derive clause. The instruction to re-derive came from this seat's dispatch brief, not from the maintainer — recorded here because the earlier wording attributed it to the ruling. The re-derivation concluded the entry no longer belongs (measured: discovery finds 37 positions with the line present and 37 without, floor 37 unchanged and met at 37; the alias types zero slots), so the line is removed here pending the maintainer's explicit confirmation of that removal.
The roster lists schemas that declare an expression surface — "a slot whose accepted grammar is narrower gets its own schema and must be listed here too".
PredicateInputSchemaearned its place only while the rebinding made it= EvaluatedExpressionInputSchemaand bound the triad to it from another file. Reverted, it is a plain alias ofExpressionInputSchematyping no slot, and the ledger header's limitation 2 names it as the standing latent example — leaving it rostered would make that paragraph false. #18638 measured the same thing independently: 「PredicateInputSchemais a plain alias ofExpressionInputSchemawith zero slot users; it stays wide with the schema it aliases」.Measured twice on this branch with the revert already applied, by raising the
headfloor to 9999 throughablation-replace.mjsso the assertion prints the count:Identical, because the three triad positions are head-matched by
ExpressionInputSchemaagain. Both mutations landed and both restores verified on disk (anchor 1 -> 0, thenblob == HEADandgit diff HEADempty). Identity grep agrees:PredicateInputSchemahas 2 hits underpackages/spec/src/**/*.zod.ts— its own definition and itsz.inputcompanion, zero slots — against a lit control of 19 files forExpressionInputSchemaand a dark control of 0.⛔ Not a gate weakening. The
headfloor stays 37 and is met at 37; no ledger row is deleted, no floor is lowered, no test is skipped or quarantined. The same three surfaces are discovered through the schema that types them. Both dogfood files are byte-identical to merged main. The same statement holds for the two other removals: the triad pin test and the ADR-0087 entry are removed because the behaviour they recorded is no longer in this PR — not to turn anything green.packages/qa/dogfoodre-run after the removal: 7 passed (7).⛔ GOVERNED SURFACE — this PR parks as a draft, by design
docs/adr/**is on the register, so this is the regime's correct resting state, not a stall. An authorized approval is owed before any seat lands this. This seat has not flipped it ready, has not enqueued it and has not armed auto-merge. Theneeds:contract-reviewcarrier is the review seat's and stays hung; a fresh at-tier review is owed on this head.Evidence
pnpm --filter @objectstack/spec check:generated— proved exactly 2 of 16 artifacts stale (api-surface-declarations/,content/docs/references/**) and--fixregenerated only those two. Re-run after: 16 of 16 up to date.check:migration-registrygreen with the entry file deleted — the generated regions matchentries/.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackand reconciled with--ran; results and every non-zero exit are in the report comment on 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.origin/mainand 10 of the files the families are derived from moved in that range. Noorigin/mainmerge was taken in this rework, on purpose — this PR rebases after feat(spec)!: every engine-evaluated expression slot requires a non-blanksource#18638 lands, and a merge now would move themerge=os-regenartefacts for a base that is about to change. So the family list is this tree's, and CI on the merge queue's rebuilt generation is what covers the rest.Acceptance notes
packages/spec/src/data/field.zod.ts:1513expression(the formula slot) is an evaluated slot onExpressionInputSchema. Untouched, and no longer this PR's business at all: it is inside feat(spec)!: every engine-evaluated expression slot requires a non-blanksource#18638's 36-position census.PredicateSchema/PredicateInputSchemahave zero slot users — measured above. The ruling says they stay as they are and that a later card may retire them as dead symbols on their own measurement. No anchor is left claiming otherwise: the anchor this PR added forshared/expression.zod.tsis deleted, and thefield.zod.tsanchor is back to its pre-PR text.Consumersline still namesshared/expression.zod.tsanddata/field.zod.ts. That is the set of files the DECISION governs, which is unchanged; it is not a claim that this PR edits them for that reason.维护者速读(草稿)
改了什么 — 按 batch #160 item 1 的裁决 A,把这个 PR 里的协议收窄整段拿掉:字段三条规则槽位(
visibleWhen/readonlyWhen/requiredWhen)回到原样,Predicate*两个别名回到原样(仍然是宽的持久化契约),对应的 ADR-0087 迁移条目、pin 测试、两个 ADR anchor、以及只因它们才产生的 api-surface 与参考文档行,全部删除。留下的是:ADR-0137(改号后的 fault 语义记录)、ADR-0089 的指针附录、cel/expression的返回类型修复。另外按指示重新推导后,删掉了 ADR-0058 D7 的那一行 roster 条目。为什么改 — 同一个收窄早在六天前就被 batch #122 item 2 裁决过了,覆盖全部 36 个求值槽位(包含本卡的三条字段规则),并且由 PR #18638 用一个 ADR-0087 id 承载、先落地。两个 PR 各带一份收窄,就是一次迁移两个 id、两份 CHANGELOG 说法。#17778 裁决的是 fault 语义,不是承载它的符号,所以记录留下、收窄交出去,没有任何被裁决过的东西丢失。
风险与代价(含回滚) — 风险很低:协议行为零变化(没有任何 zod schema 移动),对外只剩两个函数返回类型收窄,而
EvaluatedExpression可赋值给Expression,所有调用点照常编译。changeset 因此从minor+ BREAKING 降为patch,Clause-②重判为no(三项读数在上表)。代价是本 PR 不再自带任何强制:D1 的编写期拒收要等 #18638;这一点在记录的 Status 和 Scope boundary 里明写了,不是留给读者去发现。回滚成本极低——本轮全部是删除与还原,恢复即 revert 这四个 commit。席位意见 — (留空)
你要做的 — 这个 PR 碰了
docs/adr/**,属受管面,停在 draft 等一个授权批准,这是制度的正常终态。需要你看的是三件事:① ADR-0137 现在只声明不实现——D1 交给 #18638、D2/D3/D4 交给 objectui#8069,这个归属你是否认可;② 记录里原来那句「gate 槽位转换会写进裁决没给的方向」已改写为「那只对 batch #119 成立;batch #122 item 2 给了这个方向,#18638 正在做」,这个更正你是否同意;③ D7 roster 那一行被删而不是保留——测量是:删与不删,head发现数都是 37(地板 37),且PredicateInputSchema零槽位使用,所以它不再是「更窄的别名」。裁决原文把它列在「保留」里,且未附任何重新推导的条款 —— 要求重新推导的是本席派发令,不是维护者(先前措辞把它归给了裁决,此处更正)。推导结论是该条目不再属于花名册(实测:该行在与不在,发现数都是 37,地板 37 未动且 met at 37;该别名零槽位),故此处删除,等您明确确认这一删除。Generated by Claude Code