Skip to content

feat(approvals): server-computed viewer capability for precise decision-action gating (#3310)#3344

Merged
os-zhuang merged 1 commit into
mainfrom
claude/enterprise-delegation-ooo-proxy-1v3m8l
Jul 20, 2026
Merged

feat(approvals): server-computed viewer capability for precise decision-action gating (#3310)#3344
os-zhuang merged 1 commit into
mainfrom
claude/enterprise-delegation-ooo-proxy-1v3m8l

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes #3310. Pairs with objectui#… (the console contract sync + inbox hint alignment).

Problem

After the inbox retired its hand-wired decision buttons for DeclaredActionsBar (objectui#2697/#2710), the approver actions on sys_approval_request gated only on record.status == "pending". So a submitter viewing their own pending request saw Approve / Reject / Reassign / Send-back / Request-info — buttons that just 403'd — and the old client-side identity heuristic could wrongly hide a position/team-addressed approver.

What

getRequest / listRequests now attach a per-viewer block, computed from the caller's context:

ApprovalRequestRow.viewer?: { can_act: boolean; is_submitter: boolean }
  • can_act — the caller is a current pending approver (their user id is in the resolved pending_approvers while status is pending). This is the same check the decision methods authorize with — and because expandApprovers resolves position/team/manager specs to concrete user ids at open time, a plain membership test is faithful (strictly better than a client identity guess).
  • is_submitter — the caller submitted the request.

The declared actions now gate on it:

actions visible
approve / reject / reassign / send-back / request-info record.viewer.can_act
remind record.status == "pending" && record.viewer.is_submitter
recall (pending || returned) && record.viewer.is_submitter
resubmit record.status == "returned" && record.viewer.is_submitter

Where viewer is absent (a row surfaced outside a service read with a user context — e.g. a raw data-API grid), the record.viewer.* predicate fails closed.

Cheap + synchronous (attachViewers), safe on list reads. Mirrors the attachDecisionProgress (#3266) enrichment pattern.

Verification

  • plugin-approvals suite green (147 tests): new service tests — getRequest returns can_act:true for a pending approver, false for the submitter and strangers, and false once finalized; listRequests attaches viewer to every row. Object contract test pins the new predicates.
  • Browser (paired objectui change, live backend): a submitter viewing their own pending request now sees only remind + recall — approve/reject/reassign/send-back/request-info are all hidden; getRequest returned viewer: {can_act:false, is_submitter:true}.

Refs

#3310 · objectui#2697 / #2710 (button retirement) · #3266 (decision_progress, the enrichment pattern mirrored) · framework#3300 (declared action set)

🤖 Generated with Claude Code


Generated by Claude Code

…viewer capability

`getRequest` / `listRequests` now attach a per-viewer block to each row from the
caller's context — `viewer: { can_act, is_submitter }` (ApprovalRequestRow):

  - can_act    — the caller is a current pending approver (their user id is in
                 the resolved pending_approvers while status is pending). This is
                 the exact check the decision methods authorize with, so it
                 already reflects position/team/manager resolution.
  - is_submitter — the caller submitted the request.

The declared decision actions on sys_approval_request now gate their `visible`
CEL on it: approver actions (approve/reject/reassign/send-back/request-info) use
`record.viewer.can_act`; submitter levers (remind/recall/resubmit) use
`record.viewer.is_submitter`. Before this, approver actions only trimmed the
non-pending case, so a submitter viewing their own pending request saw buttons
they couldn't use (the backend 403'd), and a position-addressed approver could
be wrongly hidden by a client-side identity heuristic. Where `viewer` is absent
(a row surfaced outside a service read with a user context), the predicate fails
closed.

Tests: getRequest returns can_act true for a pending approver, false for the
submitter and strangers, and false once finalized; listRequests attaches viewer
to every row; the object contract test pins the new predicates.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ypkQikZ55oWUHUnMebwXA
@vercel

vercel Bot commented Jul 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 20, 2026 3:31am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/m labels Jul 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/plugin-approvals, @objectstack/spec.

104 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/plugin-approvals, packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/cli.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-approvals, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-approvals, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/plugin-approvals, @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang marked this pull request as ready for review July 20, 2026 03:36
@os-zhuang
os-zhuang merged commit 8efa395 into main Jul 20, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/enterprise-delegation-ooo-proxy-1v3m8l branch July 20, 2026 03:36
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Jul 20, 2026
…d + Console)

Continues the v16 page from the latest front/back-end code. Since the rc.0 cut,
9 backend changesets landed and the objectui pin advanced 94d4876 -> af1b0db
(objectui objectstack-ai#2706-objectstack-ai#2736). Sourced from each repo's own changesets.

- Retitles the now-stale 'pending the next console pin' subsection: those 4
  frontend items (objectui#2701/objectstack-ai#2706/objectstack-ai#2707/objectstack-ai#2708) are now bundled as the pin
  advanced; points to the new running section.
- Adds a 'Landed since 16.0.0-rc.0' section:
  - Breaking/behavior: aiStudio/aiSeat alias removal (objectstack-ai#3308); date-arithmetic
    now a build-time error (objectstack-ai#3306); checkboxes per-option visibleWhen + string
    option matching (objectstack-ai#3350).
  - New backend: formula null-guard/floor/ceil (objectstack-ai#3306); engine-owned managedBy
    bucket (ADR-0103 addendum, objectstack-ai#3343); approvals viewer gating (objectstack-ai#3344) +
    decision attachments (objectstack-ai#3332); discovery transactionalBatch bit (objectstack-ai#3298);
    action resultDialog i18n (objectstack-ai#3347); collab-notification/storage localization
    + wired notifications REST routes (objectstack-ai#3354).
  - Console (now bundled): option-widget visibleWhen parity, approvals-inbox
    server-viewer gating + composer retirement, related-list pagination,
    inline-analytics renderer retirement, authenticated api-fetch threading,
    result-dialog i18n, engine-owned bucket UI, write-failure surfacing.
- Adds aiStudio/aiSeat and date-arithmetic to the upgrade checklist; updates
  the Console-section intro for the advanced pin.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AHVoqyH9VAjmbyMN1Ri2jf
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants