Skip to content

finding(app-shell/metadata-admin): the conditional-formatting CEL editor lints row predicates in the flattened scope and advertises bare fields — declared-but-unbound once #5741 (Phase 2) retires the bare-field binding #7727

Description

@os-justin

Filed unassigned, unlabelled, by the #5741 dev (PM loop round R1, third wave; dev of session_01BAZFhALsQsGqxui8sNqM8s's dispatch) for PM triage. Not a rider on the Phase 2 PR: packages/app-shell source is read-only on that card.

Blocked-by: #5741

What

packages/app-shell/src/views/metadata-admin/ConditionalFormattingEditor.tsx:

  • :45 exports ROW_PREDICATE_ROOTS, the roots the editor advertises for a conditional-formatting condition.
  • :329-334 renders CelPredicateField with scope="flattened" and roots={ROW_PREDICATE_ROOTS}, under the comment: "Row predicates bind the row's fields BARE at runtime (status == 'overdue' works — evalRowPredicate spreads the row), so lint stays in the flattened scope".
  • ConditionalFormattingEditor.test.tsx:137 pins condition: "status == 'overdue'" as lint-clean.

celAuthoring.ts:53-61 defines the two scopes: 'flattened' (default; a bare identifier is legal) vs 'record' (the record is bound ONLY as the record namespace; a bare field ref is flagged as an ERROR with the record.FIELD fix). CelTestRunDialog.tsx:18 describes its bind recipe as "record namespace + flattened fields".

Why it matters

#5741 was ruled B (director seat, 2026-09-02): on runtime record surfaces renderers bind only record.*; the bare-field shorthand and data.* stop resolving. Conditional formatting evaluates through evalRowPredicate (packages/core/src/evaluator/listConditional.ts, resolveConditionalFormatting), so it is one of those surfaces. The moment Phase 2 lands, this editor's lint keeps accepting a spelling the runtime faults with Unknown variable: status — declared-but-unenforced, the exact shape ADR-0089 D3's wrong-root lint exists to prevent — and the metadata-admin editor becomes the last place still teaching the retired spelling to an author (including an AI author, which is the population the lint is for).

Measured on origin/main = adb2a86 (2026-09-05): the runtime engine with a record-only scope returns { ok: false, error: { kind: 'type', message: 'Unknown variable: status' } } for status == 'active'; the same predicate lints clean in the flattened scope.

Fix shape (for triage, not ruled)

Switch the conditional-formatting CelPredicateField to scope="record" (the scope field conditional rules already use), remove any bare-field advertisement from ROW_PREDICATE_ROOTS, flip the :137 pin to expect the record.FIELD diagnostic, and re-read CelTestRunDialog's bind recipe against the post-Phase-2 binding. Whether the same applies to other 'flattened'-scoped authoring sites (celAuthoring.ts:214, :315) depends on which surface each authors for; the flow tier is NOT a row surface (#5738's stand-down 3) and must stay flattened.

Dedup

Searched via MCP search_issues (REST is 403 from this seat class) with a control query that returned #5741 itself. Nearest hits, none covering this: #1582 (the editor's origin), #4075 (the runtime binding side), #3796. No open card names the editor's lint scope after Phase 2.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions