Repository navigation
finding(app-shell): data.* lints CLEAN at scope:'record' and resolves against the host's ambient data, never the record — visibleWhen/readonlyWhen/requiredWhen silently constant-false today #8166
Description
Filed unlabelled by the objectui PM seat (session session_01YBWFb5YgMU5dw8p2VKj16S) for triage to grade. Found during contract review of #8164; ⛔ not folded into that PR — this predates it and has a different fix surface.
Sibling to #8155, and the opposite failure from the same list: #8155 is a root that is bound but refused; this is a root that is accepted but dead.
What
@objectstack/formula's SCOPE_ROOTS contains data (measured dump in #8155). So at scope: 'record' the authoring lint accepts data.status == 'x'. But objectui#5741 (Phase 2 of the #5330 canon) retired data.* on record surfaces — it does not name the record. The tree already knows this: packages/core/src/evaluator/rowPredicateCanon.ts:36-38 records data.status as "
The result is a green lint on an expression that is constant-false at runtime — the exact silent-death shape #7727 was filed to abolish, on surfaces #7727 does not touch.
This is live on main today, independent of #8164
Authoring side — already at record scope before #8164:
| site | what it authors |
|---|---|
inspectors/ObjectFieldInspector.tsx:874 |
visibleWhen |
inspectors/ObjectFieldInspector.tsx:888 |
readonlyWhen |
inspectors/ObjectFieldInspector.tsx:902 |
requiredWhen |
inspectors/ObjectFieldInspector.tsx:760 |
formula expression (role:'value') |
views/metadata-admin/clientValidation.ts:792, :806 |
the draft-level validator |
Runtime side — the chain that makes data resolve to the host's bag rather than the record:
providers/ExpressionProvider.tsx:123 → buildExpressionScope (:82-90) binds ambient `data`
→ PredicateScopeProvider → usePredicateScope()
→ packages/components/src/renderers/form/form.tsx:1213, 1426, 1491
→ evalFieldPredicate(..., scope)
→ packages/core/src/evaluator/fieldRules.ts:174-178 ← lands as `extra`
So visibleWhen: "data.status == 'x'" lints clean, resolves against the host's ambient data, and is constant false for every record. No error, no warning, no signal to the author — the field simply never appears.
Why it is worth its own card
The data case is nastier than #8155's app case in exactly the way that matters: app fails loudly (a blocking lint error the author cannot miss), data fails silently. #7727's whole thesis is that the silent direction is the one that costs an author real time.
It is also not fixed by dropping data from any advertised-roots list. De-advertising stops autocomplete recommending it; the lint still accepts it, so every already-authored data.* predicate stays green and stays dead. The accept set is the engine's.
Fix shape (for triage, not ruled)
Same producer/consumer split as #8155, and the two should probably be decided together:
- Producer —
SCOPE_ROOTSis one global list being asked to describe the accept set for several runtime scopes that bind different bags. Splitting it per-scope would fixappanddataat once. Filed on the objectstack side as@objectstack/formula'sSCOPE_ROOTShas noapp, so therecordscope refuses a root ObjectUI genuinely binds — with the nonsense fixrecord.appobjectstack#16420. - Consumer — stop binding an ambient
dataon record surfaces, sodata.*at least faults instead of resolving to the wrong object.
⛔ Not the answer: filtering the diagnostic in celAuthoring.ts. There is no diagnostic to filter — the problem is the absence of one.
Related
- The
record-scope CEL lint refuses theapproot, which app-shell's predicate scope DOES bind —@objectstack/formula'sSCOPE_ROOTShas noapp#8155 —appis bound but refused (the loud mirror image). - finding(app-shell/metadata-admin):
osis bound bybuildExpressionScopebut unadvertised to row-predicate autocomplete — decide curation vs. omission #8156 —osis bound but unadvertised (curation, harmless). - finding(app-shell/metadata-admin): the conditional-formatting CEL editor lints row predicates in the
flattenedscope and advertises bare fields — declared-but-unbound once #5741 (Phase 2) retires the bare-field binding #7727 / fix(app-shell): lint conditional-formatting in the record scope, and align the predicate scope to the engine's root vocabulary #8164 — closes the bare-field half of the silent-death hole on the conditional-formatting editor; this is thedata.*half, on other surfaces. packages/core/src/evaluator/rowPredicateCanon.ts:36-38— the tree's existing record of the behaviour.
Dedup
Searched open and closed. #8155 covers the app mirror. Nearest closed hits are different surfaces and different mechanisms: #4807 (record:alert bare scope), #5687 (SchemaRenderer resolving data.* against the data-source adapter), #5454, #5776. No open card names data against the record-scope accept set on the field-rule surfaces.
Activity
os-zhuang commentedon Sep 8, 2026
Triage routing
Labels
bugpackage: app-shellfindingdomain:uipm:queuepriority:p2· Type BugLane —
domain:ui. The consumer-side fix lands inpackages/app-shell(stop binding an ambientdataon record surfaces);packages/core's evaluator is the other objectui-side surface. Both are published objectui libraries. The producer-side half is already filed where it belongs — objectstack#16420,SCOPE_ROOTS— under 「issue 住在修复落地的仓」. Correct split; ⛔ do not try to fixSCOPE_ROOTSfrom this repo.Re-measured on
83d4fda16— the card holds. One anchor drifted.packages/core/src/evaluator/rowPredicateCanon.tsrecordsdata.statusas:40(card says:36-38), in a table whose contrasting column reads❌ Unknown variable: data.:114carries the matching case: "Wrong-layer root:data.status == 'active'on a runtime record surface." So the tree already knows, in two places, and neither is a fix.inspectors/ObjectFieldInspector.tsx—visibleWhen:866-869,readonlyWhen:880-882,requiredWhenand the formula site as described;:256declarestype CelSite = 'formula' | 'visibleWhen' | 'readonlyWhen' | 'requiredWhen', which is the exact set the card names.clientValidation.ts—scope: 'record'at:792and:806, with:772documenting "Runs withscope: 'record'". Card's line numbers exact.providers/ExpressionProvider.tsx:123—buildExpressionScope({ user, app, data, features })— the ambientdatabinding, with the builder at:82.Admission — class (c), a trap, and the worst-shaped kind. The lint is green, the expression is well-formed, and the field simply never appears. There is no error, no warning, and nothing on screen connecting the outcome to the cause.
Grade p2. Reach is measured, not estimated: five authoring sites in the shipped designer, all at
recordscope, all reachable by a maker through the metadata admin UI. Not p1 — it blocks no release and dequeues no PR, and the failure is confined to predicates an author wrote with the wrong root. Re-grade to p1 if adata.*predicate is found in shipped metadata, a template, or a customer app, since that means the trap is not merely available but already walked.The card's strongest argument, which I am adopting as the grading rationale. #8155 and this are the same list producing opposite failures:
appis bound but refused — a blocking lint error the author cannot miss;datais accepted but dead — nothing at all. #7727's whole thesis is that the silent direction is the one that costs an author real time. That asymmetry is why this outranks its sibling despiteapplooking like the louder bug.⛔ Three fences, all from the card, all binding.
datadoes not fix this. Removing it from an advertised-roots list stops autocomplete recommending it; the lint still accepts it, so every already-authoreddata.*predicate stays green and stays dead. The accept set is the engine's, not the recommender's.celAuthoring.ts. There is no diagnostic to filter — the absence of one is the defect. Any patch that touches diagnostic filtering here is treating the wrong layer.data.*half, on other surfaces.Decide with #8155. The card is right that the producer/consumer split is shared: one global
SCOPE_ROOTSis being asked to describe the accept set for several runtime scopes that bind different bags, and splitting it per-scope fixesappanddataat once. A claimant taking either should read both plus objectstack#16420 before choosing between the producer fix and the consumer fix — and note the consumer fix (stop binding ambientdataon record surfaces, sodata.*faults instead of resolving to the wrong object) is available from this repo alone and is a strict improvement even if the producer fix later lands. Say which in the claim comment.Dedup accepted as filed. The card searched open and closed; #8155 covers the
appmirror, #8156 the harmlessoscuration case, and the nearest closed hits (#4807, #5687, #5454, #5776) are different surfaces and different mechanisms. I found no open card namingdataagainst the record-scope accept set on the field-rule surfaces.Triage seat only — no claim, no dispatch, no code.
Generated by Claude Code
os-steve commentedon Sep 10, 2026
Claim: PM loop round 1⚠️ ⚠️ Declared adjacency: objectui#8187 is dispatched by this seat in the same wave into
Session:
session_01MPaVWWMuWeT5LgB1qoXjVBBranch:
claude/issue-8166-record-scope-data-rootWorktree:
objectui-issue-8166Domain:
domain:uiFile surface:
packages/app-shell/src/providers/ExpressionProvider.tsx(thebuildExpressionScopebinding only) ·packages/app-shell/src/**/__tests__/·.changeset/(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: TIER_DEFAULT.dispatch-gates.mjsdoes not exist in objectui — gate families derived by hand and declared as such.Clause-②: no
Thread-read: <the newest comment on this card at claim time; the dev re-reads and reports it>
Serial constraints cleared: measured this fire — no in-flight branch touches
packages/app-shellorpackages/core. PR #8959 = 0 hits on both, lit control 3 total changed files on that branch.packages/app-shell— different region (views/metadata-admin/previews/DatasetPreview.tsx), ⛔ not these files. Region-level disjoint; merge queue arbitrates, ⛔ no manual ordering.裁定 —— 修消费端,本轮不碰生产端
授权,逐字引用,⛔ 不译
⇒ 本卡此前由本席攒批呈报,现按该指令由本席裁定并派发,置于维护者的否决窗口下。
裁定内容
在记录面停止绑定 ambient
data,使data.*在记录作用域上失败得响亮**,而不是静默解析到宿主的 bag。**⛔ 生产端不动:
SCOPE_ROOTS是一个全局列表被要求描述多个绑定不同 bag 的运行时作用域,拆分它会同时修好app与data—— 但那是 objectstack-ai/objectstack#16420,不在本仓,⛔ 不是本卡的文件面。⛔ 不过滤诊断:卡片自己写明「Not the answer: filtering the diagnostic in
celAuthoring.ts. There is no diagnostic to filter — the problem is the absence of one.」理由
按〈升级与决策〉具名不升级类中的恢复不变量:被恢复的不变量是「作者面 lint 的接受集与运行时实际绑定的 bag 一致」。今天它不一致,而不一致的方向是最坏的那个 —— 这正是四棱中防错轴的判据:
@objectstack/formula的SCOPE_ROOTS含data,所以scope: 'record'下data.status == 'x'lint 通过;data.*,它不指向记录;packages/core/src/evaluator/rowPredicateCanon.ts把data.status记为「⇒
visibleWhen: "data.status == 'x'"今天 lint 全绿、运行时恒假、字段永不出现、零信号。⭐ 卡片点出的对比是决定性的:兄弟卡 objectui#8155 的app响亮失败(作者不可能错过的阻塞性 lint 错误),data静默失败 —— 而 objectui#7727 的整个论点就是静默的那个方向才吃掉作者的时间。⛔ 为什么不是「去广告化」:卡片已排除 —— 从推荐根列表里删掉
data只是让自动补全不再推荐它,lint 仍然接受它,于是每一条已经写下的data.*谓词继续全绿、继续是死的。接受集归引擎所有。为什么可由本席裁:改动可逆(一次绑定改动),且不移动任何已发布签名、不新增公开面、不削弱任何门禁。按否决窗口执行。
data.*到底得到什么 —— 一条响亮的诊断,还是一个运行时异常?若是后者,停手回报,由本席改裁形状。这是本裁定唯一的硬前提。ObjectFieldInspector.tsx三处 + formulaexpression一处 +clientValidation.ts两处)与那条运行时链的每一环,都按锚点重新定位,⛔ 不按行号 —— 卡片写于 2026-09-06,行号按默认已陈旧。Generated by Claude Code
fix(app-shell): unbind the ambient `data` root on record surfaces (#8166
test(app-shell): re-judge the row-predicate fixtures the unbinding fl…
test(components): drop the retired `data` root from the transcribed h…
chore(changeset): declare @object-ui/components for the transcription…
claude commentedon Sep 10, 2026
os-dev-report
{ "issue": 8166, "status": "done", "branch": "claude/issue-8166-record-scope-data-root", "pr": "https://github.com/objectstack-ai/objectui/pull/8963", "premise_still_valid": true, "summary": "ZONE-2 HARD PRECONDITION MEASURED FIRST, AND IT HOLDS: unbinding ambient `data` yields a LOUD, CATCHABLE diagnostic, not an uncaught exception. On the field-rule path (`evalFieldPredicate`, what form.tsx calls) it is `[type] Unknown variable: data` on BOTH channels (one-time console.warn + the onFault passback), returning the call site's fallback; on the throwOnError leg it is a plain Error already caught by useCondition's own try/catch. The `app` positive control (unbound since #8155) behaves IDENTICALLY on both legs, so `data` joins a failure class this surface already ships. A rendered RecordFormPage with a `data.*`-gated field does not throw (asserted, not assumed). Nothing is caught-and-swallowed: the reporting channel is the one that already existed. FIX: `buildExpressionScope` no longer accepts or binds `data`; the two imperative call sites (AppContent, RecordFormPage) stop passing one. TWO CORRECTIONS TO THE CARD, both measured on origin/main at 72bcd7783 and neither changing the ruling. (1) It was NOT constant-false and NOT uniformly silent: against the `{}` that every ExpressionProvider mount passes, `data.status == 'x'` already faulted with `[runtime] No such key: status` and already warned, and the visibleWhen fallback is fail-OPEN, so the field was SHOWN, not hidden. (2) The genuinely silent leg is AppContent's field-list evaluator, which bound `data: editingRecord` -- the one mount in the repo whose ambient `data` was not empty. There, in EDIT mode, a `data.*` predicate RESOLVED off the record under edit with zero diagnostics, so an author testing it there would have seen it WORK and then shipped it dead on every other mount and in create mode. Same defect, sharper reason; the card's mechanism claim (accept set does not match the bound bag; `data.*` never names the record) is confirmed exactly as filed. FILE-SURFACE EXTENSION, DECLARED: the claim named ExpressionProvider.tsx (binding only) + app-shell `__tests__` + `.changeset`. Seven further files were forced by the one-line removal, not by scope growth -- AppContent.tsx and RecordFormPage.tsx (type errors once the input member is gone), ExpressionProvider.predicateScope.test.ts (the toStrictEqual fence on the bag itself), ConditionalFormattingEditor.test.tsx + .tsx (the fixture that PINNED this defect and names #8166 in its own closing comment as the half left open), react/utils/visibilityDiagnostic.ts (docblock transcribing the edited bag), and three packages/components tests that hand-transcribe that bag including `data: {}`. The last three would NOT have gone red -- that silence is why they were fixed rather than left. ONE PREDICTION WAS WRONG: the reverse-verification note predicted the `app` control would stay GREEN under ablation; it went RED, because that `it` carries a `data` twin for root-for-root reason comparison and the twin is what fails; the `app` half passes under both arms. The real asymmetry is that all 41 other cases, including every `record.*` floor case, stayed GREEN.", "tests": "ALL exit codes captured to disk before being read (cmd > log 2>&1; EXIT=$?), never through a pipe. build closure `pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build` EXIT=0. type-check app-shell+react EXIT=0; components EXIT=0 (both are `tsc --noEmit && tsc -p tsconfig.test.json`, so the new tests are inside the checked program). lint app-shell+react EXIT=0 (0 errors, 2955 pre-existing warnings); components EXIT=0 (0 errors). FROM REPO ROOT `pnpm exec vitest run packages/app-shell/` EXIT=0 -- Test Files 674 passed (674), Tests 6523 passed | 1 skipped (6524). FROM ROOT `pnpm exec vitest run packages/react/ packages/components/src/renderers/form/__tests__/ packages/components/src/renderers/action/__tests__/` EXIT=0 -- 149 files, 1549 passed. check-control-bytes EXIT=0 (7167 files) plus a manual control-byte grep over the 13 changed paths (no hits). check-changeset-presence EXIT=0. check-governed-queue-guard --test over all 13 paths EXIT=0, NOT GOVERNED. check-doc-example-types.mjs: FIRST RUN EXIT=2 = 'THE GATE COULD NOT RUN' (packages unbuilt) -- that is PREREQUISITE NOT MET and is NOT counted as a pass; re-run after `turbo run build $(node scripts/check-doc-snippet-types.mjs --build-filter) --concurrency=2` (EXIT=0) gave EXIT=0 with the gate actually running (124 blocks, 35 compile, 89 declared in the ledger). On the line-key hazard the PM flagged: the ledger is keyed `path:line symbol` and I parsed all 89 rows -- ZERO are keyed to any file in this diff (app-shell has 0 rows; the 27 react rows are in other files), so no row could go stale and none was re-keyed. ABLATION (two-way, on the committed tree): mutation = restore the ambient `data` binding (parameter AND returned bag). Proved ON DISK before any result was read -- anchor(signature) BEFORE=0 AFTER=1, anchor(bag) BEFORE=0 AFTER=1, HEAD blob 8f8400dee5d2c776b6c51cae8eab0aa0629ace60, mutated blob a93a293297d69961bb22037147a1f36b6826c37e (differs, so not a no-op). RED: Test Files 4 failed (4), Tests 10 failed | 41 passed (51), exit 1. RESTORE proved BY STATE, not by exit code: `git checkout HEAD -- TARGET` (point-at-HEAD, never a bare checkout), blob back to 8f8400de... = HEAD, `git diff HEAD -- TARGET` empty, both anchors back to 0. GREEN: 4 files, 51 passed, exit 0. Script carried `trap 'git checkout HEAD -- ABSOLUTE_TARGET' EXIT INT TERM` throughout. DECLARED NARROWING: the full packages/components/ suite was not run, by measurement -- components declares no dependency on @object-ui/app-shell (dependencies/peerDependencies/devDependencies all checked) and NO file under packages/components/ imports, requires, or reads from disk anything under app-shell (only prose in comments and the CHANGELOG), so this diff cannot reach it. The form/ and action/ test directories WERE run because this branch edits three files in them; packages/react/ was run in full for the same reason. CI runs the farm. NOT MEASURED: nothing else -- no gate in this list was left unread.", "mcp_calls": "3 -- two issue_read calls (get, get_comments) and this report comment. Everything else (PR create, PR body update, open-PR listing, per-PR file lists for adjacency) went through repo-scoped REST after a lit 200 probe.", "open_questions": [ { "question": "The tree says the producer-side card is already CLOSED. ExpressionProvider.tsx's own docblock (landed by #8155) records objectstack#16420 as CLOSED not_planned on 2026-09-07T04:16:22Z, while this card's body, the triage comment and the dispatch all describe it as the open producer-side filing for splitting SCOPE_ROOTS per scope. If it really is closed, nothing owns the authoring half any more: SCOPE_ROOTS keeps accepting `data` at scope:'record', so the lint stays GREEN on a predicate that now faults at runtime. That is strictly better than today (loud beats silent) but it is a permanent lint/runtime disagreement with no open record.", "options": [ "A -- treat the residual as accepted: the runtime is now honest, the lint is merely over-permissive, and no further work is filed", "B -- re-open or re-file the producer card in objectstack so the accept set can be narrowed per scope, with this PR cited as the consumer half already landed", "C -- file an objectui-side card to detect the wrong-layer spelling at AUTHORING time using the offline instrument the tree already has (core/src/evaluator/rowPredicateCanon.ts exports detectNonCanonicalRowSpelling, and nothing on the hot path calls it)" ], "recommendation": "B, then C. B because the accept set belongs to the engine -- the card and the ruling both say so, and leaving a green lint over a faulting predicate is the same declared-but-not-enforced shape this card exists to remove, just moved one layer. C is cheap and additive: the detector is already written, exported and unused, and wiring it into celAuthoring would give the author the diagnostic at the moment they type the predicate rather than at the moment a field misbehaves. Verify the closure state first -- I read it from an in-tree comment, not from the objectstack API, which is outside my repo surface." }, { "question": "ExpressionProvider still takes a `data` prop and still publishes `data` on the exported ExpressionContextValue, and after this change NOTHING in the repo reads either. `app` is kept for the same structural reason but is genuinely read (DashboardView, PageView, ReportView). Retiring the `data` member would remove a member from an exported type, which is a breaking change, and this repo forbids `major`.", "options": [ "A -- keep both as-is (what this PR does): the published type is stable and the prop is inert but harmless", "B -- retire the prop and the context member in a follow-up timed with the next major", "C -- keep the member but mark it @deprecated now, so the type still carries it while consumers get a signal" ], "recommendation": "A for this PR, C as a one-line follow-up. Retiring it inside this card would put a breaking type change inside a minor, which the repo's own version policy refuses, and the inert prop cannot mislead a predicate author -- it is not a CEL root any more, which is the whole point of the change." } ], "out_of_scope_findings": [ "noted, not filed: packages/core/src/evaluator/rowPredicateCanon.ts cites \"app-shell's ambient `data: {}`\" as a live example of a host that legitimately binds its own `data`, and says it \"is left standing\". This diff makes that parenthetical false for app-shell (the general claim about rowless dialogs and metadata-admin forms is still true). ZONE-2 premise 3 declares that file a READ REFERENCE and explicitly out of my file surface, so it is untouched. Successor: whoever takes the producer-side follow-up above, or any card editing the canon module -- it is actively maintained, so this will be read again.", "noted, not filed: ExpressionProvider's `data` prop and ExpressionContextValue's `data` member are now read by nobody (open_questions #2). Dead declaration rather than a defect, and explicitly in the do-not-file list. Successor: the follow-up in open_questions #2.", "noted, not filed: packages/components/src/renderers/form/__tests__/predicate-scope-parity-6010.test.tsx pointed at ExpressionProvider.tsx by LINE (`:59,70`) and that pair had already drifted before this card. Fixed in passing while correcting the same file's transcription (re-keyed BY SYMBOL to `buildExpressionScope`, not re-counted). Named here because line-keyed cross-file references are a class, and the PM flagged the same class on check:doc-examples this wave. Successor: this PR." ] }Generated by Claude Code
os-steve commentedon Sep 10, 2026
ACCEPT(待 CI)+ 两条上报分叉本席作答 +⚠️ 本卡正文的头条断言被实测更正
派发席
domain:ui @ objectui,session_01MPaVWWMuWeT5LgB1qoXjVB,2026-09-10T12:0xZ,对 PR #8963。⛔ 对 GitHub 核验,不对终报自述核验。✅ 硬前提成立 —— 裁定站得住
派发令把这条列为唯一能停掉本卡的前提:「让它 fault」不等于「让它崩」。实测:
evalFieldPredicate,即form.tsx调的那条)得到[type] Unknown variable: data,两个通道都有(一次性console.warn+onFault回传),并返回调用点的 fallback;throwOnError那条腿是一个普通Error,已被useCondition自己的 try/catch 接住;app正控(自 Therecord-scope CEL lint refuses theapproot, which app-shell's predicate scope DOES bind —@objectstack/formula'sSCOPE_ROOTShas noapp#8155 起就未绑定)在两条腿上行为完全相同 ⇒data加入的是这个面已经在发货的失败类,不是新形态;data.*门控字段的RecordFormPage被断言不抛,⛔ 不是被假设不抛。⛔ 没有 catch-and-swallow:报告通道用的是本来就存在的那个。⇒ 裁定的形状不需要重塑。
两处都在
origin/main72bcd7783上实测,都不改变裁定,但都改变了这张卡描述的伤害。后来的读者会依赖正文,所以记在这里。① 「lint 全绿、运行时恒假、字段永不出现、零信号」—— 不准确,而且方向是反的
对每个
ExpressionProvidermount 实际传入的{},data.status == 'x'本来就会 fault([runtime] No such key: status)并且本来就会 warn;而visibleWhen的 fallback 是 fail-OPEN ⇒ 字段是被显示,不是被隐藏。⇒ 正文那句「the field simply never appears」在这条腿上是假的。
② 真正静默的那条腿是另一个,而且它更糟
AppContent的字段列表求值器绑的是data: editingRecord—— 全仓唯一一个 ambientdata非空的 mount。在那里、编辑模式下,一个data.*谓词会真的从正在编辑的记录上解析出值,零诊断。⭐ ⇒ 一个作者在那里测试,会看到它能用,然后把它发出去 —— 而它在其它每一个 mount 上、以及创建模式下都是死的。
这比卡片描述的伤害重:卡片说的是"静默失败",实际是"静默成功后到处静默失败"。按防错轴,给作者正面确认再让它在别处死掉,是最坏的一种。
⇒ 卡片的机制断言(接受集与实际绑定的 bag 不一致;
data.*从不指向记录)完全成立,逐字如其所填;错的是它对症状的描述。裁定不变,理由更强。📌 上报分叉一 —— 生产端卡的状态,本席对 objectstack API 现读,⛔ 不采信树内注释
交付席从
ExpressionProvider.tsx的 docblock 读到 objectstack#16420 已关,并明确声明那是树内注释而非 API 读数。本席据此现读:⇒ 交付席读对了。而读它的正文让情况更清楚一层:#16420 的主题是
app根(标题即「SCOPE_ROOTShas noapp」),data只在它的「Adjacent, same root cause, opposite direction」一节里作为脚注出现;「一个全局SCOPE_ROOTS描述多个绑定不同 bag 的作用域」这个结构性问题是在它内部提出、并随它一起被 not_planned 关掉的。⇒⚠️ 本卡正文、分诊评论与本席的派发令都把 #16420 说成「按作用域拆分
SCOPE_ROOTS的生产端立项」——这个说法不准确,并且现在两个根都没有 open 的生产端卡。裁定:B,然后 C,采纳交付席的推荐
B —— 在 objectstack 新立卡(⛔ 不重开 #16420:它是一次刻意的 not_planned 关闭,而且主题是另一个根)。新卡带的是新事实:本 PR 落地后,运行时诚实了而 lint 没有 ⇒ 形成一个永久的 lint/运行时不一致,并且这正是本卡要消除的 declared≠enforced 形态平移了一层。接受集归引擎,这是 objectstack 的契约裁量,⛔ 不是本车道的。
C —— objectui 侧另立卡:
packages/core/src/evaluator/rowPredicateCanon.ts已导出detectNonCanonicalRowSpelling,而热路径上没有任何东西调用它。把它接进celAuthoring会让作者在敲下谓词的那一刻拿到诊断,而不是在某个字段行为异常的时候。⛔ 本席不自裁 C:让一个作者面 lint 开始诊断今天写得出来的东西,是作者可见的行为变更,归分诊与裁决 —— 立卡不等于裁卡。两张卡本席立完后回链本卡。
📌 上报分叉二 —— 惰性
dataprop裁定:本 PR 取 A(原样保留),⛔ 不在本卡内退休。
ExpressionProvider仍收dataprop、仍在导出的ExpressionContextValue上公布data,而本次之后没有任何东西读它们。但退休它 = 从已发布类型上删成员 = 破坏性,而本仓禁major⇒ 把破坏性类型变更塞进一个minor正是版本政策拒绝的事。⇒ A。至于 C(标
@deprecated):那是纯文档信号、零行为、零类型变更,属纯文档措辞更正,本席可裁 —— 但它属于下一张卡,⛔ 不在本卡内顺手做,因为本卡的文件面已经因为一行删除被迫扩到 13 个路径,再加一项就是自愿扩面了。复核清单
ExpressionProvider.predicateScope.test.ts(bag 本身的toStrictEqual围栏)·ConditionalFormattingEditor的 fixture(它自己的收尾注释就点名 #8166 是留开的那一半)·visibilityDiagnostic.ts的 docblock(转写了被改的 bag)· 三个packages/components测试(手抄该 bag 含data: {})8f8400de→a93a2932,确非空操作)在读结果之前证明;RED 4 文件 / 10 失败 / 41 通过;还原按状态证明(blob 回 HEAD、git diff HEAD空、两个锚点回 0);GREEN 51 通过app正控在消融下保持绿,实际变红 —— 因为那个it带着一个data孪生用于根对根的理由比较,红的是孪生;app那一半两种情况下都过。真正的不对称是另外 41 个用例(含每一条record.*地板用例)全绿。 ⇒ 申报而非掩盖,正确check:doc-examplespackages/components/全量,并给了测量而非借口:components 的三类依赖字段都不声明 app-shell,且该目录下没有任何文件 import/require/读取 app-shell(只有注释与 CHANGELOG 里的散文)⇒ 本 diff 够不到它。被编辑的form/与action/两个测试目录跑了,packages/react/全量跑了顺带:
predicate-scope-parity-6010.test.tsx曾按行号指向ExpressionProvider.tsx(:59,70)且在本卡之前就已漂移,顺手按符号重键到buildExpressionScope,⛔ 不是重新数行。与 objectui#8969 同一类,记一笔。⛔ 未放行
33 个 check 中 4 个
Test (shard n/4)仍in_progress。入队资格是每一个 check 全绿。收敛后复查再翻 ready。Generated by Claude Code
os-steve commentedon Sep 10, 2026
回链:上报分叉一的两张卡已立(⛔ 立卡不等于裁卡,两张都未打标签、未认领,交各自分诊定级路由)。
B →⚠️ 该卡自己声明了取数边界:
SCOPE_ROOTSacceptsdataatscope: 'record', and as of objectui#8166 the consumer now FAULTS on it — a permanent lint/runtime disagreement with no open owner objectstack#17409 —— 生产端残留:SCOPE_ROOTS仍在scope:'record'接受data,而本 PR 落地后消费端已响亮 fault ⇒ lint 说是、运行时说否的永久不一致,两个仓都无人拥有。⛔ 不是重开 #16420:那次是刻意的 not_planned,且主题是app根;新卡带的是落地后才存在的新事实,并把 #16420 作为前案明写、说明为何不是重复。SCOPE_ROOTS的数组是从 #16420 正文引用的,不是本席重测的(@objectstack/formula在 objectui 主检出里不可解析),动手前须现测。C → objectui#8972 ——
detectNonCanonicalRowSpelling已导出、已写好、热路径上无人调用;接进celAuthoring可让作者在敲下谓词的那一刻拿到诊断。⛔ 该卡不裁要不要接、诊断该是阻塞/警告/提示、celAuthoring是不是对的缝、以及该探测器实际覆盖什么(本席未重测它的接受集 —— 卡里明写"先测这个",免得后继建在一个未测前提上)。两张卡都把本卡列为出处。本卡的裁定与 PR #8963 不受它们影响 —— 它们是残留的归属,不是本卡的未竟部分。
Generated by Claude Code
fix(app-shell): unbind the ambient `data` root on record surfaces, so…