|
635 | 635 | "title": "The Studio CEL editors reach the SAME verdict as the engine: formula result-type inference, previous. completion in conditional rules, and RLS lint + test-run — the editor UI, not a second grammar", |
636 | 636 | "since": "v16", |
637 | 637 | "status": "active", |
638 | | - "revision": 3, |
| 638 | + "revision": 4, |
639 | 639 | "priority": "P1", |
640 | 640 | "surface": "mixed", |
641 | 641 | "personas": ["admin"], |
|
649 | 649 | "knownGaps": [ |
650 | 650 | "the CEL editors lazy-load @objectstack/formula and FEATURE-DETECT every entry point (celAuthoring.ts) — a missing/older engine degrades to no lint / no suggestions / test-run 'unavailable', never an exception. If the vendored bundle lacks the engine, record the affected clauses blocked(dependency) and verify the graceful-degradation clause instead", |
651 | 651 | "AUTOMATION IS PINNED IN THE objectui REPO, NOT HERE — `automated.ref` names objectui specs exclusively, so from this checkout the item is neither runnable nor pin-evidenced (no console bundle is built here and none of those specs exist here). Drive it by hand in the browser and score it as a manual run, OR run the pin in an objectui checkout and cite the output naming the revision (17.1.0 sweep subject: 9a3daf8d37ad). ⛔ Never score it as covered on the strength of the `automated` field alone — an unrun pin is a claim, not evidence. Full protocol: RUNNER.md, the objectui-pinned-automation standing fact.", |
652 | | - "acceptance[7] (graceful degradation) is PIN-ONLY BY DESIGN: its verify forbids hand-breaking the bundle. The environment it needs is an objectui checkout at the .objectui-sha revision with its install done, to run packages/app-shell/src/views/metadata-admin/celAuthoring.test.ts (describe 'celAuthoring · graceful degradation', present at objectui 0abd4f9f8769). QA run #21784 had no runnable checkout and recorded blocked(environment) (parked on #22017 row 16), which is the right verdict without one: never a hand-driven verdict, never inferred from the source. CI in this repository does not run objectui's suites; objectui's own CI does, on objectui PRs" |
| 652 | + "acceptance[7] (graceful degradation) is PIN-ONLY BY DESIGN: its verify forbids hand-breaking the bundle. The environment it needs is an objectui checkout at the .objectui-sha revision with its install done, to run packages/app-shell/src/views/metadata-admin/celAuthoring.test.ts (describe 'celAuthoring · graceful degradation', present at objectui 0abd4f9f8769). QA run #21784 had no runnable checkout and recorded blocked(environment) (parked on #22017 row 16), which is the right verdict without one: never a hand-driven verdict, never inferred from the source. CI in this repository does not run objectui's suites; objectui's own CI does, on objectui PRs", |
| 653 | + "acceptance[2] and acceptance[4] score the editor against the ENGINE's tier for a non-lowerable RLS read predicate, which is ERROR (rls-predicate-unenforceable, fail-closed). At the console pin 4016370a the editor still gives it severity 'warning' with fail-open wording (objectui packages/app-shell/src/views/metadata-admin/celAuthoring.ts lintCelPredicate) and autosaves it — the product defect objectstack-ai/objectui#13061 (QA run #24604 negative[0], rule-7 verified). Until that lands, those two clauses FAIL as written: score them fail with that card cited, never 'pass as written'" |
653 | 654 | ] |
654 | 655 | }, |
655 | 656 | "steps": [ |
656 | | - "ground the engine truth FIRST (rule 6 parity): for each probe expression, capture the canonical verdict from the engine — via MCP validate_expression (site: formula / flow_condition) or `objectstack build` — so the editor is judged against server truth. The ENGINE itself is pinned by ai.mcp-validate-expression / api-backend.formula-gates; this item only checks the EDITOR agrees", |
| 657 | + "ground the engine truth FIRST (rule 6 parity): for each probe expression, capture the canonical verdict from the engine — via MCP validate_expression (site: formula / flow_condition) or `objectstack build` — so the editor is judged against server truth. validate_expression has NO RLS site (formula | validation | flow_condition | template), so the RLS probes' engine verdict is the author-time gate `os build` and publish run: packages/lint/src/validate-rls-predicate-enforceability.ts (rls-predicate-unenforceable / rls-predicate-unparseable, both error tier). The ENGINE itself is pinned by ai.mcp-validate-expression / api-backend.formula-gates; this item only checks the EDITOR agrees", |
657 | 658 | "F1 formula result-type: open the object field designer (ObjectFieldInspector) on a formula field (type=formula, role=value, scope=record), TYPE a proven-number expression (record.est_hours * 1.1, or record.quantity * record.unit_price) and, separately, one cel-js cannot prove (record.a + record.b); after the 250ms debounce, screenshot the inferred-result-type (Σ) affordance for each", |
658 | 659 | "type an unknown field (record.est_hourz) and a bare field ref in record scope (est_hours, no record. prefix); capture the inline error under the field (aria-invalid, destructive border) and its record.<field> fix text", |
659 | 660 | "F2 conditional-rule editor: open the same field's visibleWhen editor (scope=record, roots record/previous/parent); type `previous.` and then `record.` and capture the autocomplete listbox — it must offer the object's field catalog; type `current_user.` and confirm it does NOT list this object's fields; accept a suggestion and confirm the insertion", |
660 | | - "F4 RLS editor: open the permission set's Advanced Facets RLS policy (PermissionAdvancedFacets), enter a USING predicate that is NOT a simple field comparison (blast-radius) and one with a parse fault; confirm the non-pushdownable filter raises a WARNING (Save stays enabled) while the parse fault raises a blocking ERROR that disables Save (celErrorCount>0 → title perm.cel.saveBlocked)", |
| 661 | + "F4 RLS editor: open the permission set's Advanced Facets RLS policy (PermissionAdvancedFacets), enter a USING predicate that is NOT a simple field comparison (blast-radius) and one with a parse fault; confirm BOTH raise a blocking ERROR: the non-pushdownable filter at the engine's tier — rls-predicate-unenforceable refuses it at build and publish (ADR-0056 D4) because the runtime drops the policy and fails CLOSED, so it grants no access — and the parse fault as a parse error; with an error present the save is held (celErrorCount>0): the environment door's Save is disabled with title perm.cel.saveBlocked, and the Studio package door, which autosaves and has no Save button, shows the perm-autosave-held status and issues no PUT", |
661 | 662 | "RLS test-run: open the CelTestRunDialog ('Test this policy against a sample record'), supply a sample record + current_user, Run; capture allow (shield-check) / deny (shield-x) / a non-boolean value (non-bool smell) across three sample+predicate pairs", |
662 | 663 | "parity cross-check: for the SAME expressions, confirm the editor's verdict tier (error/warning/ok, inferred type, allow/deny) equals the engine verdict captured in step 1", |
663 | 664 | "persistence round-trip (writable package): Save the edited formula field → GET /api/v1/meta/object/<obj> shows Field.returnType stamped from the inferred type; Save the RLS policy → GET /api/v1/meta/permission/<name> carries the USING/CHECK predicate" |
|
676 | 677 | "evidence": "the meta read" |
677 | 678 | }, |
678 | 679 | { |
679 | | - "clause": "the editor's lint TIERS equal the canonical engine's — an unknown field and a bare field ref in record scope are ERRORS (with the record.<field> fix, blocking Save), a non-pushdownable USING read filter is a WARNING; the editor calls the SAME @objectstack/formula validateExpression the server and validate_expression use (ADR-0032), so the verdict is identical, not a second grammar", |
| 680 | + "clause": "the editor's lint TIERS equal the canonical engine's — an unknown field and a bare field ref in record scope are ERRORS (with the record.<field> fix, blocking Save), and a non-pushdownable (non-lowerable) USING read filter is an ERROR too: the engine refuses it at build and publish (rls-predicate-unenforceable, ADR-0056 D4) because the runtime drops the policy and fails CLOSED. The editor calls the SAME @objectstack/formula validateExpression the server and validate_expression use (ADR-0032), so the verdict is identical, not a second grammar", |
680 | 681 | "oracle": "api", |
681 | | - "verify": "for each probe, the editor's inline finding severity matches validate_expression / build for the same expression+site (error vs warning vs ok)", |
| 682 | + "verify": "for each probe, the editor's inline finding severity matches the engine for the same expression+site (error vs warning vs ok): validate_expression / build for the formula and conditional-rule probes, and — validate_expression having no RLS site — the build/publish gate's rls-predicate-unenforceable (error) for the non-pushdownable USING probe (POST /api/v1/meta/permission/NAME/publish answers 422 naming the rule). An editor WARNING there is the tier divergence negative[0] scores as a FAIL", |
682 | 683 | "evidence": "the paired editor screenshot + engine verdict, per probe" |
683 | 684 | }, |
684 | 685 | { |
|
688 | 689 | "evidence": "the autocomplete DOM reads + the bare-ref error" |
689 | 690 | }, |
690 | 691 | { |
691 | | - "clause": "the RLS blast-radius advisory and the Save gate work together: a non-pushdownable USING read filter raises the fail-open WARNING (advisory, Save stays enabled), while a parse fault raises a blocking ERROR that disables Save (celErrorCount>0, title perm.cel.saveBlocked) — a malformed predicate cannot be persisted", |
| 692 | + "clause": "the RLS lint and the save gate agree with the engine: a non-pushdownable USING read filter raises a blocking ERROR, not an advisory warning, because the engine refuses it (rls-predicate-unenforceable, ADR-0056 D4) and the runtime fails CLOSED — the policy is dropped and grants no access, it never widens access — and a parse fault raises a blocking ERROR too; with either error present the save is held (celErrorCount>0) — a predicate the engine refuses cannot be persisted", |
692 | 693 | "oracle": "screenshot", |
693 | | - "verify": "the non-pushdownable filter shows the widen-access warning with Save still enabled; a parse-faulted predicate disables Save", |
694 | | - "evidence": "the two editor states" |
| 694 | + "verify": "the non-pushdownable filter shows an error-tier finding and the save is held; a parse-faulted predicate is held the same way. Held means: on the environment door, Save disabled with title perm.cel.saveBlocked; on the Studio package door, which autosaves and has no Save button, the perm-autosave-held status with no PUT issued (only the package door is writable on stock showcase)", |
| 695 | + "evidence": "the two held editor states + the network trace showing no save PUT" |
695 | 696 | }, |
696 | 697 | { |
697 | 698 | "clause": "test-run dry-runs through the SAME engine the server uses: a predicate that should allow the sample returns allow, one that should deny returns deny, and a non-boolean returns the value/non-bool smell — matching a direct engine evaluate of the same predicate+scope", |
|
720 | 721 | "variants": [ |
721 | 722 | "formula field expression (role=value, scope=record) — inferred result type", |
722 | 723 | "field conditional rule visibleWhen / readonlyWhen / requiredWhen (scope=record, roots record/previous/parent) — previous. completion", |
723 | | - "RLS USING read filter (pushdown / fail-open advisory)", |
| 724 | + "RLS USING read filter (lowerable, or non-lowerable → ERROR and fail-CLOSED — never a fail-open advisory)", |
724 | 725 | "RLS CHECK write filter" |
725 | 726 | ], |
726 | 727 | "traps": ["stale-console-bundle", "hydration-race", "automation-input"], |
|
748 | 749 | "date": "2026-10-06", |
749 | 750 | "change": "partial-coverage gap of QA run #21784 parked on #22017 (row 16). A8 was blocked(environment): its pin lives only in objectui and no checkout at the pin was runnable. Disposition: pin. The existing objectui pin (celAuthoring.test.ts, 'graceful degradation') is confirmed at the current .objectui-sha (0abd4f9f8769), and a knownGap names the environment that runs it and the verdict without it. No clause changed", |
750 | 751 | "ref": "#22017" |
| 752 | + }, |
| 753 | + { |
| 754 | + "revision": 4, |
| 755 | + "date": "2026-10-19", |
| 756 | + "change": "acceptance[2] (clause, verify) and acceptance[4] (clause, verify, evidence) re-pointed from the editor's WARNING tier to the engine's ERROR tier for a non-lowerable RLS read predicate — §1 of wave anchor #24579 (added by its round-2 comment 6272314386), from QA run #24604 at ba4afcc2 and QA run #24571 at 5b14b7f3. The two clauses required a non-pushdownable USING filter to be an advisory warning with Save enabled and fail-open framing. The engine refuses that predicate at build and publish (packages/lint/src/validate-rls-predicate-enforceability.ts, rls-predicate-unenforceable, error; ADR-0056 D4), and the runtime fails CLOSED (plugin-security's compileFilter answers the deny filter). So the item contradicted its own negative[0], which fails any editor verdict that differs from the engine, and #24604 scored acceptance[4] 'pass (as written)' beside that negative[0] FAIL. Riding the same re-point, so the item does not contradict it: steps[4] and variants[2] now expect the error tier; steps[0] names the build/publish gate as the RLS probes' engine truth (validate_expression has no RLS site); acceptance[4] names the Studio package door's autosave-held status beside the environment door's disabled Save (#24604: the package door has no Save button); and a knownGap records that the clauses fail until objectstack-ai/objectui#13061 lands", |
| 757 | + "ref": "#PENDING-24579R2" |
751 | 758 | } |
752 | 759 | ] |
753 | 760 | }, |
|
0 commit comments