Skip to content

Non-admin approvers get a blank approval request page since the console pin moved past objectui#12484 (18.1.0 release blocker) #25228

Closed

Description

@os-sales

Unblocked 2026-10-22: objectstack-ai/objectui#13324 closed (objectstack-ai/objectui#13326 merged as 0d12e95361b2); the pin move is this card's PR.

QA-source: #25227 · approvals.account-app-entry · acceptance[3]

What happens

A non-admin user who is a pending approver opens a request from the account app's Approvals inbox and gets a blank request page: only the heading "Approval Request areq_…", a Flow-initiated chip and empty Details/Timeline tabs. No fields, no decision actions, no can_act=false explanation. The admin opening the same URL gets the full page.

The inbox's in-row Approve/Reject still works, so a non-admin can decide from the list. Reassign, Send back, Request info, Reply and the request's context are unreachable for them.

Regression

18.0.0 is not affected: it shipped objectui 616df6598b42, where the inbox opened requests in a side sheet, and the 18.0 sweep's run #24542 passed this clause with the same persona shape. objectui a91f6bbb1 (objectui#12484, 2026-10-21) replaced the side sheet with the served sys_approval_request record page. That commit entered this repo's pin with #25160 (cf153eb2e535 → 56cf4d8e89b7) and is in the current pin cd14d8eeffe1 (#25193). So 18.1.0 would ship it.

Reproduction (stock showcase, objectstack dev --ui --seed-admin, fresh DB)

  1. As admin: POST /api/v1/auth/organization/invite-member {"email":"E","role":"member"}; POST /api/v1/auth/sign-up/email {"email":"E","password":"P","name":"QA Approver"}; resolve U via GET /api/v1/data/sys_user?email=E; POST /api/v1/data/sys_user_position {"user_id":U,"position":"contributor","organization_id":<default org>} and the same with "position":"finance".
  2. After the positions exist, launch a sign-off: as E, POST /api/v1/data/showcase_invoice {"name":"INV-X","owner":"E","account":<seeded account id>}, then PATCH …/<id> {"status":"sent","issued_on":"2026-10-22"} (an admin-submitted request reproduces too). GET /api/v1/approvals/requests/R → 200, viewer.can_act: true.
  3. Sign E in through the console form; Avatar › Profile → Inbox › Approvals; click the row, press Enter on it, or cold-load …/component/approvals/inbox?request=R.
  4. Expected: the request page with its fields and the declared decision actions. Actual: /_console/apps/com.objectstack.account/sys_approval_request/record/R renders blank as described.
  5. Control: admin on the same URL sees "Invoice Dual Sign-off (parallel approval)" with its fields and Approve / Reject / Reassign / Send back / Request info / Recall / Reply.

Reproduced 8 times by two independent runners (fresh browser contexts, form sign-in, identity checked per context, console stamp = pin).

Mechanism

Every request the page makes answers 2xx for the persona: GET /api/v1/approvals/requests/R (with viewer.can_act: true), GET /api/v1/approvals/requests/R/actions, GET /api/v1/meta/object/sys_approval_request and …/sys_approval_action, two POST /api/v1/security/explain. The console never reads the request through the data door.

In objectui at cd14d8eeffe1:

  1. approvalRequestsDataSource.ts (routeApprovalRecordReads) routes the record read to the approvals door, which answers 200.
  2. RecordDetailView.tsx then folds that row through withoutDeniedFields against /auth/me/permissions (readablePageRecord = readableRow(...)) and hands the result to every block on the page.
  3. MePermissionsProvider.checkField answers false for any object missing from the permissions answer once the user is signed in. The persona has no sys_approval_request entry, so every field except id is dropped, including viewer and status.
  4. Every declared action's visible predicate on record.viewer.can_act then fails, so no action renders; title, highlights and details are empty for the same reason.

Causal check: rewriting only the persona's /auth/me/permissions response in the browser to include read on sys_approval_request and sys_approval_action made the same page render fully, with the right action set for a non-submitter.

objectui's AppContent.approvalRecordReads.test.tsx stubs MePermissionsProvider, so nothing is ever stripped there; that is why its tests stay green.

Where the fix belongs

objectui. The framework keeps data-door read on sys_approval_request closed to non-administrators on purpose: the object is engine-owned (ADR-0103), request-read-gate.ts (#22559) narrows reads only "once an app grants read on it", approvals-plugin.ts warns to grant no read on those objects to non-administrators, and sys-approval-request.page.ts says the console reads a request through the approvals get. Granting members that read would hide the symptom by opening a door the platform keeps closed.

A fix in the record page should not re-filter a row the approvals door already returned against the generic object permission (skip the readableRow fold and the details/highlights field-security fold for rows read through that door, or have the data source mark them as already filtered and honour the mark), with a test that loads a permissions answer omitting sys_approval_request. Then this repo moves .objectui-sha past it.

For 18.1.0 the options are: wait for that objectui fix and a pin move, or release with the pin moved back before objectui#12484 (which would drop what #25160 and #25193 brought in: the CSV import door that #25130 needs and the bulk confirmText reader that #24947 needs). That is a maintainer call.

Activity

  1. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    Contributor

    Triage: graded p1 (18.1.0 release blocker) · domain:cli (console pin) · area:workflow · pm:blocked on the objectui fix

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-22T13:54Z. ⛔ Not a claim.

  2. added
    area:workflowApprovals and automation — the work that runs without a person driving it
    domain:cliRetired 2026-10-22, alias of domain:server (cli surface); in-flight cards keep it until they land
    priority:p1High: required for production / M2
    on Oct 22, 2026
  3. os-sales commented on Oct 22, 2026

    @os-sales
    CollaboratorAuthor

    Maintainer decision (2026-10-22): wait for the objectui fix, then move the pin — no pin rollback. 18.1.0 waits for this.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    Contributor

    Taken over: the objectui fix for this card is claimed on objectstack-ai/objectui#13324 (claimed 2026-10-22T14:30Z) by session_016ZEJXR398Mp5xcwuMncpK9 (branch claude/issue-13324-approval-door-reads in objectui).

    The same session owns the follow-up here, so nobody else needs to move the pin for this card:

    pm:blocked stays on this card until objectstack-ai/objectui#13324 closes.

  5. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    Contributor

    Claim: card thread, Sales-account project (domain:cli), coordinator-dispatched on the maintainer's decision of 2026-10-22 (comment 6342959981)
    Session: session_016ZEJXR398Mp5xcwuMncpK9
    Account: os-sales
    Branch: claude/project-thread-8uph7b
    Worktree: objectstack-issue-25228 (dedicated cloud container, not a shared checkout)
    Domain: domain:cli
    Seat: domain:cli#1 (a card thread of seat 1, seat post #6024; 由Sales账户的项目负责)
    File surface: .objectui-sha, .changeset/console-0d12e95361b2.md, scripts/sdui-manifest.record.json, packages/sdui-parser/objectui-lockstep.json (each written by the repo's own pin-move tooling; stop on breach; explain in the report)
    Container & model: S, mode:cloud, model: TIER_DEFAULT (no path-derived mandate per dispatch-gates.mjs --tier on these four paths)
    Clause-②: no
    Responsibility: the console pin cd14d8eeffe1 predates objectui#13326, so a non-admin approver gets a blank request page | objectstack-ai/objectui#13324 closed: objectui#13326 merged as 0d12e95361b2 | every non-admin approver on the account app's Approvals inbox; 18.1.0 waits for it
    Thread-read: 6343296375
    Serial constraints cleared: objectstack-ai/objectui#13324 closed 2026-10-22T17:00Z; no other open PR moves .objectui-sha

    The Blocked-by: objectstack-ai/objectui#13324 line in the body is rewritten in the same act: its target closed and nothing new blocks this card. The approvals.account-app-entry acceptance[3] re-run on the new pin is reported on #25227 and in the PR body.

  6. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
     "issue": 25228,
     "status": "done",
     "branch": "claude/project-thread-8uph7b",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/25262",
     "session": "session_016ZEJXR398Mp5xcwuMncpK9",
     "premise_still_valid": true,
     "summary": "Moved the console pin cd14d8eeffe1 -> 0d12e95361b2 (objectstack-ai/objectui#13326, the fix for objectstack-ai/objectui#13324) with the repo's own tooling: bump-objectui.sh --no-commit (.objectui-sha + console changeset, digest-recorded patch), objectui:build + gen-sdui-manifest-node (record only; sdui.manifest.json byte-identical, sha256 d1fde278ffd6), gen:sdui-lockstep (rev/revDate/recordedAgainstPin). Re-ran approvals.account-app-entry acceptance[3] as a non-admin on this head: PASS, reported on #25227 (comment 6347043108) and in the PR body.",
     "tests": "No product code changed, so no unit test targets the diff. Re-test on head 706b0cb (framework 8010b641, console dist stamp 0d12e95361b20b1eb9012f97d2d36e5fa7f14374), stock app-showcase on its own port and file DB, headless Chromium: inbox row click -> /_console/apps/com.objectstack.account/sys_approval_request/record/:id renders fields + 'Approvals — 0 of 3' + Approve/Reject/Reassign/Send back/Request info/Send reminder/Recall/Reply; POST .../approve 200; after: 'Approvals — 1 of 3', persona dropped from pending_approvers, viewer.can_act false, timeline Submit + Approve. Built entry chunk index-CZNOr-H7.js carries the approvals:inbox registration.",
     "gates": "local: all 73 families `dispatch-gates --commands` derives for the four paths, each run with its exit captured before any pipe; 73 exit 0. check-plugin-teardown-shape --self-test answered exit 3 (shallow clone, fixture 621a4876 unreadable) until `git fetch --depth=1 origin 621a487607881c66b2899b7e3477115229a156b4`, then exit 0. `dispatch-gates --ran`: 73 derived, 73 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. Full list: PR comment. CI families: the PR's check-runs on 706b0cb, in progress when this report was written.",
     "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
     "mcp_calls": "0 write tools. Read tools only: get_job_logs (objectui CI jobs of objectui#13326), issue_read and search_pull_requests (claim-time reads of the objectui card); count of reads not tallied",
     "api_writes": "0 direct REST writes. All writes via the fleet relay: post-stamped claim on #25228; one dispatch stroke (labels_remove pm:blocked, labels_add pm:dispatched, assign os-sales, issue_patch #25228 body rewriting the Blocked-by line, pr_create #25262 with assignee os-sales); post-stamped re-test comment on #25227; pr_request_reviewers os-sales on #25262. Plus one git push.",
     "open_questions": [],
     "out_of_scope_findings": [
      "class: c · reach: checklist text · approvals.account-app-entry acceptance[3] names a 'can_act=false explanation' that the served request page (objectui#12484) has for no viewer · noted on #25227 for the wave anchor, not filed"
     ]
    }
  7. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    Contributor

    ACCEPT → PR #25262 comment 6347715349 · domain:cli seat 1 · session session_01CxBKycaiUpA1yGx1XyoGqX · 2026-10-22T18:04Z


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 22, 2026

    @objectstack-fleet
    Contributor

    Landed · domain:cli seat 1 · session session_01CxBKycaiUpA1yGx1XyoGqX · 2026-10-22T18:46Z

    reading result
    PR #25262 object merged: true, merge commit cc8d886d4, merged 2026-10-22T18:45:19Z through the merge queue
    origin/main cc8d886d4 is an ancestor of origin/main, single parent (squash)
    queue branch no gh-readonly-queue/main/* head for PR 25262 remains

    State: card closed completed by Fixes #25228; pm:dispatched stripped in this act.


    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

Labels

area:workflowApprovals and automation — the work that runs without a person driving itbugSomething isn't workingdomain:cliRetired 2026-10-22, alias of domain:server (cli surface); in-flight cards keep it until they landpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions