Repository navigation
QA run · automation.flow-run-semantics-v18 (1/1) · ba4afcc2 · 2026-10-19 · 0 PASS / 0 PARTIAL / 1 FAIL / 0 BLOCKED / 0 NOT-RUN #24606
Description
Second-round 18.0 release-sweep re-test (maintainer-approved re-test on the release candidate; round 1 at 5b14b7f3, anchor #24579). This item was re-driven because #24518 (04669a4) changed runAs: 'user' identity seeding after round 1.
Environment fingerprint
- framework
ba4afcc2391922a86678bf5e47b1a0e4d2c87ff1;.objectui-sha4016370af3c9c9dafec2bc5299afd8e8079701b8 - app: a scratch copy of
examples/app-showcaseat that sha, outside the repo tree, plus 3 scratch flows:qa_stamp_current_user(record_change, record-after-insert onshowcase_taskwhentitle.startsWith("qa-flow"), runAs user,update_recordsetsnotes = current_user.id);qa_elevated(autolaunched, runAs system, start then end);qa_decision(autolaunched, runAs user, a decision with two true edgestrueand1 == 1that assignbranchtofirstorsecond) - boot:
OS_PORT=47411 OS_LOG_LEVEL=info objectstack dev --ui --seed-admin -p 47411 -d file:(scratch)/data.db, 2026-10-19 14:27Z - personas: the seeded admin;
qa-member-r2(invite-member then sign-up; positionsorg_member,everyone), later granted the stock showcase permission setshowcase_contributorviaPOST /api/v1/data/sys_user_permission_set - sequential, one runner
Scope
- selector
automation.flow-run-semantics-v18, revision 1. Judged 1 of 1.
Per-clause verdicts
| clause | verdict | oracle evidence (text) |
|---|---|---|
| acceptance[0] pins pass | pass | vitest at ba4afcc: service-automation flow-cel-current-user.test.ts 14/14, flow-cel-record-binding.test.ts 17/17, builtin/decision-overlapping-edge-conditions.pin.test.ts 22/22; runtime action-flow-elevated-door.test.ts 35/35, endpoint-flow-elevated-door.test.ts 17/17 |
| acceptance[1] record-triggered run stamps the member's id | fail | member holding showcase_contributor: the run fails at the stamp node, "You do not have access to this record", while the member's own request reads and updates the same row (reproduction below); positive control: the admin's run stamps the admin id |
| acceptance[2] member start of an elevated flow is refused | pass | member POST /api/v1/automation/qa_elevated/trigger 403 PERMISSION_DENIED "This caller may not start this flow..."; GET .../qa_elevated/runs empty before and after. Controls: the same member starts qa_decision (runAs user) 200; the admin's start of qa_elevated is also 403 |
| acceptance[3] decision takes the first true edge | pass | POST /api/v1/automation/qa_decision/trigger gives output.branch = "first"; summary: node b skipped, gate edge e3 skipped 1 |
| negatives[0] | holds | no non-system caller ran the system flow |
Derived item verdict: FAIL (1 product defect). Round 1 (#24545, #24558) scored acceptance[1] pass with a plain member on a scratch object that has no email-keyed row policy, so that run never exercised this path. This is a coverage difference, not a regression: git diff 5b14b7f3 ba4afcc2 -- packages/services/service-automation packages/core changes no production file.
Reproduction (acceptance[1])
- Boot the showcase with the
qa_stamp_current_userflow above. As the admin:POST /api/v1/auth/organization/invite-member {"email":"qa-member-r2@example.com","role":"member"}, thenPOST /api/v1/auth/sign-up/emailfor that address. Grant the member the stock set:POST /api/v1/data/sys_user_permission_set {"user_id": member id, "permission_set": "showcase_contributor"}. That set carries the row policytask_own_rows(select,assignee == current_user.email,examples/app-showcase/src/security/permission-sets.ts). - As the member:
POST /api/v1/data/showcase_task {"title":"qa-flow-member3","project":"(Website Relaunch id)","assignee":"qa-member-r2@example.com"}answers 201. - Expected: the run completes and
notesequals the member's user id. Actual:GET /api/v1/automation/qa_stamp_current_user/runsshows statusfailed,trigger.userId= the member's id, and the stamp node error "update_record(showcase_task) failed: You do not have access to this record. Contact the person who owns it, or your administrator, if you need access.";notesstays null. - The same member's own request on the same row:
GET /api/v1/data/showcase_task/(id)200 andPATCH {"notes":"..."}200. - Reproduced twice on fresh rows (
qa-flow-member3under Website Relaunch,qa-flow-member4under Data Platform). A baseline member withoutshowcase_contributorfails with "You do not have permission"; the member's own PATCH is also 403 there, which is consistent and not this defect.
Mechanism (code): resolveRunDataContext (packages/services/service-automation/src/runtime-identity.ts) builds the runAs user data context as {isSystem:false, userId, positions, permissions, org_user_ids?, tenantId?, flowRunId?}, with no email. The plugin's grants bridge receives grants.email from resolveUserAuthzGrants and drops it. The request path seeds ctx.email (packages/core/src/security/resolve-authz-context.ts), and the RLS compiler binds current_user.email only from the execution context's email (packages/plugins/plugin-security/src/rls-compiler.ts). So inside the run, assignee == current_user.email cannot match a row the same user can see. An independent rule-7 verifier re-derived this from the evidence and the code, and confirmed it: product defect, pre-existing, not a regression.
Checklist-accuracy findings / fixture gaps
- The item's fixture does not say which persona and row policy the record-triggered clause must run under. A member whose only access to the row comes through an email-keyed policy (the stock
showcase_contributor) is the case that separates run identity from request identity; a plain member on a policy-free scratch object passes vacuously. Suggest naming the contributor persona infixtures.requires. - The
qa_elevatedrefusal message is the generic "may not start this flow" sentence, and the admin is refused too, so the clause's oracle (403 plus no run) holds. The message does not say the refusal is because ofrunAs: 'system'.
Handover
None; every clause has a verdict.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 19, 2026
Extracted: #24613 · automation.flow-run-semantics-v18 · acceptance[1]
Parked-on: #24579 · this record's checklist-accuracy findings (the contributor persona as the fixture for acceptance[1]; the generic elevated-start refusal sentence), handed to the dispatching session for the anchor
Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Closure complete (checklist-test §5): every FAIL in this record has a disposition (the
Extracted:/Parked-on:lines above), and its partial coverage gaps and not-run handover are parked on #24579.Closed by: the 18.0 QA wave dispatch session
session_01CfRDy7SuTfUqaTu2AEH2Hi, on the maintainer's instruction there on 2026-10-19, verbatim 「qa-run的卡片处理完的是不是可以关闭」.Generated by Claude Code