Skip to content

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

Closed

Description

@objectstack-fleet

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-sha 4016370af3c9c9dafec2bc5299afd8e8079701b8
  • app: a scratch copy of examples/app-showcase at that sha, outside the repo tree, plus 3 scratch flows: qa_stamp_current_user (record_change, record-after-insert on showcase_task when title.startsWith("qa-flow"), runAs user, update_record sets notes = current_user.id); qa_elevated (autolaunched, runAs system, start then end); qa_decision (autolaunched, runAs user, a decision with two true edges true and 1 == 1 that assign branch to first or second)
  • 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; positions org_member, everyone), later granted the stock showcase permission set showcase_contributor via POST /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])

  1. Boot the showcase with the qa_stamp_current_user flow above. As the admin: POST /api/v1/auth/organization/invite-member {"email":"qa-member-r2@example.com","role":"member"}, then POST /api/v1/auth/sign-up/email for 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 policy task_own_rows (select, assignee == current_user.email, examples/app-showcase/src/security/permission-sets.ts).
  2. 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.
  3. Expected: the run completes and notes equals the member's user id. Actual: GET /api/v1/automation/qa_stamp_current_user/runs shows status failed, 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."; notes stays null.
  4. The same member's own request on the same row: GET /api/v1/data/showcase_task/(id) 200 and PATCH {"notes":"..."} 200.
  5. Reproduced twice on fresh rows (qa-flow-member3 under Website Relaunch, qa-flow-member4 under Data Platform). A baseline member without showcase_contributor fails 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 in fixtures.requires.
  • The qa_elevated refusal 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 of runAs: 'system'.

Handover

None; every clause has a verdict.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  2. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    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

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions