Skip to content

spec: hold a predicate to what the engine can run; declare its fault semantics (ADR-0136) - #18985

Draft
os-elon-musk wants to merge 16 commits into
mainfrom
claude/issue-17778-field-rule-predicate-fault-semantics
Draft

os-elon-musk wants to merge 16 commits into
mainfrom
claude/issue-17778-field-rule-predicate-fault-semantics

Conversation

@os-elon-musk

@os-elon-musk os-elon-musk commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Fixes #17778

Clause-②: no

The domain:spec half 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-column visibleWhen / 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 three FieldSchema triad-slot edits; the PredicateSchema / PredicateInputSchema rebinding, which go back to composing the persistence schemas, wide; the field-rule-predicate-evaluated-slot-source-required ADR-0087 entry (the entry file and, through gen: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.zod anchor were restored with git checkout d8b12fca97 -- (the merged-main parent of this branch's merge commit, a pinned sha, not a moving ref); git diff d8b12fca97 is empty for each. registry.ts was not hand-edited — the entry FILE was deleted and gen:migration-registry re-emitted the generated regions; the result is byte-identical to merged main and check:migration-registry is green.

After regenerating, the whole diff against merged main is 6 files, and the generated half of it is 4 lines:

artefact delta vs merged main
content/docs/references/** byte-identical — 0 rows
packages/spec/api-surface-declarations/ 4 lines, in root.txt and shared.txt, all of them the producer fix
packages/spec/api-surface/, export-origins/, declaration-map/ unchanged — check:api-surface green

The producer fix, on its own terms

cel() and expression() (hence F and P) always write a non-blank source, but were declared as returning Expression, whose source is 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.

-declare function cel(strings: TemplateStringsArray, ...values: unknown[]): Expression;
+declare function cel(strings: TemplateStringsArray, ...values: unknown[]): EvaluatedExpression;
-declare function expression(source: string, dialect?: ExpressionDialect, meta?: ExpressionMeta): Expression;
+declare function expression(source: string, dialect?: ExpressionDialect, meta?: ExpressionMeta): EvaluatedExpression;

Narrowing a return type removes nothing from a caller — EvaluatedExpression is assignable to Expression — so no call site changes.

Clause-② re-judged, and the changeset level follows it

The previous body declared Clause-②: yes (narrowing). With the accept-set narrowing gone that declared something this diff no longer does, and Check Changeset read it and reddened. Re-judged against the six files that remain: Clause-②: no. The judgement is measured, not asserted:

question the declaration asks reading instrument
does it widen an accept set? no — no zod schema moves at all; packages/spec/src changes are two return types and two docblocks git diff d8b12fca97 -- packages/spec/src
does it narrow an accept set? no — same reading; the narrowing left with commit 9f30a18a9 as above
does it expand the public surface? no — no export added, removed or renamed check:api-surface green, 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), so skip-changeset would be wrong; a producer-side fix in a released package is exactly what a patch entry is for.

Re-run locally against a pull_request payload carrying this body, before pushing: GITHUB_EVENT_NAME=pull_request node scripts/check-changeset-no-major.mjs --base origin/main --event PAYLOAD_PATHexit 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.md about 42 hours earlier. scripts/check-adr-anchors.mjs prescribes 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.

0137 re-verified free on this rework, not inherited from the earlier sweep:

query result
git ls-tree origin/main docs/adr/ tops out at 0135 — neither 0136 nor 0137 on main
added docs/adr/* files across all 31 open PRs (GET /pulls/{n}/files) two hits, both 0136: #18480 and this PR. Zero on 0137

The scan lit twice on 0136, so the zero on 0137 is 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's celRefusal records 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:424 and :686, both visible: SettingsVisibilityInputSchema, which is ExpressionInputSchema.superRefine(...). Measured through SettingsManifestSchema.safeParse on the built dist:

authored visible manifest slot (:686) specifier slot (:424)
{ dialect: 'cel', ast: … } ACCEPTED ACCEPTED
{ dialect: 'cel', source: ' ' } ACCEPTED ACCEPTED
bare ' ' ACCEPTED ACCEPTED
LIT CONTROL 'data.provider.toUpperCase()' REFUSED custom@visible REFUSED custom@specifiers.0.visible
DARK CONTROL "data.provider === 'smtp'" ACCEPTED

The refinement is live at both slots and narrows neither the ast-only nor the blank-source arm — 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". PredicateInputSchema earned its place only while the rebinding made it = EvaluatedExpressionInputSchema and bound the triad to it from another file. Reverted, it is a plain alias of ExpressionInputSchema typing 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: PredicateInputSchema is a plain alias of ExpressionInputSchema with zero slot users; it stays wide with the schema it aliases」.

Measured twice on this branch with the revert already applied, by raising the head floor to 9999 through ablation-replace.mjs so the assertion prints the count:

roster entry PRESENT  -> discovery found 37 position(s) via 'head'   (floor 37)
roster entry ABSENT   -> discovery found 37 position(s) via 'head'   (floor 37)

Identical, because the three triad positions are head-matched by ExpressionInputSchema again. Both mutations landed and both restores verified on disk (anchor 1 -> 0, then blob == HEAD and git diff HEAD empty). Identity grep agrees: PredicateInputSchema has 2 hits under packages/spec/src/**/*.zod.ts — 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, 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/dogfood re-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. The needs:contract-review carrier is the review seat's and stays hung; a fresh at-tier review is owed on this head.

Evidence

Acceptance notes

  • packages/spec/src/data/field.zod.ts:1513 expression (the formula slot) is an evaluated slot on ExpressionInputSchema. Untouched, and no longer this PR's business at all: it is inside feat(spec)!: every engine-evaluated expression slot requires a non-blank source #18638's 36-position census.
  • PredicateSchema / PredicateInputSchema have 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 for shared/expression.zod.ts is deleted, and the field.zod.ts anchor is back to its pre-PR text.
  • The ADR-0137 record's Consumers line still names shared/expression.zod.ts and data/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.

维护者速读(草稿)

席位意见一节留空,由席位在 at-tier 评审后定稿为评论。

改了什么 — 按 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

… 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>
@github-actions

github-actions Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/spec/src/shared/expression.zod.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/src/shared/expression.zod.ts) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json eeaa882459edffd077c1cda2972af06d1f10550bpackageMentionDocs.

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:data tests tooling labels Sep 18, 2026

os-elon-musk commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator Author

Type Check red on this head — diagnosed and handed to the implementer

Two checks red on head 4f9d0cf3c513b72d3393ea76c0429aa39c95bfb6, read 2026-09-18T0945Z, one root cause:

  • Type Check · consumer gates (job 105553471244)
  • Type Check · debt ledger (job 105553471301)

From the consumer-gates log:

examples/app-showcase typecheck: src/data/objects/invoice.object.ts(249,7): error TS2322
  Type '{ dialect: "template"|"cel"|"cron"; source?: string | undefined; … }'
  is not assignable to type
  'string | { dialect: "template"|"cel"|"cron"; source: string; … } | undefined'
      Types of property 'source' are incompatible.
        Type 'string | undefined' is not assignable to type 'string'.

Same error at lines 249, 279 and 285 of that one file.

⚠️ Correction by this comment's author, added after reading the second log. As first posted, this comment treated the two reds as one root cause and left the debt-ledger red as a question for the implementer. They are two distinct populations. Measured:

check-type-check-coverage --re-measure: 1 ledger entr(ies) drifted upward

  • @objectstack/spec-monorepo: DEBT records 26 raw tsc error(s),
    `tsc --noEmit` now reports 34 (+8).

That is the root package (@objectstack/spec-monorepo, its typecheck:root program), not examples/app-showcase. The three example errors belong to a separate workspace package with its own typecheck and do not account for the +8, so clearing the first red does not clear the second.

The remedy on the second red is bounded, and the gate says so itself: "DEBT is frozen debt, not a permission slip — the ledger is a ratchet and may only shrink. Fix the new errors — that is the author's remedy … ⛔ MAINTAINER-ONLY, NOT a co-equal option: … Raising the entry weakens a shrink-only ratchet … do not take this path to get CI green." So the eight new errors get fixed. Raising the DEBT entry, editing its numbers, or rewriting its note to accommodate them is not available to the implementer and not available to this seat either — weakening a gate threshold sits on this project's maintainer-only floor. If the eight prove genuinely irreducible inside this card's scope, the implementer stops and reports, and this seat escalates it as a decision rather than pressing the ratchet back.

Noted from the same run and ⛔ NOT this pull request's to fix: that ledger entry already carries a tier itemisation declared stale by compositionAt (tallied at 80, recorded 26), so its existing note does not describe what the pile is currently made of.

This is this pull request's to fix, and it is the narrowing working rather than a defect. The change makes source required in that slot; an in-repo example still writes it optional. Updating in-repo consumers that a landed narrowing breaks is part of landing it, so the three sites in examples/app-showcase are in scope — ⛔ not a reason to widen the pull request, and ⛔ not a flake (no re-run was spent on it).

One question was handed to the implementer with the diagnosis, and it is a question rather than a finding: PR #18952 landed earlier today publishing required-one-of(['source','ast']) on ExpressionSchema, so the contract there is source OR ast and an ast-only expression is legal. If this change requires source unconditionally, the ast arm would become unreachable — which the ruling did not ask for (its blank-predicate point is about a blank predicate, not about dropping ast). The expected type in the error is a union with string, which suggests a field-rule slot rather than ExpressionSchema itself, in which case there is no conflict at all. The implementer will measure which it is and state it; if the ast arm really did become unreachable, that exceeds the ruling and is the seat's to escalate rather than the implementer's to quietly keep or revert.

⚠️ Reminder on this pull request's resting state, unchanged by the above: it touches docs/adr/**, which the governed-surface predicate reports as governed, so it parks as a draft awaiting an authorized human approval. This seat cannot approve it, flip it ready, or enqueue it. A green head does not change that.


Generated by Claude Code

`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>

os-elon-musk commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator Author

Dogfood Regression Gate red on the current head — the three slots this card is about left the conformance ledger

Live on head d3c10713d0883fbb997d7596f1fa20911d1cd826 (read 2026-09-18T1001Z), so this is a new failure and not the superseded batch. The earlier typecheck reds are fixed — 21 success on this head.

Dogfood Regression Gate (3/3), job 105556637504: 2 failed / 436 passed, both in packages/qa/dogfood/test/expression-conformance.test.ts, the ADR-0058 D7 expression surface conformance ledger.

STALE covers — surface no longer in source: data/field.zod.ts:FieldSchema.requiredWhen
STALE covers — surface no longer in source: data/field.zod.ts:FieldSchema.readonlyWhen
STALE covers — surface no longer in source: data/field.zod.ts:FieldSchema.visibleWhen

discovery found 34 position(s) via 'head' (floor 37) — a roster name immediately after
`field:` (the pre-#17630 scan). Discovery has lost a mechanism it is required to see.

37 − 34 = 3, and the three named surfaces are exactly the three field-rule predicate slots. The two assertions are one fact seen twice.

Why this is worth stopping on rather than clearing

visibleWhen / readonlyWhen / requiredWhen are the three slots this card exists to govern. The D7 ledger is the instrument that records which expression surfaces exist and whether anything evaluates them — the same class of instrument that made the unevaluated-predicate gaps visible in the first place. A change whose purpose is to make these predicates' fault semantics contractual should not end with them invisible to the contract ledger.

⛔ The remedy is bounded, and the gate says so itself

If a position was legitimately retired, lower the floor in SCAN_CONTROLS in the same commit that deletes its ledger row, and say which position went away; ⛔ do not lower it to make a re-narrowed scan pass.

So: ⛔ the SCAN_CONTROLS floor is not to be lowered, and ⛔ those ledger rows are not to be deleted to silence the STALE complaint. Lowering a discovery floor is gate-weakening, which sits on this project's maintainer-only floor independently of that message — not available to the implementer, and not available to this seat either. This is the same standing as the DEBT ratchet noted in the previous comment.

✅ RESOLVED, and the seat's hypothesis below was FALSIFIED — correction by this comment's author

Dogfood Regression Gate (3/3) is success on head e1978a0f3b02608d6137ac4daab928540bb52b6b (read 2026-09-18T1026Z; 28 success / 2 skipped / 0 failures). The implementer measured the cause and it is not what this comment hypothesised.

What actually happened. The three slots were NOT restructured out of the shape D7 scans for, and no expression surface was removed from the conformance ledger. They are still exactly name: SchemaIdentifier.optional(). Only the schema name changed — the slots now bind PredicateInputSchema — and EXPRESSION_INPUT_SCHEMAS is a fixed roster of names that did not contain that one. Alias resolution there is file-local, so the rename alone took the three positions out of discovery.

The ledger had already written this trap down. Its header carried it as limitation 2, naming this very alias as "latent rather than live … it types no slot anywhere". This card made the alias live from another file, which is precisely the condition that limitation described. The roster's own docblock prescribes the repair: "A new narrowed alias belongs in this list on the same commit that introduces it", with #7327 and #15807 as two prior instances.

The fix was therefore to ADD the roster entry — plus a correction to limitation 2, which this change makes false. ⛔ No floor was lowered. ⛔ No ledger row was deleted. The implementer reproduced CI byte-for-byte by removing that one roster entry again (same 3 STALE covers, same discovery found 34 position(s) via 'head' (floor 37), 2 failed / 5 passed), then restored it clean — so the entry is demonstrably the fix rather than a silencer.

⇒ The escalation this comment flagged as possible is withdrawn: nothing exceeded the ruling, and there is no maintainer decision owed on whether those three slots may leave the D7 ledger. They never left it in the sense that mattered; discovery simply could not see their new name.

The hypothesis is left below rather than deleted, because it was wrong in a way worth keeping: it was stated as a lead with an explicit request to falsify it, and falsifying it is what produced the actual fix.

What was handed to the implementer, and it is a lead rather than a finding

The hypothesis given: restructuring field.zod.ts moved those three declarations out of the shape D7 scans for, as an unintended side effect rather than a decision — in which case the fix is to restore a form discovery can see, ⛔ not to touch the scan or the floor.

The implementer was asked to falsify that if it is wrong, and told that if the three slots were deliberately restructured out of that surface, the correct action is to stop rather than proceed: removing three published expression surfaces from the conformance ledger is a contract-visible change the governing ruling did not ask for, which makes it an escalation to the maintainer rather than something to land or to wave through.

⚠️ This pull request's resting state is unchanged by any of the above: it touches docs/adr/**, so it parks as a draft awaiting an authorized human approval that this seat cannot give. A green head does not change that.


Generated by Claude Code

…/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>

Copy link
Copy Markdown
Collaborator Author

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 finding only — no domain:*, no priority, those are triage's to set.

The sentence in the ADR does not cite a number, so the reference is still dangling in the text. Adding #19000 to it is a one-line edit I am deliberately not requesting right now: the review is mid-flight against e1978a0f3b02608d6137ac4daab928540bb52b6b, and re-heading the PR under a running review buys a dangling-reference fix at the cost of the review's subject. I will decide it at the landing step.

2. Two numbers in the Scope boundary are off, both in the direction that strengthens its own argument.

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 this PR's head, taken 2026-09-18T10:36Z. Every window had a lit control; zero dark instruments.

  • The record says "several of those slots carry their own declared fault directions". Measured: exactly 4 of 21, and they disagree 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 (disabledWhen). The other 17 declare no direction at all. A 3-to-1 split with 17 abstentions is a harder case against converting them in one sweep than "several" conveys, so this corroborates the deferral rather than undercutting it.
  • The Scope boundary's enumeration names 19 slots. The same instrument finds 21. The two unnamed are packages/spec/src/system/settings-manifest.zod.ts:424 and :686, both visible: SettingsVisibilityInputSchema, which is ExpressionInputSchema.superRefine(...): the refinement adds a check but does not narrow the ast-only or blank-source arm, so their exposure is identical to the named 19. Control for that zero: the record mentions settings-manifest or SettingsVisibility 0 times against 10 hits for visibleWhen in the same file.

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 EXPRESSION_INPUT_SCHEMAS, and the ast-arm type-level measurement answering whether #18952's required-one-of(['source','ast']) survived. A reader taking the body as current will be reading three developments behind the head. Recording it here rather than editing the body, for the same reason as item 1.

4. Landing posture, restated so it is on the record and not only in a seat post.

This PR touches docs/adr/**, which is a governed surface. Whatever the review returns, the landing path stops short of the queue: on PASS I strip both needs:contract-review carriers, post the ## Contract review record in the template shape, confirm check-clause2-carriers.mjs --pair 18985 exits 0, and stop there. I do not flip it ready, do not attach auto-merge, and do not enqueue. It parks in draft awaiting a human approval that this seat has no authority to give. Landing does not happen on this shift.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

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 ## Contract review record is being written on this head, and neither needs:contract-review carrier is being stripped. Reasons below.

Review admissibility. Isolated at-tier review, dispatched because this seat serves below CONTRACT_REVIEW_TIER (scripts/pm/dispatch-gates.mjs:11899). Tier verified the only way it can be — by census of the reviewer's own transcript, since a subagent's get_session reads the parent and cannot self-attest: 88 of 88 assistant turns at the tier constant's value, zero at any other model. Bound to head e1978a0f3b02608d6137ac4daab928540bb52b6b, stated in full by the reviewer for every reading.

Verdict: CLAUSE-② REVIEW: PASS. Accept-set narrowing on the FieldSchema triad and the public PredicateSchema / PredicateInputSchema / Predicate / PredicateInput; persistence contract measured un-narrowed; minor + BREAKING + ADR-0087 D3 entry present and consistent; major correctly not used. All ten review items PASS. GATE-WEAKENING CHECK: none takenSCAN_CONTROLS floors unchanged at 37/3/2, D7 ledger 26 rows to 26 rows with non-comment bytes identical, scripts/check-type-check-coverage.mjs byte-identical with spec-monorepo errors: 26 intact, zero deleted files, zero added .skip/.only/.todo/xit/xdescribe lines against a lit control of 9 added it(' lines, zero removed lines in any test file.

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: docs/adr/0136-declared-journeys-as-priority-anchor.md is added by open PR #18480, created 2026-09-16T15:07:00Z, about 42 hours before this PR. I swept all 32 open PRs for added docs/adr/NNNN- files — exactly two, both claiming 0136, zero parse errors — and 0137 is free on origin/main at f347c793e16322a4befc77651d1ab8760bf36874 (highest record there is 0135) and unclaimed by any open PR. The fix is the one scripts/check-adr-anchors.mjs:545 prescribes: the new record takes the next free number. Renumbering an already-accepted record was ruled out in #5992, and neither of these two is accepted yet, so that ruling does not arbitrate between them — taking 0137 is simply the option that needs no other lane's cooperation.

Why no record and no carrier strip on this head. The gate reads the PR's live head (check-clause2-carriers.mjs:5407), and a head that moves after the carriers are cleared earns a re-hang row (:7082, :7249). The head must move — for the reconciliation edit, the renumber and the migration-entry wording fix. A record written against e1978a0f would be invalidated by the very next push. So the carriers stay on, which is the honest state: 「开着的载体恒 = 真实待审」. The reviewer has confirmed it is ready to re-affirm against the renumbered head with the rename diff only.

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 evaluated-expression-slots-source-required, and which of two seats' PRs lands first on twelve shared files, are cross-seat and architectural questions. #18638 belongs to another seat in this lane; I hold #17778 and this PR. I am not answering them for the maintainer and I am not touching #15811 or #18638 beyond posting evidence on #15811 (comment 5728924245, evidence only, no label, assignee or state change).

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 surface wording "the public exports every other declaration site composes" is false — zero declaration sites composed them, measured; tmpl() / cron() still declare Expression while always setting source, which #18638 already narrows; the PR body now predates four developments, the #18638 intersection included.

Seat: domain:spec#3. Posted 2026-09-18T10:50Z.


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>
@github-actions github-actions Bot added size/m and removed size/xl labels Sep 18, 2026
…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>

Copy link
Copy Markdown
Collaborator Author

PM note — Check Changeset is red on this PR and the cause is this seat's to clear, not the author's. Recorded once so the red is not read as unfinished work.

Live head 1bbe8f56243a8b2644d364198b6fdae9f5647cd2, read 2026-09-18T13:03Z: 32 check names — 11 success, 3 skipped, 17 still in progress, 1 failure. The one failure is Check Changeset (run 105608045077), and its finding is verbatim:

The two declarations disagree, inside one PR:
· carrier: needs:contract-review IS on this PR
· declaration line: Clause-②: no
· direction arm: none declared — the declaration names no widening and no narrowing

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 needs:contract-review carrier is the review seat's to place and to clear, and the Clause-②: line is the claim's」.

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 PredicateSchema / PredicateInputSchema rebinding and this PR's own ADR-0087 entry were removed, because PR #18638 (card #15811, batch #122 item 2) is the sole owner of that accept-set narrowing. The diff went from 22 files to 6. The declaration correctly followed to Clause-②: no and the changeset to patch.

That means the head that was reviewed no longer exists. The clause-② PASS on record (comment 5729578584) judged e1978a0f3b02608d6137ac4daab928540bb52b6b, and a carrier may only be cleared against a verdict on the head that is actually there. A fresh at-tier review of the final head is owed and is this seat's next act; the carrier comes off with that verdict, PASS or FAIL alike, and this red clears with it.

What will NOT be done to clear it. Raising @objectstack/spec back to minor would satisfy the gate by asserting a published-surface change this PR no longer makes — the gate's route 1, which does not apply here and which would make the gate agree with a false statement. No tolerance, no allowlist entry, no skip. The gate's own text forbids routing around a declaration, and gate-weakening is a maintainer-only floor in any case.

⚠️ Also for the record, because the PR's check history is misleading right now: the earlier failures on head 89dfabbb8b37ed9382f257e3fababc8f488cc036 — including a TypeScript Type Check and a Test Core failure — are supersession artefacts, not defects. That head was replaced mid-run; seven of its checks read cancelled, and the TypeScript Type Check aggregator failed solely because its member lane typecheck-workspace concluded cancelled rather than success. Read the latest run per check name bound to the CURRENT head, never an event's sha.

No re-run has been spent, and none is warranted: this is not a flake and not another PR's failure.

Seat: domain:spec#3.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: 209/209 CONTRACT_REVIEW_TIER
Head-sha: 1bbe8f56243a8b2644d364198b6fdae9f5647cd2

① Derived judgments

Isolated at-tier re-review. The earlier PASS on this PR judged e1978a0f3b02608d6137ac4daab928540bb52b6b at 22 files and is void for this head — decision batch #160 item 1 (letter A) reduced the diff to 6 files, so that verdict was not inherited and the reviewer confirms it did not consult it. Tier verified by census of the reviewer's own transcript at 209 of 209 assistant turns; a subagent cannot self-attest, its get_session reads the parent.

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 d8b12fca97, and the restricted git diff parent..head over them is empty. The inverse was checked too, because the ruling forbids publishing the aliases narrowed: PredicateSchema = ExpressionSchema and PredicateInputSchema = ExpressionInputSchema read identical at head and at origin/main, and their rows in the two api-surface shards are main's wide union verbatim. The KEEP list survives: ADR-0137, the ADR-0089 addendum, and the producer fix present in source and in the built .d.ts.

The renumber holds on an independently widened instrument: origin/main tops at 0135, the head carries 0137 with zero 0136 hits anywhere in the tree, and a collision scan across all 34 open PRs (three more than the dev's 31, opened after 13:00Z) finds exactly two added ADR records — #18480 at 0136 and this PR at 0137. The lit pair fired.

② Semver level

Clause-②: no with @objectstack/spec patch, no BREAKING banner, no ADR-0087 marker — confirmed as the right set rather than the permitted one. Zero zod bytes move. The published movement is real and was not waved away: two return types reach the shipped .d.ts, proven by a lit control (swapping the parent shard back makes the declarations gate report ~ cel, ~ expression, "0 removed, 0 added, 2 reshaped", exit 1). A tsc probe in both directions shows a caller loses nothing while an implementer of typeof cel would now break — and no such implementer exists in this repo. ⚠️ Recorded because it matters to a reader: check:api-surface is names-only and blind to return types, so its green is not evidence of no .d.ts change; the declarations gate is the instrument that sees this.

③ Boundary flags

Gate-weakening: none. SCAN_CONTROLS reads 37 / 3 / 2 unchanged, the dogfood test and ledger blobs are byte-identical to the parent, and no floor, ratchet, baseline or ledger file appears in the 6-file diff. This PR deletes three things and each was adjudicated separately rather than in aggregate: the pin test (pinned refusals of a narrowing no longer shipped), the ADR-0087 entry (recorded a migration no longer performed; registry.ts re-emitted and blob-identical to parent), and the ADR-0058 D7 roster line (inert at this head — discovery finds 37 positions with it present and 37 without, against a lit control of 8 when ExpressionInputSchema is removed from the roster, so the scan is provably live and 37 is met at 37, not lowered to fit). All three record behaviour this PR no longer has. No test skipped, disabled or quarantined.

Implemented-by: claude/issue-17778-field-rule-predicate-fault-semantics
Reviewed-by: session_019srGWGCBBCBHqcDoRZpQRh

VERDICT: PASS

⚠️ PASS is not clearance to land, and one blocking item is owed to the maintainer. The reviewer's B1: the ruling's KEEP list names the D7 roster entry and carries no re-derive clause — the re-derive instruction came from this seat's dispatch brief. In substance the removal is a consequence of ruling A (the entry's only premise was on the ruling's own REMOVE list), but in procedure it is a departure, and this PR's body had attributed the instruction to the maintainer. That wording is corrected as of this act, in both languages, and the removal now goes to the maintainer for an explicit one-line confirmation with the alternative stated. ⛔ This seat does not reverse a ruling on its own reading.

Landing is additionally gated twice over: docs/adr/** is a governed surface and this seat has no authority to approve it, and ruling A sequences this PR to rebase after PR #18638 lands.

Non-blocking, carried: ADR-0137's Consumers header is acceptable but is the one sentence a reader can take for a claim about this diff rather than the decision's scope; after #19024's revert lands the two shard hunks drop on rebase and this PR's "4 lines in api-surface-declarations/" sentence needs rewording, since the patch evidence becomes a build plus a .d.ts grep; the dev's "every error TS2307" over-claims (measured 257 TS2307 plus downstream codes, conclusion unchanged); and whether objectui or cloud type anything against typeof cel — the only shape this narrowing can break, and the only path letter C's consumer-compile gate would see it through — is not measured at the pinned sha.


Generated by Claude Code

…eld-rule-predicate-fault-semantics

# Conflicts:
#	packages/spec/src/shared/expression.zod.ts
…/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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants