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 "⚠️ silently accepted" by the authoring oracle.
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:
⛔ Not the answer: filtering the diagnostic in celAuthoring.ts. There is no diagnostic to filter — the problem is the absence of one.
Related
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.
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'sSCOPE_ROOTScontainsdata(measured dump in #8155). So atscope: 'record'the authoring lint acceptsdata.status == 'x'. But objectui#5741 (Phase 2 of the #5330 canon) retireddata.*on record surfaces — it does not name the record. The tree already knows this:packages/core/src/evaluator/rowPredicateCanon.ts:36-38recordsdata.statusas "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
maintoday, independent of #8164Authoring side — already at
recordscope before #8164:inspectors/ObjectFieldInspector.tsx:874visibleWheninspectors/ObjectFieldInspector.tsx:888readonlyWheninspectors/ObjectFieldInspector.tsx:902requiredWheninspectors/ObjectFieldInspector.tsx:760expression(role:'value')views/metadata-admin/clientValidation.ts:792,:806Runtime side — the chain that makes
dataresolve to the host's bag rather than the record:So
visibleWhen: "data.status == 'x'"lints clean, resolves against the host's ambientdata, 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
datacase is nastier than #8155'sappcase in exactly the way that matters:appfails loudly (a blocking lint error the author cannot miss),datafails 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
datafrom any advertised-roots list. De-advertising 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.Fix shape (for triage, not ruled)
Same producer/consumer split as #8155, and the two should probably be decided together:
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.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
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).osis bound bybuildExpressionScopebut unadvertised to row-predicate autocomplete — decide curation vs. omission #8156 —osis bound but unadvertised (curation, harmless).flattenedscope and advertises bare fields — declared-but-unbound once #5741 (Phase 2) retires the bare-field binding #7727 / fix(app-shell): lint conditional-formatting conditions in the record scope #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
appmirror. Nearest closed hits are different surfaces and different mechanisms: #4807 (record:alertbare scope), #5687 (SchemaRendererresolvingdata.*against the data-source adapter), #5454, #5776. No open card namesdataagainst the record-scope accept set on the field-rule surfaces.