Repository navigation
Description
domain:devx#1 · sunhaolin · session_017xRBcRX4wwwVw5bEckffdp
Part of #27377 (wave 3, W3-RT — retest of the wave's fixed FAILs)
Dispatched by the PM session session_017xRBcVy4VXmkQjn2MGeaWk on the maintainer's instruction, verbatim: 「同意开 复测云卡片」. This run re-judges, live, the wave-3 FAILs whose fixes landed after c4c515ad (anchor §0.1, column "re-judged": no). It also takes the first live read of approvals.dynamic-approver-routing acceptance[3]'s snapshot leg (anchor comment 6539452860). Per checklist-test SKILL §5, this is trigger 2. The runner only tests.
Environment fingerprint
- Framework:
6e11a929e453e1f9a78a9eb7b311c46b2fa8d094(mainat 2026-11-01T10:15Z), detached.ui/is the same tree. The tree was clean before and after (git statusempty). - Build:
pnpm install --frozen-lockfile, thenpnpm build --concurrency=2(10:38–10:47Z), thenpnpm console:build --concurrency=2, which printed72 successful(a cache hit plus a copy).node scripts/check-dev-prereqs.mjs: 106 package artefacts present.
- Console: the served
/_console/entryindex-rNiny7yV.jsequalspackages/console/dist/assets/index-rNiny7yV.js(byte-identical, sha256a386a1fc…). Checked on every boot, which rules out a stale console bundle. - App and boots: stock showcase. One compile boot brought
examples/app-showcase/dist/objectstack.jsoncurrent. Every item then booted from the tree:- with
--no-compile --no-watch --seed-admin; - with
OS_PORTandOS_STORAGE_LOCAL_ROOTexported; - each on its own port and its own file DB: ports 41711–41714, 41721–41724, 41731–41734 and 41741, DB
file:/tmp/w3rt/ITEM/data.db; - stopped by process group afterwards.
- The email item's declared-source reload leg booted from a scratch copy of the app.
- with
- Fixtures: writable fixture packages were authored at runtime through
POST /api/v1/packageson each item's own DB, with no tree edits. - Personas:
- the seeded admin;
- members minted per RUNNER (invite then sign-up, or admin create-user);
- the seeded demo personas Ada Auditor and Mei.
- Each persona's identity was proved with
GET /api/v1/auth/get-sessionbefore its cells, in one browser context per persona, signed in through the console form.
- Browser: Playwright chromium, headless, at most 3 browser runners at once.
- Runners:
- four runner subagents (R1–R4, effort high) plus one independent RUNNER-rule-7 verifier for the P0 FAIL;
- each subagent's verdicts were checked against its artefacts (network logs, DB rows, screenshots, pin output) before they were accepted.
- Clock: 2026-11-01, 10:37Z to 12:10Z, container timezone
Etc/UTC.
Scope
Selector release-18.3.0-w3-retest: 13 item ids, each pinned at the revision node scripts/checklist-select.mjs ID --json reports at 6e11a929. No planned items.
| item | rev | retests | earlier verdict |
|---|---|---|---|
studio-authoring.expression-editors |
7 | #27482 (arms #27612, #27643, #27644), #27485 | FAIL #27441, #27438 |
studio-authoring.access-owd-and-suggestions |
4 | #27458 | FAIL #27441 acc[1] |
studio-authoring.app-and-nav-properties |
4 | #27457 | FAIL #27441 acc[3] |
studio-authoring.authored-page-slots-and-variables |
3 | #27457 | FAIL #27441 acc[1], neg[0] |
studio-authoring.dashboard-widget-and-filter-types |
4 | #27459 | FAIL #27441 acc[4] |
studio-authoring.summary-field-authoring |
4 | #27461 | FAIL #27441 acc[0], acc[1], neg[0] |
studio-authoring.held-edit-leave-guard |
4 | #27464 | FAIL #27441 acc[1] |
integration-system.email-template-render |
11 | #27488 | FAIL #27438 neg[0] |
studio-authoring.object-api-tab-curl |
2 | #27490 | FAIL #27438 acc[1], acc[2], neg[0] |
approvals.account-app-entry |
7 | #28546 | FAIL #28545 acc[3], acc[4] |
approvals.dynamic-approver-routing |
5 | acc[3] snapshot leg (6539452860) | — |
api-backend.formula-stdlib-matrix |
6 | acc[0] re-point 29 → 30 (#28340) | FAIL #27441 acc[0] |
records-forms.field-history-tracking |
10 | acc[2] re-point (#27492) | FAIL #27438 acc[2] |
Item verdicts and "fix holds?"
| item | verdict | fix holds? |
|---|---|---|
studio-authoring.expression-editors |
FAIL (A2, A4, A6, N0) | #27485 holds. #27612 holds. #27643 holds. #27644 holds. Parent #27482 (open) does not hold as filed: its steps 3 and 5 still reproduce. The first autosave of each newly refused RLS text goes out with 200 (the formula-field probe). |
studio-authoring.access-owd-and-suggestions |
PASS | #27458 holds |
studio-authoring.app-and-nav-properties |
PASS | #27457 holds (nav visible) |
studio-authoring.authored-page-slots-and-variables |
PASS | #27457 holds (page visibleWhen) |
studio-authoring.dashboard-widget-and-filter-types |
PASS | #27459 holds |
studio-authoring.summary-field-authoring |
PASS | #27461 holds |
studio-authoring.held-edit-leave-guard |
PASS | #27464 holds |
integration-system.email-template-render |
PASS | #27488 holds |
studio-authoring.object-api-tab-curl |
PASS | #27490 holds |
approvals.account-app-entry |
FAIL (A3) | #28546 holds for its scope: no access-denied screen, and zero data-door reads. A3 fails on a different door, the action door (see its reproduction). It does not reopen #28546. |
approvals.dynamic-approver-routing |
PASS | The snapshot leg was read live for the first time; the clause's key spelling is accurate. |
api-backend.formula-stdlib-matrix |
PASS | The #28340 re-point holds: the count is 30 (29 chained registerFunction + label), and dist, docs and CEL_STDLIB_FUNCTIONS all agree. |
records-forms.field-history-tracking |
PASS | The #27492 re-point holds. |
Per-clause verdicts (text oracle evidence)
studio-authoring.expression-editors · rev 7 · port 41711
| clause | verdict | evidence |
|---|---|---|
| acceptance[0] | pass | The inspector reads "Valid CEL · Result type: Number" for record.est_hours * 1.2. For record.a + record.b it reads "Result type: Unknown — the engine cannot prove a single type. Wrap operands in double() / int() / string()…". The engine (inferExpressionType) and MCP validate_expression give number and unknown. |
| acceptance[1] | pass | Editing f_formula to string(record.est_hours) sends an autosave PUT (200) carrying returnType:"text". The un-edited f_total keeps returnType:"currency", and the draft reads _diagnostics.valid:true. |
| acceptance[2] | fail | 11 of 12 probes match the engine's tier. These now match: the non-lowerable USING (error, engine 422 rls-predicate-unenforceable) and the parse fault (error, engine 422 rls-predicate-unparseable). The formula-field probe record.f_formula > 5 does not: the editor reads "Valid CEL", while the publish door answers 422 rls-predicate-unenforceable (INVALID_FIELD, virtual formula field). |
| acceptance[3] | pass | previous.est gives [est_hours] and record.est gives [est_hours]; accepting inserts record.est_hours. current_user. and parent. open no listbox. A bare est_hours > 5 is aria-invalid, with the record.est_hours fix offered. |
| acceptance[4] | fail | The non-lowerable and parse-fault USING are held: perm-autosave-held "Fix the CEL syntax errors before saving.", and 0 PUTs in 6 s. The formula-field USING (f_formula > 5, > 7, > 9, f_total > 5) is not held: it reads "Valid CEL", and PUT /api/v1/meta/permission/qee_job_editor?mode=draft&package=com.qa.rone&surface=studio → 200 carries the refused predicate. The stored draft rows hold it. |
| acceptance[5] | pass | record.status == 'open' gives "Allowed" for {status:open} and "Denied" for {status:closed}. record.est_hours gives "Non-boolean result 7" for {est_hours:7}. ExpressionEngine.evaluate gives true, false and 7. |
| acceptance[6] | fail | The parity table is 11 of 12. The divergent probe is the formula-field RLS one (editor ok, engine error). |
| acceptance[7] | pass | Pin ui/…/metadata-admin/celAuthoring.test.ts: 40/40 at 6e11a929, including the 5 cases of celAuthoring · graceful degradation. Unit layer, which is this clause's oracle layer. |
| negative[0] | fail | The editor's verdict differs from the engine's on an RLS USING over a formula field. |
| negative[1] | pass | A parse-fault formula record.est_hours > ( is held through Escape, Close, clicking another card, and the Records tab: 0 PUTs in 8–10 s. An RLS parse fault: 0 PUTs. |
| negative[2] | pass | No unbound-root members are offered, and the bare ref is flagged. |
studio-authoring.access-owd-and-suggestions · rev 4 · port 41712
| clause | verdict | evidence |
|---|---|---|
| acceptance[0] | pass | One PUT /api/v1/meta/object/qow_note?mode=draft&package=com.qa.rtwo&surface=studio → 200 with sharingModel:"public_read", and no write for the other two objects. |
| acceptance[1] | pass | qow_step shows "By parent → qow_project". The no-master qow_note answers 422 at sharingModel. On qow_step, 409 DESTRUCTIVE_CHANGE opens the confirm "Make the relationship required?", and the force PUT → 200. After publish, live controlled_by_parent with project required: true. |
| acceptance[2] | pass | Grant: POST …/suggested-bindings/ID/confirm 200 confirmed, and member M goes from 403 to 200. Revoke: /dismiss 200, and M gets 403. |
| acceptance[3] | pass | The second suggestion was dismissed → dismissed, and M gets 403 on qow_project. |
| negative[0] | pass | Grant and revoke took effect for M at once. |
studio-authoring.app-and-nav-properties · rev 4 · port 41721
| clause | verdict | evidence |
|---|---|---|
| acceptance[0] | pass | --brand-primary: #e11d48 and a green accent. The same-origin logo loads. The area switcher shows Notes Area and Tasks Area, and the context selector renders. |
| acceptance[1] | pass | The keys land where they should, for both personas: viewName → /qrt_note/view/closed_only "Closed Only · 3"; recordId + recordMode → the edit form; filter[status]=open → "2 records" (API total 2); the badge reads "NEW" with bg-destructive. |
| acceptance[2] | pass | The requiresObject: qrt_no_such_object entry is absent for both personas (/meta/app serves it, so the gate is client-side). |
| acceptance[3] | pass | The bare-string 'org_owner' in current_user.positions entry shows for the admin (who holds org_owner) and is absent for the member. No predicate fault: the "Unexpected token" fault #27441 recorded is gone. |
| acceptance[4] | pass | For the member, the hidden: true app's meta read answers 200, the app is absent from the switcher and the launcher, and its URL opens. |
| acceptance[5] | pass | The inspector shows landing, record id, open mode, filters, badge, order and visible. The autosave PUT …/app/qrt_app?mode=draft → 200 carries order: 3. The requiresObject cell was not judged, per knownGaps[0]. |
| negative[0] | pass | The hidden app is not over-hidden. |
studio-authoring.authored-page-slots-and-variables · rev 3 · port 41722
| clause | verdict | evidence |
|---|---|---|
| acceptance[0] | pass | Authored blocks render in the highlights, details and discussion slots, and the header and actions keep their defaults. |
| acceptance[1] | pass | All 13 visibility reads match their predicates across the six variable types, including size(page.items) > 0 as a bare string. The run was repeated on a fresh load. The "size is not a function" error #27441 recorded is gone. |
| acceptance[2] | pass | Typing a name and picking a record flip only their own blocks, with 0 navigations. Repeated twice. |
| negative[0] | pass | No slot saved without rendering, and no block ignored its predicate. |
studio-authoring.dashboard-widget-and-filter-types · rev 4 · port 41723
| clause | verdict | evidence |
|---|---|---|
| acceptance[0] | pass | All 20 widget types were authored in Studio and render on 24 multi-bucket rows. Grouping types draw SVG marks; single-value types show a value (gauge 34.5, metric 24, kpi 12600); the table total reads 24 / 12600. No error panel. |
| acceptance[1] | pass | The Add menu's 20 ids equal the Type picker's 20 entries, which equal ChartTypeSchema (20). |
| acceptance[2] | pass | The filter bar renders all five controls: text, select, date (with presets), number and lookup. |
| acceptance[3] | pass | POST /api/v1/analytics/dataset/query, counted per filter change: text → 1 query (table 24 → 10); select Won → 2 (bar, pie); date → 1 (line); number 500 → 1 (metric 3); lookup → 1 (column). The unbound kpi stays at 12600 throughout. |
| acceptance[4] | pass | Global Filters "Add item" holds the save: "Not saved yet: Field on item #6 … needs a value", and no PUT. Filling Field sends one PUT …/dashboard/qdw_board?mode=draft → 200, carrying both the held label edit and the 6th filter. After a reload the dashboard reads the same, _diagnostics.valid:true. No PUT ever carried an incomplete item. |
| negative[0] | pass | Multi-bucket data, and no zero-height frame. |
studio-authoring.summary-field-authoring · rev 4 · port 41724
| clause | verdict | evidence |
|---|---|---|
| acceptance[0] | pass | A Rollup with no configuration sends 0 meta writes. The held line reads "Child object on the field "Child Count" needs a value". The first PUT goes out only once the function is chosen. |
| acceptance[1] | pass | The Sum and Average pickers offer only [Amount (number), Price (currency)]. |
| acceptance[2] | pass | The Studio PUT summaries with count (no field) and sum(amount) → 200; the published meta matches, _diagnostics.valid:true. |
| acceptance[3] | pass | With 5 children (10..50): count 5, sum 150. A PATCH gives 5 / 250. A DELETE gives 4 / 230. |
| negative[0] | pass | No unconfigured roll-up was saved. |
studio-authoring.held-edit-leave-guard · rev 4 · port 41714
| clause | verdict | evidence |
|---|---|---|
| acceptance[0] | pass | A 50-cell matrix. Every in-app departure from a held or dirty pillar raised the confirm "You have unsaved changes. Leave this page anyway?" (data, flow, widget, access, nav). All 14 clean cells raised no dialog. |
| acceptance[1] | pass | A reload fired beforeunload on every held or dirty pillar, including the nav rename that #27464 fixed. |
| acceptance[2] | pass | Stay keeps the edit. Completing it sends an autosave PUT 200 carrying it, on object, flow, dashboard, permission and app. |
| acceptance[3] | pass | Leave leaves _drafts as []. The draft read GET …?state=draft answers 404 before and after. |
| negative[0] | pass | No held edit was dropped without a prompt. |
| negative[1] | pass | No prompt on a clean pillar (14/14). |
integration-system.email-template-render · rev 11 · port 41733
| clause | verdict | evidence |
|---|---|---|
| acceptance[0] | pass | sys_email subject "✅ Task done: Ship the R3 release". The body is substituted, with no {{ residue. |
| acceptance[1] | pass | A missing required title → delivery dead, "MISSING_VARIABLES: title", and no sys_email row. |
| acceptance[2] | pass | "TEMPLATE_INACTIVE: r3_inactive_email". |
| acceptance[3] | pass | The zh-CN member renders "已完成任务:Ship it". Locale zh gets en-US (no folding). A zh-CN-only template sent to zh → TEMPLATE_NOT_FOUND. |
| acceptance[4] | pass | from_address "R3 Ops" (ops@example.test), reply_to set, body_text derived from the HTML. The served item has no bodyText. |
| acceptance[5] | pass | A declared-source edit reloaded without a restart (listener PID unchanged) is served by-name, in the list, and in the send. An admin overlay survives a cold reboot. |
| acceptance[6] | pass | A stray key is refused, naming it. content gets the bodyHtml prescription. A bad name is refused. The save door answers 422 / 400. |
| acceptance[7] | pass | Admin send → 200 sent. Anonymous → 401. Malformed → 400. A member → 403 (manage_platform_settings). All 7 bounds answer 403, and sys_email stays at 9 rows. |
| negative[0] | pass | The #27488 path: after the reload, by-name, list and send all serve "🆕 R3 HOT-RELOADED subject: …", over 2 cycles. |
| negative[1] | pass | No {{ residue was delivered. |
| negative[2] | pass | 400 "at least one of text or html is required", as the item expects. |
studio-authoring.object-api-tab-curl · rev 2 · port 41713
| clause | verdict | evidence |
|---|---|---|
| acceptance[0] | pass | The tab lists the 5 endpoints (list, create, get, patch, delete) on /api/v1/data/qap_item. |
| acceptance[1] | pass | The example body carries every required field, with an id placeholder for the required lookup, and leaves out owner_id and the optional lookup. A field added as a draft joins the body. After publish, the copied create → 201. |
| acceptance[2] | pass | All 5 copied curls carry a Bearer header. Run verbatim (token and id substituted): LIST 200, CREATE 201, GET 200, PATCH 200, DELETE 200, then a re-GET → 404. |
| negative[0] | pass | No invented ids, and no owner_id. |
| negative[1] | pass | A create before publish → 400 INVALID_FIELD, which is the draft not being live yet, as the item notes. |
approvals.account-app-entry · rev 7 · port 41731 (P0)
| clause | verdict | evidence |
|---|---|---|
| acceptance[0] | pass | The built entry index-rNiny7yV.js registers approvals:inbox (1 hit), from the in-repo console:build. |
| acceptance[1] | pass | The nav entry lands on /apps/com.objectstack.account/component/approvals/inbox, with tabs My Pending 1 / Submitted by me / All, and no missing-component state. |
| acceptance[2] | pass | The non-admin persona's GET /api/v1/meta/app returns [showcase_app, account], and GET /api/v1/meta/app/setup → 403. |
| acceptance[3] | fail | Rendering holds: Approve, Reject, Reassign and the other actions show on the request page. Executing does not: Approve → Confirm sends POST /api/v1/actions/sys_approval_request/approval_approve → 404 RECORD_NOT_FOUND, and the request stays at 0/2. Reproduced 3× on fresh loads by the runner, on a second flow with a second persona, and 2× by the independent rule-7 verifier on its own boot. |
| acceptance[4] | pass | A cold load of ?request=AREQ replaces itself with /sys_approval_request/record/AREQ, with record-access-denied count 0. Its read is GET /api/v1/approvals/requests/AREQ → 200. |
| acceptance[5] | pass | The record link keeps the app segment: /_console/apps/com.objectstack.account/showcase_invoice/record/ID, and the data GET → 200. |
| acceptance[6] | pass | In zh-CN: 待我审批 / 我发起的 / 全部, with the badge 待审批. |
| negative[0] | pass | No missing-component state. |
| negative[1] | pass | The account entry is kept, and setup is absent. |
approvals.dynamic-approver-routing · rev 5 · port 41732
| clause | verdict | evidence |
|---|---|---|
| acceptance[0] | pass | The Approve dialog shows "Next Reviewers*", a sys_user record picker that accepts several picks. The node declares {key: next_reviewers, type: user, multiple: true, required: true}. |
| acceptance[1] | pass | An approve with no outputs → 400 VALIDATION_FAILED "decision output(s) next_reviewers are required…", and the request stays pending. |
| acceptance[2] | pass | The decision → 200. The stage-2 co_sign request's pending_approvers equals the picked id. |
| acceptance[3] | pass | First live read. GET /api/v1/automation/showcase_dynamic_approval/runs/RUN (the stage-2 request's flow_run_id) → status: "paused", variables['lead_review.next_reviewers'] = ["Ov8y…"]. A second chain with 2 picks gives 2 approvers, in pick order. |
| acceptance[4] | pass | The stage-2 decision → request approved, run completed (11:03:06Z and 11:04:14Z). |
| negative[0] | pass | An outputless approve is refused at the decision. |
api-backend.formula-stdlib-matrix · rev 6 · port 41741
| clause | verdict | evidence |
|---|---|---|
| acceptance[0] | pass | Harness: MATRIX 82 pass / 0 fail; functions covered 30/30; uncovered [], with @objectstack/formula resolved to packages/formula/dist/index.js. registered-not-documented = [] and documented-not-registered = []. |
| acceptance[1] | pass | Under Asia/Shanghai, today() = 2026-08-31T00:00Z (the UTC control gives 08-30). Live, with the tenant on Pacific/Kiritimati, f_today reads 2026-11-02. |
| acceptance[2] | pass | addMonths(2026-01-31,1) = 2026-02-28. floor(-1.2) = -2 and ceil(-1.2) = -1. joinNonEmpty gives "a-b". |
| acceptance[3] | pass | record.amount/100 = 12.345 (double); 7/2 = 3 (int). |
| acceptance[4] | pass | 4/4 budget_remaining and 2/2 f_formula are correct. A scratch stdlib formula reads ADA-LOVELACE / 20 on create, and the same on GET and list. |
| acceptance[5] | pass | An active PUT with sqrt(4) → 422 INVALID_METADATA expression-invalid, and the object stays 404. publish-drafts answers outcome: "refused" with failedCount: 1. |
| negative[0] | pass | require.cache holds only the dist file. |
| negative[1] | pass | Across the America/New_York fall-back on 2026-11-01, 3 instants all give 2026-11-01T00:00Z. |
| negative[2] | pass | No 2xx for an unregistered function at the active door. |
records-forms.field-history-tracking · rev 10 · port 41734
| clause | verdict | evidence |
|---|---|---|
| acceptance[0] | pass | The sys_activity summary reads "Industry: Technology → Finance" (labels). |
| acceptance[1] | pass | A website-only edit gives "Updated Account "Hooli": Website", with no diff. |
| acceptance[2] | pass | The network log from page load, at 1440, 1000 and 700 px: the account makes exactly 1 GET /api/v1/data/sys_audit_log?…record_id…, and the task makes 0. |
| acceptance[3] | pass | /auth/me/permissions: showcase_account ops include history, and showcase_task ops do not. |
| acceptance[4] | pass | audit-lookup-summary.test.ts + audit-option-label-summary.test.ts: 2 files / 32 passed (unit, the oracle's layer). |
| negative[0] | pass | A mixed PATCH summarises only industry. |
| negative[1] | pass | 0 audit reads on the task. |
| negative[2] | pass | Labels only, no raw option values. |
Reproduction rules for each fail
1. approvals.account-app-entry · acceptance[3] (stock showcase at 6e11a929, Console built in-repo):
- Boot:
OS_PORT=PORT pnpm -C examples/app-showcase exec objectstack dev --ui --seed-admin --no-compile -p PORT -d file:/tmp/x/data.db. - As admin,
POST /api/v1/auth/organization/invite-member{"email":"X","role":"member"}; then XPOST /api/v1/auth/sign-up/email. - As admin,
POST /api/v1/data/sys_user_position{"user_id":X_ID,"position":"contributor","organization_id":ORG}, and the same with"finance". - As X:
POST /api/v1/data/showcase_invoice{"name":"INV-1","account":ACTIVE_ACCOUNT_ID,"owner":"X"};- then
PATCH /api/v1/data/showcase_invoice/ID{"status":"sent","issued_on":"2026-11-01"}. - This opens request AREQ with X pending.
- Sign X in through the form. Open
/_console/apps/com.objectstack.account/component/approvals/inbox, click the row's Request cell, thengetByRole('button',{name:'Approve'})→getByRole('button',{name:'Confirm'}). - Expected:
decision_progress0/2 → 1/2, X leavespending_approvers, and anapproverow appears in/approvals/requests/AREQ/actions. - Actual:
POST /api/v1/actions/sys_approval_request/approval_approve{"recordId":"AREQ","params":{}}→ 404{"code":"RECORD_NOT_FOUND","message":"Record AREQ not found in sys_approval_request"}, and the request is unchanged. - Controls:
- X
POST /api/v1/approvals/requests/AREQ/approve{}→ 200; - the admin on the action door → 200;
- X
GET /api/v1/data/sys_approval_request/AREQ→ 403. approval_reject,approval_commentandapproval_request_info→ 404 as well.
- X
- Mechanism:
packages/runtime/src/domains/actions.tsloads the subject row in the caller's data scope (loadActionSubjectRecord), andrefuseDeniedSubjectLoadanswers 404 before thetype:'api'forward.
2. studio-authoring.expression-editors · acceptance[2], [4], [6], negative[0]:
- As admin,
POST /api/v1/packages{"manifest":{"id":"com.qa.rone","name":"QA R1 Expr","version":"0.1.0","type":"app","namespace":"qee"}}. PUT /api/v1/meta/object/qee_job?mode=draft&package=com.qa.rone, with numberest_hoursand formulaf_formula{"type":"formula","expression":"record.est_hours * 1.1","returnType":"number"}.PUT /api/v1/meta/permission/qee_job_editor?mode=draft&package=com.qa.rone, withobjects.qee_job.allowReadandrowLevelSecurity: [{"name":"qee_probe_policy","object":"qee_job","operation":"select","using":"record.status == 'open'"}].POST /api/v1/packages/com.qa.rone/publish-drafts.- Sign in through the form. Open
/_console/studio/com.qa.rone/access?surface=permission:qee_job_editor, click[data-testid=perm-tab-rls], and typerecord.f_formula > 5into USING. - Expected: an error-tier finding, and the save held with no PUT.
- Actual:
- at about 1.1 s the field reads "Valid CEL", with no
perm-autosave-held; - at about 2.1 s,
PUT …/permission/qee_job_editor?mode=draft…→ 200 carrying"using":"record.f_formula > 5"; - at about 2.6 s the field turns aria-invalid: "RLS using (INVALID_FIELD / 400): … 'f_formula', a virtual formula field".
- Typing
record.f_formula > 9repeats the cycle. POST …/permission/qee_job_editor/publish→ 422rls-predicate-unenforceable.
- at about 1.1 s the field reads "Valid CEL", with no
- Mechanism:
ui/…/metadata-admin/celAuthoring.tsrlsPredicateVerdictjudges a one-clause stack that names no object, so the virtual-field branch cannot fire client-side.draftVerdict.tsserverHoldAtholds only after the server has judged that text. - This is the residual that PR fix(app-shell): a server error at an RLS clause's path holds the permission-set autosave #27612 documents as by design; parent Studio: an expression the engine refuses (RLS USING on a formula field, a null-unsafe validation predicate) keeps autosaving — the server verdict is shown on the field but never holds the save #27482 is still open.
Disposition of each fail (SKILL §5)
approvals.account-app-entry· acceptance[3]: product defect. It is a new door, not Approvals: a non-admin approver who opens an approval request from the Approvals inbox sees "You don't have access to Approval Request records" instead of the decision panel (regression from #27713) #28546: extracted as its own card, listed in the close-out comment.studio-authoring.expression-editors· acceptance[2], [4], [6], negative[0]: product defect, carried by the open card Studio: an expression the engine refuses (RLS USING on a formula field, a null-unsafe validation predicate) keeps autosaving — the server verdict is shown on the field but never holds the save #27482. That was a dedupe hit, so it is not re-extracted. Studio: an expression the engine refuses (RLS USING on a formula field, a null-unsafe validation predicate) keeps autosaving — the server verdict is shown on the field but never holds the save #27482 should not close on Studio: the Validations tab holds the autosave on a served CEL verdict at the rule's own path, as the RLS arm does (#27482 layer 2 of 2) #27644 alone. Its owner decides between:- an editor fix (the editor judges the formula-field probe with the object in scope, so the first save is held), or
- a maintainer ruling that the once-per-new-text save is accepted. In that case acceptance[4]'s "no PUT issued" is reworded, and that is a ledger edit parked on the anchor.
Pins run (at 6e11a929, --maxWorkers=1, one package at a time)
- ui (jsdom), corroboration only except where noted:
- metadata-admin CEL / hold suites: 8 files, 98 passed.
celAuthoring.test.ts40/40 scores expression-editors acceptance[7]. - OWD panel: 3 files, 18 passed.
ObjectApiPanel.exampleBody-11803: 13/13.- Studio leave guards: 6 files, 30 passed.
- metadata-admin CEL / hold suites: 8 files, 98 passed.
- formula stdlib:
packages/formulaoption-label + cel-stdlib-drift: 12 passed (unit).packages/metadata-protocolprotocol.runtime-authoring-gate.test.ts: 222/222. Protocol over a stub engine, which is above acceptance[5]'s HTTP oracle, so acceptance[5] was scored live.packages/lintruntime-gate.object-formula-writes: 11 passed.packages/objectqlengine-formula-fault-log: 6 passed (engine layer).
- email:
packages/qa/dogfoodemail-template-*.dogfood.test.ts: 3 files, 11 passed (engine + REST).packages/restemail-send-door-bounds.test.ts: 36 passed (HTTP layer; pins acceptance[7]'s 501 leg).packages/plugins/plugin-emailsend-template.test.ts: 20 passed (unit, below the api oracle, so driven live).
- field history:
plugin-auditlookup + option-label summary: 32 passed.packages/qa/dogfoodfield-history-tracking-activity: 3 passed.ui/e2e/live/record-history-display.spec.tswas scored by hand, not run. It needs the vite console on :5180.
Off-clause probes (RUNNER rule 8)
About 50 minutes across the 13 items, on the same boots and personas.
- Doors probed:
- the RLS / CEL editors, the OWD panel and suggested-bindings doors;
- the app nav and page-variable save doors, and the dashboard held-item doors;
- the summary save and publish doors, and child writes after publish;
- the cURL bodies over every field type;
- the approvals decision and action doors, and the dynamic-approval outputs;
- the email send door bounds, and template reads as a member;
- the audit and activity reads as non-admins;
- formula stdlib edge cases, with the live authoring door and tenant-timezone switching.
- Boundary classes: empty string, unknown or missing id, unknown field or object, wrong type, duplicates, draft against publish, bare against envelope predicates,
selectagainst write policies, admin against member against anonymous, and DST and extreme timezones. - Findings: eight product defects, each extracted as its own card (listed in the close-out comment).
- Probe time per item fell short of the 10–15 min budget for several items. The per-item minutes are in the runner notes; this is stated plainly, not padded.
Fixture gaps and partial coverage
studio-authoring.app-and-nav-propertiesacceptance[5]: the requiresObject cell has no Studio control (knownGaps[0]), so it was not judged. The other cells pass.studio-authoring.expression-editors: only the Studio package door was observable, not the environment door's disabled Save, as the clause allows on stock showcase.records-forms.field-history-trackingacceptance[4]: unit only, because the showcase has no tracked reference field.records-forms.field-history-tracking:record-history-display.spec.tsneeds a vite console; it was hand-mirrored here.integration-system.email-template-renderknownGaps[0]: no stock flow invokessendTemplate, so a scratchtype:'api'flow was used.
Checklist-accuracy findings are parked on the anchor #27377, not here.
Time
- Environment prep:
- 11 min wall clock (cold install, build, console build, compile boot: 10:37–10:48Z);
- about 35 runner-minutes of per-item boots and fixture authoring.
- Testing, probes included:
- 80 min wall clock (10:48–12:08Z, four runners in parallel);
- about 155 agent-minutes, plus 6 minutes for the rule-7 verifier.
- Ratio: about 1 : 7.3 by wall clock, and 1 : 3.4 by agent-minutes.
Generated by Claude Code
Activity
objectstack-fleet commentedon Nov 1, 2026
domain:devx#1 · sunhaolin · session_017xRBcVy4VXmkQjn2MGeaWk
Filed by the PM seat for the W3-RT runner
session_017xRBcRX4wwwVw5bEckffdp, whose own write was refused by its session permissions (RUNNER rule 2: staged text, posted verbatim through the same door).Close-out · 2026-11-01T12:23Z
Every product defect from this run is extracted below. Each card carries
QA-source: #28656 · area.item · clause, has noqa-runand nopm:*label, and none is dispatched.Disposition of each
fail(four-way):approvals.account-app-entry· acceptance[3] → product defect, extracted as approvals: a non-admin approver's decision from the request page answers 404 RECORD_NOT_FOUND — the action door loads sys_approval_request in the caller's data scope #28657. It is a new door (the action door's subject load), not Approvals: a non-admin approver who opens an approval request from the Approvals inbox sees "You don't have access to Approval Request records" instead of the decision panel (regression from #27713) #28546. Approvals: a non-admin approver who opens an approval request from the Approvals inbox sees "You don't have access to Approval Request records" instead of the decision panel (regression from #27713) #28546's fix holds.studio-authoring.expression-editors· acceptance[2], acceptance[4], acceptance[6], negative[0] → product defect, carried by the open card Studio: an expression the engine refuses (RLS USING on a formula field, a null-unsafe validation predicate) keeps autosaving — the server verdict is shown on the field but never holds the save #27482. That was a dedupe hit, so it is not re-extracted. The residual that still fails is the first autosave of each newly refused RLS text (the formula-field USING probe reads "Valid CEL" and saves with 200). Studio: an expression the engine refuses (RLS USING on a formula field, a null-unsafe validation predicate) keeps autosaving — the server verdict is shown on the field but never holds the save #27482 should not close on Studio: the Validations tab holds the autosave on a served CEL verdict at the rule's own path, as the RLS arm does (#27482 layer 2 of 2) #27644 alone. Its owner chooses between an editor fix and a maintainer ruling that accepts the residual. A ruling would make the clause a stale-spec re-point, which is parked on the anchor.No
failis left without a disposition.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:devx#1 · sunhaolin · session_017xRBcVy4VXmkQjn2MGeaWk
Record closed · 2026-11-02T17:33Z
Close-out is complete for the W3-RT retest at
6e11a929: 6557900711 disposes both FAILs.The 18 checklist-accuracy lines landed through S10 #28701.
Generated by Claude Code