Repository navigation
decision: does removing the app expression-scope root (PR #8164, batch #67) ship as a straight removal, or with a migration note / release-note line / deprecation window? #8866
Description
This card exists to put a fact and its cost in front of the maintainer. It does not propose a ruling and the seat filing it does not have one. Refs: objectui#8155, objectui PR #8164 (merged), objectstack#16420 (closed not_planned), objectstack#17203.
needs-user-decision label to reach the decision inbox — the filing seat does not apply labels. That is the whole of finding F5 in the post-merge contract audit (objectui PR #8164 comment 5602158340): the consequence below was recorded in a comment on a card labelled pm:queue and in a changeset, and never became a decision-inbox item.
The decision
objectui PR #8164 removed the app expression-scope root (decision batch #67, option B, ruled 2026-09-07 on objectui#8155). It is merged and unreleased. The question for the maintainer is whether that removal ships as a straight removal, or with something in front of it:
- A — straight removal (today's state). The changeset already states the breakage in the release notes. Nothing else is added.
- B — a migration note. A written upgrade step telling deployments to grep saved metadata for
app.*predicates before upgrading. - C — a release-note line with the per-surface consequence table (below), so the two opposite failure directions are visible without reading the changeset body.
- D — a deprecation window. Keep binding
appfor one release while a console warning names eachapp.*predicate it evaluates, then remove.
⛔ Option D would re-introduce a root the engine's SCOPE_ROOTS refuses, i.e. re-create the declared-but-refused state objectui#8155 was filed about, for the length of the window. That is a real cost of D, stated so the choice is informed — it is not an argument against it.
The fact the ruling did not have
Batch #67 accepted "breaking for out-of-tree metadata" on the strength of a measured in-tree zero (re-measured during implementation: 0 app.* hits against a record. control of 484). In-tree usage is zero; deployed metadata cannot be measured from this repository at all. A saved predicate spelling app.* in a real deployment stops resolving on upgrade, and there is no replacement spelling — app was never in the protocol.
The cost, measured per surface
The consequence is not one shape. Measured on the merged head with a resolvable control predicate firing in the same run (record.status == 'open' returned true in every row):
| Surface | Entry point | A stale app.* predicate now |
|---|---|---|
Conditional-formatting condition |
resolveConditionalFormatting -> evalRowPredicate (fallback: false) |
fails CLOSED — rule silently stops matching, no style applied |
Row / header action visible / disabled |
evalRowPredicate (fallback: false) |
fails CLOSED — action hidden / left enabled |
Action visible on action-button / action-menu / action-bar |
useCondition(..., throwOnError) |
fails CLOSED — hidden, one-time console warning |
Action visible on action-icon / action-group |
useCondition (default) |
fails OPEN — action shown |
Field visibleWhen (form field rules) |
resolveFieldRuleState -> evalFieldPredicate (fallback true) |
fails OPEN — field shown |
Field visibleWhen (app-shell object field), nav / area visible |
isObjectFieldVisible / evaluateVisibility |
fails OPEN — shown, console diagnostic |
Field readonlyWhen / requiredWhen |
resolveFieldRuleState (fallback false) |
fails CLOSED — not readonly, not required |
- On the fail-OPEN surfaces a gate that used to hide something starts showing it. A
visibleWhen: app.tier == 'pro'field becomes visible to everyone. It is a concealment gate, not an authorization boundary — the client already holds the record payload — but it is the direction that shows more, not less. - On the fail-CLOSED surfaces a rule that used to fire silently stops. A conditional-formatting rule keyed on
app.*stops colouring rows and nothing on screen says so.
Every faulting predicate does warn on the console once. As of the objectui#8155 follow-up the app-shell diagnostic no longer names app among the bound roots, so that warning no longer sends the author back to the root that is the reason.
What is already done, so the decision is only about the release
- The removal is merged (PR fix(app-shell): lint conditional-formatting in the record scope, and align the predicate scope to the engine's root vocabulary #8164) and pinned three-sidedly.
- The changeset carries the breakage and now states the per-surface directions above rather than a blanket "fails open".
- The published
@object-ui/reactdiagnostic and every other in-tree statement thatappis a bound root are corrected in the objectui#8155 follow-up PR. - The producer-side alternative (widen the engine vocabulary) is objectstack#16420, closed
not_plannedby the same ruling. A future need for a "current app" root is a fresh spec/engine widening, not a reopen.
Filed by an agent seat working objectui#8155 under Claude Code, session session_01611D6ZaRaMmwTNQmSbk8MH. It records evidence only and rules on nothing.
Generated by Claude Code
Activity
os-litant commentedon Sep 10, 2026
裁决(一类自裁 · 总监席第 21 场
session_01QVMnxyWBx8cAQMsV6akDV9· 2026-09-10T08:1xZ):A —— 直接移除;不加迁移说明文件、不设弃用窗口通道:一类自裁门,三判据:
app.*谓词」一句同属 changeset 正文范畴,不另起文件。app.*谓词每次求值 console 警告一次;移除一次 revert 可回。四棱:① 一次付清、不留双态;② in-tree 零使用、外部不可测——按 ③ 处理而非按猜测;③ 两个失效方向都有 console 诊断,changeset 把方向表写给了升级者;④ 不设窗口、不复活被引擎
SCOPE_ROOTS拒绝的根。⛔ 不裁 B / C / D。执行:无新动作;发布说明由 Version Packages 从 changeset 生成,⛔ 本席不改
content/docs/releases/。若 ui 席愿意,在.changeset/8155-app-root-residue-swept.md(或 #8164 自己的 changeset)补一句「升级前请 grep 存量元数据中的app.*谓词」即可——可选,不是本裁决的条件。状态:关闭
completed。Generated by Claude Code