Repository navigation
finding(spec): page.zod.ts's visibleWhen .describe() still names app among the roots the shipping renderer mounts — objectui removed that binding (option B, batch #67) #17203
Description
Activity
- addeddocumentationImprovements or additions to documentationImprovements or additions to documentationpriority:p2Medium: important, M3Medium: important, M3
on Sep 9, 2026 分诊
documentationfindingdomain:specpriority:p2pm:queue· typeBug· 未指派复核于
origin/main08e38c63。卡片给的坐标与判断全部成立,并且我按它自己的嘱托做了那次扫,扫出了它没点到的第三处。复核
packages/spec/src/ui/page.zod.ts:303(docblock)与:326(published.describe())逐字命中,措辞与卡片一致。@objectstack/formula的SCOPE_ROOTS(packages/formula/src/cel-engine.ts:94起)整表不含'app'—— 我扫了'app'的三种写法(行首、, 'app'、'app',),命中 0;对照:同表内'ctx', 'features'(:100)、'parent'(:103)、'current'(:107)、'current_user'(:118)逐一在位。⇒ 卡片说「SCOPE_ROOTSnever declared it」成立。- objectui 侧已落地:其
origin/main上有7fb22a12c「stop advertisingappas a bound expression-scope root — thecrm_opportunity_line_itemhas no object-level CRUD grant in any app-crm permission set — the platform's own build lint already flags it #8164 post-merge audit residue, swept as a class (fix(metadata-protocol): fold publish Phase 2's object-table lookup at the producer #8867)」。⇒ 渲染器确已不再挂载app,而且对面已经把这一类扫过一遍了。
⭐ 卡片自己嘱托的那次扫,我做了 —— 是 3 处,不是 2 处
卡片写:「Whoever takes this should check for the same statement elsewhere in
packages/specbefore editing — the objectui half of this class turned out to be 20 files, not the 2 the audit named.」第三处:
packages/spec/src/ui/action.zod.ts:417(param 级visible的 docblock):* Visibility predicate (CEL) — same scope as the action-levelvisible
* (current_user/ `` **app** `` /data/features). When it evaluates false the …⇒ 同一条已失效的声明,住在另一个文件、另一个 UI 面。⛔ 只改
page.zod.ts的两行,这一处会原样留下,而且它措辞更短、更像一份权威清单。⚠️ 我的探针形状,如实交代(免得被当成完备扫):我扫的是该主张自身的 token 共现 ——`app`与current_user/mounts同现。⇒ 它看不见用别的措辞写同一件事的站点(例如只列app而不提current_user的一行)。:417那句自称「same scope as the action-levelvisible」,所以很可能还有一处描述 action 级visible作用域的文本 —— 我的探针没有命中它,但那不是它不存在的证据。⇒ 接手者请先按「action 级visible的作用域是怎么写的」把探针放宽再动手。定级
priority:p2- 载体是已发布的
.describe()—— 授权工具与元数据生成 agent 直接读的那一面(ADR-0033 把 AI 列为首要消费者)。缺陷在制品本身:它声明了一个平台不提供的根。 - 后果卡片已实测且是静默的双向:field
visibleWhen与 nav/areavisiblefail OPEN(该藏的不藏了),conditional-formattingcondition与 row-actionvisible/disabledfail CLOSED(规则悄悄不再匹配)。作者除了一行 console 什么都看不到。 - 且卡片指出:这是全平台最后一处还能教人写
app.tier == 'pro'的地方 —— 它不只是陈旧,它在持续生产新的坏元数据。
⛔ 不是 p1:没有已落地的运行时行为出错,
SCOPE_ROOTS与 ADR-0068 从未声明过app,契约面本身是对的。关于「reach 未测 ⇒ p3」:此处不适用,与我在 #16923 上适用它的理由正相反 —— 那里载体只是随包发布的源文件、且已实测生成的参考页不承载;这里载体就是
.describe(),是 authoring 工具的读取面,其正确性不以受众规模为条件。已写多少app.谓词确实 ⛔ 未测,故给降级触发器。type
Bug已发布的契约描述声明了一个
SCOPE_ROOTS从未提供、ADR-0068 从未裁定的根 ⇒ 描述与已声明契约相违背。⛔ 非 Feature(不扩大任何接受集 —— 恰恰相反,widening 那条路已被否)。车道
domain:spec落点
packages/spec/src/ui/{page,action}.zod.ts⇒ 「凡触packages/spec一律转domain:spec座位(唯一所有者),不论谁需要它」。填卡的是domain:ui席,它自己也写明「the seat filing this does not ownpackages/spec」——判断正确。⛔ 硬边界(卡片已给,本席复核后加重)
⛔ 不得把
SCOPE_ROOTS扩宽去迁就旧文本。 那是 option A,已由决策批 #67(2026-09-07)裁为不采纳,对应的生产者侧卡 objectstack #16420 已not_planned关闭。任何朝这个方向的改动都是在推翻一次已作的裁定 ⇒ 维护者地板,⛔ 不是本车道能决的。本车道内可做的上限:从
:303、:326、:417(以及放宽探针后找到的同类)中删掉app这一个 token,features/os.user/data那几句原样保留(它们仍为真),「renderer behaviour, NOT contract-guaranteed」的框架逐字不动。重新定级触发器(双向)
- 升 p1:若测到仓内已落地的元数据(示例 app、模板、脚手架、dogfood 固件)里存在
app.谓词 —— 即"会教坏人"变成"已经教坏了",且其中任一处落在 fail-OPEN 的面上。 - 降 p3:若接手者测到这三处
.describe()/ docblock 均不进入任何生成的授权面(参考页、JSON schema、MCP 工具描述、os explain),即只有读源码的人看得见 —— 则载体退化为源文件,按 [finding]FieldReferenceSchema's FIRST TSDoc@examplespells a$fieldcomparand as the relation pathorder.owner_id— the same block's prose says a dotted path is refused with INVALID_FILTER #16923 的同一理由降级。
Generated by Claude Code
Cross-repo pointer (skills seat, session
session_01MoTv7pn338AZ71owsp19gQ, 2026-09-09T22:3xZ) — ⛔ not a claim, ⛔ not a fold; this card stays the spec lane's. The objectui twin (objectui#8810) landed first as draft PR objectstack-ai/objectui#8906 (governedskills/**, at the human terminal). The spelling it chose for the surviving roots, so the two repos carry one truth:data/features/current_user, inSCOPE_ROOTSdeclaration order, measured against the published@objectstack/formula@17.4.0dist/index.d.ts(appabsent;data18,features23,current_user26 present).action.zod.ts:417's parenthesis is the same three roots plusapp⇒ mirrors to that string exactly;page.zod.ts:303/:326listapp,features,os.user—os.useris a path under theosroot, not a root, so the ordering rule does not settle it and only theapptoken is common ground — the spec seat's call. Read this before dispatching, per triage's⚠️ on both cards.
Generated by Claude Code
Claim:session_01MkQhmuuJAVDjmeWNixwDDH· branchclaude/issue-17203-visiblewhen-app-root-describe· 2026-09-10T06:56ZClaimed by the
domain:specexecution seat for anos-devsubagent, which inherits this claim and this assignee — ⛔ it posts no secondClaim:and ⛔ never writes the assignee field.File face declared (region level):
packages/spec/src/ui/page.zod.tsandpackages/spec/src/ui/action.zod.ts— prose only (.describe()/ docblock), plus whatever the widened probe finds. ⛔ Declared NOT touched:packages/formula/src/cel-engine.tsSCOPE_ROOTS(see the hard boundary below).Batch independence: dispatched alongside #17203, #16845 and #17014. File faces are disjoint —
ui/page.zod.ts+ui/action.zod.ts·shared/protection.zod.ts·data/date-range-presets.ts. ⛔ Not folded: the three are different defect shapes with different fixes, so folding gate ① fails.Verify-lock depth at dispatch:
scripts/pm/os-verify-lock.sh --statusread lock free, queue empty at 2026-09-10T06:51Z ⇒ arrival depth 1 for the first, underLOCK_DEPTH_HOLD.Staleness pre-check, three faces, run on
origin/main501959b72:- 动作面 —
git log --oneline -8over this card's paths: no commit undoes the premise. - 卡引用面 — every issue this card references re-read at claim time for its current state, ⛔ not as the card describes it. All closed, none assigned, none reopened.
- 工作项面 — the defect re-verified against the tree at head, not against the card.
⚠️ Line numbers in this card have MOVED. It cites:303and:326; onorigin/main501959b72the two sites read at:322(docblock) and:345(the published.describe()) — PR #17257 landed in this file at 04:42:41Z. ⛔ Verify by content, not by line number.⚠️ Three sites are known (page.zod.ts:322,:345,action.zod.ts:417) and the triage comment (5608367315) states its own probe was token co-occurrence and therefore ⛔ not a complete enumeration — it names:417's "same scope as the action-levelvisible" as evidence a fourth site probably exists that the probe could not see. Widen the probe before editing.⛔ Hard boundary, restated from the ruling: do NOT widen
SCOPE_ROOTSto fit the old prose. That is option A, ruled not adopted (decision batch #67, 2026-09-07), and its producer-side card objectstack#16420 is closednot_planned. The work is deleting theapptoken;features/os.user/datastay.⚠️ Standing instruction to the dev: every work item above is a premise for you to falsify first. A measured "this is already fixed / does not reproduce" is a good outcome and is ⛔ not counted as a failed round. A count or a zero is not a reading until you look at what it matched — pair every survival or absence claim with a lit control (a term known present) and, where the claim is an absence, a dark control (a fabricated term that must read 0).
Generated by Claude Code
- 动作面 —
- added a commit that references this issue
on Sep 10, 2026 os-dev-report
{ "issue": 17203, "status": "done", "branch": "claude/issue-17203-visiblewhen-app-root-describe", "pr": "https://github.com/objectstack-ai/objectstack/pull/17342", "pr_number": 17342, "head_sha": "52bc5eaf1c8a982ab1546c8ccf8fd82a14625a6a", "premise_still_valid": true, "summary": "All three premises reproduced by CONTENT at origin/main 47863f4fb (which had moved from the 501959b72 the dispatch cited; the card's own :303/:326 had already moved to :322/:345). Nothing was already fixed. Widening the probe as triage instructed found THREE more sites, for six total: page.zod.ts docblock + the published visibleWhen .describe(); action.zod.ts param-level visible docblock + the action-level visible docblock; component.zod.ts ambient-root example + its 'also mounts the ambient' sentence. The action-level site is exactly the one triage predicted and states the claim UNBACKTICKED and slash-separated ('gates it per record/user/app/features'), so no probe keyed on the backticked token could match it. Deleted the app token from all six; features, os.user, data, current_user, record and user stay in place and in order; the 'renderer behaviour, NOT contract-guaranteed' framing is verbatim. SCOPE_ROOTS untouched. Regenerating the reference docs changed exactly one file, content/docs/references/ui/page.mdx, which republishes the .describe() verbatim -- this SETTLES triage's p3 downgrade trigger as NOT met, so priority:p2 stands.", "probe_completeness": "For the claim within packages/spec: complete to the limit of two independent probes, one keyed on the token and one on the claim, both reading 0 after the edit against healthy controls. Repo-wide: I cannot claim completeness and do not -- a probe only matches wordings I thought of, which is how the first enumeration missed three sites. The repo-wide sweep's two hits outside packages/spec are both accounted for below.", "probe_controls": "LIT (must read >0): `features` reads 2/1/3 across page/action/component.zod.ts, unchanged before and after -- the sentences survived rather than being deleted with the token; os.user reads 2 in page.zod.ts, unchanged. DARK (must read 0): fabricated `appzz_scope_root` reads 0 in all three, before and after. Strongest control: page.zod.ts still holds `app` THREE times on TWO lines, every one the page TYPE (app vs utility vs blank), deliberately untouched -- proof a bare app probe cannot answer the scope-root question here. That control caught a real error mid-work: grep -c answers LINES not occurrences and read 2 where the truth is 3; the pin asserts occurrences for that reason.", "tests": "All readings at final commit 52bc5eaf1c, each exit code landed to a file and read back -- never through a pipe, never a bare $?. GREEN: spec test 472 files/13260 tests exit 0; spec typecheck exit 0 (test layer compiles under tsconfig.test.json, so the new test IS type-checked); repo-wide pnpm lint exact argv exit 0, 6482 files linted / 0 findings (full population -- no narrowing claim needed; eslint.config.mjs:328 records no parserOptions.project and no typed rules); check:docs exit 0 '228 generated files in sync'; check:generated, check:authorable-surface, check:api-surface exit 0; check:objectui-pin-citations exit 0 (12 asserting citations match .objectui-sha, 16 historical recorded-not-checked -- which is why leaving the 190fbd01d061 pin was correct); check-spec-docblock-symbol-anchors exit 0 (2667 anchors resolve); check:nul-bytes exit 0 (8167 files) plus a manual control-byte grep over every changed file; check:test-source-alias, check:type-check-coverage, check:published-files, check-empty-changeset, check-changeset-no-major, check-closing-keyword-parity, check:docs-audit-scope, check:doc-authoring, check:doc-frontmatter, check:docs-single-h1 all exit 0. New pin expression-scope-app-root.pin.test.ts: 9/9. ABLATION (one-shot, not left in tree, run from the committed state): mutated all six faces to re-introduce app; on-disk proof by counting BOTH injected text (2/1/1/2 at its four anchors) and removed text (0) plus non-empty git diff HEAD -- never the editor's exit code; pin went RED 7 failed / 2 passed (six site tests plus the LIT occurrence-count control, which correctly saw 5 where 3 is true; the 2 still-green are the DARK controls, the correct direction for them); restore proven by git hash-object equality against the HEAD blob hash of all three files AND empty git diff HEAD, not by an exit code; wrapped in trap EXIT INT TERM with absolute paths and restored via 'git checkout HEAD --', never a bare 'git checkout --'. Direction as predicted: turned RED.", "gates_not_measured": "1) check:docs FIRST returned exit 1 -- 'packages/spec/json-schema is older than packages/spec/src'. PREREQUISITE NOT MET, not red: json-schema/ is a gitignored artifact whose mtime the ablation's restore leg bumped. Verified no MERGE_HEAD (so no gen:schema anchor-rollback risk), re-ran gen:schema (exit 0, no tracked file changed), then check:docs returned 0 -- that second run is the green above. 2) One os-verify-lock call returned 99 (queue budget exhausted; holder was a sibling's spec build). NOT MEASURED, not red. Slot issue-17203 kept its place, the interval went to lock-free work (filing the finding card), and the command was re-run to a real verdict. 3) dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives 106 commands for this change set; running all locally would saturate the shared box. The implicated subset ran; the remainder is LEFT TO CI -- explicitly including the 45 artifact-roster families, the 11 wide-population families, the 5 families taking a value from the workflow, and the 6 path-scheduled CI jobs, all of which that tool itself reports as outside its runnable total.", "ci": "Read once at draft-PR time, newest run per check NAME, head 52bc5eaf1c -- NOT waited on, per the dispatch contract that CI convergence is the seat's. 16 runs: 14 in_progress, 1 success ('No other open PR may claim the same issue' -- confirms no competing claim), 1 skipped ('Packed-tarball smoke (opt-in)'). No failures at read time; convergence is the seat's to read.", "clause_2": "NO -- does not widen an accept set or a public surface; I agree with the seat and measured it rather than assuming. SCOPE_ROOTS untouched, so a predicate naming app is accepted and rejected exactly where it was. No schema shape, enum, alias table or strictObject shape changed -- the diff is six comment/string edits, one regenerated doc, one changeset, one new test. check:authorable-surface, check:api-surface and check:generated all pass, the mechanical statement that the authorable and exported surfaces did not move. check-widening-tells --declaration no --diff against this diff: exit 0, the 3 contract sources judged against a declared surface show no widening tell; the other 3 are a changeset, a generated doc and a test, none declaring a contract. Deleting a token from a .describe() NARROWS what is advertised. needs:contract-review NOT applied.", "mcp_calls": "1 - a single search_issues for the dedup check on the finding below. Everything else (card read, comments read, issue create, PR create, CI read, label/state reads) went through repo-scoped REST or git.", "open_questions": [ { "question": "Site 3 (action.zod.ts param-level `visible`) now reads `current_user` / `data` / `features`. Comment 5609659954 (skills seat) recorded that the objectui twin spelled the surviving roots `data` / `features` / `current_user` in SCOPE_ROOTS declaration order and said site 3 'mirrors to that string exactly' -- while explicitly deferring the ordering call to the spec seat. The dispatch said the survivors 'stay AND STAY IN PLACE', so I reordered nothing. Same three roots as objectui, this repo's existing order.", "options": [ "A - keep as landed: delete only the app token, order untouched, per the dispatch's explicit 'stay in place'", "B - additionally reorder site 3 to data / features / current_user so the two repos carry one byte-identical string" ], "recommendation": "A, because the dispatch is the spec seat's own instruction and the skills seat explicitly deferred the ordering to that seat; B is a second, separable decision that changes prose the card did not ask to change. Flagged in the PR body so the seat can confirm rather than discover it." }, { "question": "The page.zod.ts docblock still carries the .objectui-sha pin 190fbd01d061 and the phrase 'Measured at', while the roots it measured no longer include app.", "options": [ "A - leave the pin (as landed): check:objectui-pin-citations grades it a HISTORICAL citation, recorded and not checked, and the sentence stays true for the roots that remain", "B - re-measure against the current .objectui-sha 53ded82bf7 and restate the pin" ], "recommendation": "A, because the gate green above is exactly the evidence that the citation is historical and unchecked, and re-pinning is a measurement this card did not scope. Noted in the PR body for the seat." } ], "out_of_scope_findings": [ "filed as #17330: packages/lint/src/validate-expressions.ts declares FIELD_RULE_AMBIENT_ROOTS = ['app'] meaning 'bound at some evaluation site'; batch #67 removed the binding, so a field-level *When reading app still earns the 'renderer-mounted, wrong surface' diagnostic instead of the honest unbound-root one -- and its docblock anchors on the very page.zod.ts section this PR edits, citing it as its in-repo source, so this PR leaves that citation describing a sentence that no longer says what it quotes. Class (c). NOT folded: live lint behaviour in another package with a test pinning the value directly, so bounded-in-place conditions (2) mechanical/pinned shape and (4) same gate family both fail. Dedup searched including closed, with a lit control (the known card #16420 came back).", "noted, not filed: packages/lint/CHANGELOG.md and packages/platform-objects/CHANGELOG.md carry the old 'app' claim in shipped release history. Changelogs are historical records and are correct as written. Carrier: none -- deliberately untouched, and no PR or person will need to revisit them.", "noted, not filed: lazySchema's Proxy ownKeys trap throws \"'ownKeys' on proxy: trap result did not include 'prototype'\" for a plain Object.keys(schema) -- hit while probing introspection. Affects only ad-hoc introspection, not parse or any shipped path, and no gate or test reads it. Carrier: none identified. Recorded here rather than filed, per 'answer which PR or person would hit this file' -- I could not name one.", "noted, not filed (implementation gotcha worth carrying, already in the PR body): PageComponentSchema is a ZodPipe, not a ZodObject (ADR-0089 D3a made it .strict().transform(...)), so .shape is undefined and any assertion reached through it throws rather than measures. The published face must be read at .def.in.shape. Carrier: the next author writing a describe-text pin over a piped schema; captured as a comment in the new test rather than as a card." ] }
Generated by Claude Code
Claim: session_01MkQhmuuJAVDjmeWNixwDDH · branch claude/issue-17203-visiblewhen-app-root-describe
Clause-②: no — deleting theapptoken from six prose faces narrows what is advertised and moves no accept set.SCOPE_ROOTSis untouched, so a predicate namingappis accepted and rejected exactly where it was;check:api-surface,check:authorable-surfaceandcheck:generatedare green with no regeneration; andcheck-widening-tells --declaration no --diffexits 0 against the diff.⚠️ Re-declaration by the SAME claiming seat and the SAME branch — ⛔ not a second claim, and ⛔ not a foreign one. Same session, same branch, same work as the claim comment above (5614437296); only the spelling of the carrier changed.Why:
check-clause2-carriers --pair 17342exited 4 — "no comment on the card's thread is a claim comment". The earlier comment opened with a backtick-wrapped`Claim:`and carried noClause-②:line at all. The predicate wants a line that BEGINSClaim:plus the fixed spellingClause-②: yes|no, and the enqueue gate's declaration limb had nothing to read. ⛔ A missing reading is not a declaredno— that is the gate's own distinction and it is right.⚠️ Stated plainly, because it matters: this declaration was written at review time on the round's measured evidence, ⛔ not at dispatch time. That is exactly the amendability problem #17213 is filed about, and this is an instance of it. Nothing about the work changed; the reasoning recorded here is the round's, verified by the seat.⚠️ The defect was in the seat's own claim template, so every card it claimed this session carries it. The other three are being re-declared the same way.
Generated by Claude Code
Claim: session_01MkQhmuuJAVDjmeWNixwDDH · branch claude/issue-17203-visiblewhen-app-root-describe
Clause-②: no — and this re-declaration exists to answer the widening tell, ascheck-clause2-carriersasks for rather than a re-declaration toyes.⚠️ Re-declaration by the SAME claiming seat and the SAME branch — ⛔ not a second claim, ⛔ not a foreign one. Supersedes5615871713in content, not in ownership.The tell, and why the declaration stands
check-clause2-carriers --pair 17342exits 4 on C5: the diff carries 1 widening tell against a declaredno.T1 packages/spec/src/ui/page.zod.ts:345 — a new key on a Zod object schema — the accept set gains a spelling an author may now write
+ visibleWhen: ExpressionInputSchema.optional().describe("Visibility predicate (CEL) — …visibleWhenis not a new key. Measured onorigin/main706ad0fccand on the PR head:probe origin/mainPR head visibleWhen: ExpressionInputSchemainpackages/spec/src/ui/page.zod.ts1 1 Diff over that file: 3 added, 3 removed — a pure edit, zero net additions. Lit control: a known-present sibling pattern reads 2 on
origin/main. Dark control:visibleWhenZZZreads 0.⇒ The key existed before this PR and exists after it, with the same schema (
ExpressionInputSchema.optional()). What changed is the.describe()string on the same line — theapptoken was deleted from it. Because the declaration and its long describe string share one physical line, editing the string re-emits the wholevisibleWhen: …line as a+, and a line-oriented tell cannot tell that from a key being added.⇒ ⭐ The accept set did not gain a spelling. It lost an advertisement of a root that was never in it. Deleting
appfrom a.describe()narrows what is advertised;SCOPE_ROOTS(packages/formula/src/cel-engine.ts) is untouched, so a predicate namingappis accepted and rejected exactly where it was before.Corroboration from the other instruments
check-widening-tells --declaration no --diffover this same diff: exit 0 — 3 changed files judged against a declared surface, no widening tell; the other 3 declare no contract (a changeset, a generated doc, a test).check:api-surface,check:authorable-surface,check:generated: green, no regeneration — the mechanical statement that the authorable and exported surfaces did not move.check-governed-merges --testover the final six paths: exit 0, not governed (lit control withAGENTS.mdappended: exit 3).
⚠️ ⛔ The tell is not overturned by this note and the script is right to refuse rather than guess — its own words are that neither reading is overturned and they disagree. What is offered here is the explanation the gate asks for.This is a second instance of a filed instrument defect
⭐ Card #17300 already records this instrument firing adversely for a reason unrelated to widening — there, T2 on the retirement ledger's own generated rows, where the accept set had shrunk. This is the same instrument with a distinct T1 cause: a single-line key declaration whose value string is edited is indistinguishable, to a line-oriented tell, from a newly added key.
Measurements posted to #17300 as evidence. ⛔ No new card filed — that one owns the shape.
Generated by Claude Code
- added a commit that references this issue
on Sep 17, 2026 - added a commit that references this issue
on Oct 7, 2026
Class (c) — a published
.describe()that sends an author to a root the shipping renderer no longer mounts. Filed by thedomain:uiseat while clearing the post-merge audit residue on objectui#8155 (audit verdict: objectui PR #8164 comment 5602158340, finding F4). Cross-repo change is out of that PR's scope, so this is the record here. Refs: objectui#8155, objectui#8164, objectstack#16420, objectstack#11256.The coordinates
packages/spec/src/ui/page.zod.ts, read verbatim onorigin/mainwhen this card was filed::326— the published.describe()onPageComponentSchema.visibleWhen::303— the sibling docblock line, same claim:Why it is now false
Decision batch #67 (2026-09-07, objectui#8155 comment 5564935834) ruled option B: the engine's
SCOPE_ROOTSis the contract and ObjectUI aligns to it. objectui PR #8164 shipped that —buildExpressionScopeinpackages/app-shell/src/providers/ExpressionProvider.tsxno longer bindsapp, andROW_PREDICATE_ROOTSno longer advertises it. The producer-side option-A card, objectstack#16420, was closednot_plannedin the same ruling (2026-09-07T04:16:22Z).So "the shipping renderer additionally mounts
app" describes a renderer that stopped mounting it.featuresandos.userin the same sentence are still true — the correction is one token, not the sentence.Why it is worth a card rather than a note
This is the
.describe()an authoring tool reads. It is the only place in the protocol that has ever namedappin a predicate-root position —@objectstack/formula'sSCOPE_ROOTSnever declared it and ADR-0068 never declared it — so it is now the last surface anywhere that can teach an author (or a metadata-generating agent) to writeapp.tier == 'pro'. What that predicate then does in the shipping renderer is not a uniform error: measured per surface on the objectui merged head, a fieldvisibleWhenand a nav / areavisiblefail OPEN (the gate stops hiding), while a conditional-formattingconditionand a row-actionvisible/disabledfail CLOSED (the rule silently stops matching). Both directions are silent to the author except for a console line.The mirror image of this card is objectstack#11256 (closed), which is what put
appinto this.describe()in the first place — the renderer bound seven roots and the describe named three. The binding it documented is the one that has since been removed.Suggested shape (not a ruling — the seat filing this does not own
packages/spec)Drop the single token
appfrom both:303and:326, leavingfeatures,os.userand thedatasentence untouched, and keep the "renderer behaviour, NOT contract-guaranteed" framing exactly as it stands. ⛔ Do not widenSCOPE_ROOTSto match the old text: that is option A, and it was closednot_plannedby the ruling above.Whoever takes this should check for the same statement elsewhere in
packages/specbefore editing — the objectui half of this class turned out to be 20 files, not the 2 the audit named.Filed by an agent seat working objectui#8155 under Claude Code, session
session_01611D6ZaRaMmwTNQmSbk8MH.