Skip to content

feat(approvals): server-computed decision progress + notification deep links (#3266 / objectui#2678 P1.5)#3274

Merged
os-zhuang merged 2 commits into
mainfrom
claude/enterprise-delegation-ooo-proxy-1v3m8l
Jul 19, 2026
Merged

feat(approvals): server-computed decision progress + notification deep links (#3266 / objectui#2678 P1.5)#3274
os-zhuang merged 2 commits into
mainfrom
claude/enterprise-delegation-ooo-proxy-1v3m8l

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Backend half of objectui#2678 P1.5 (the two highest-ROI UX items from the post-P1 assessment). UI counterpart rides objectui#2681. Both are SDUI-shaped: the server computes, any client renders.

Changes

1. decision_progress on single-request reads
getRequest attaches a server-computed aggregation progress for PENDING multi-approver requests (unanimous / quorum / per_group):

  • unanimous/quorum → { behavior, got, need } (quorum need = the clamped threshold);
  • per_group → satisfied-groups got/need plus per-group {group, got, need, satisfied} detail, tallied against the open-time approver-group snapshot (OOO-substituted members count for their group).
    Single-read enrichment only (same pattern as attachFlowSteps), display-only — decideNode's finalization tally stays authoritative. Typed on the ApprovalRequestRow contract. This means "2 of 3 approved" / per-group ticks render identically in the console drawer, a future mobile client, or anything else — and a future weighted behavior needs zero client changes.

2. Notification deep links
notify() centrally rewrites the bare /system/approvals actionUrl to /system/approvals?request=<id> from the notification's source, so all twelve call sites — and any future one — land the recipient on the exact request. The console inbox consumes the param and auto-opens the drawer (objectui#2681).

Tests

4 new: per-group progress updates per approval; quorum reports against the clamped threshold; first_response carries no progress; the deep-link rewrite. 136 green in plugin-approvals.

Refs

🤖 Generated with Claude Code


Generated by Claude Code

…p links (#3266, objectui#2678 P1.5)

UX follow-ups on quorum/per_group, both SDUI-shaped (server computes, any
client renders):

- getRequest now attaches `decision_progress` for PENDING multi-approver
  requests: unanimous/quorum report approvals got/need (quorum against the
  clamped threshold); per_group reports satisfied-groups got/need plus a
  per-group {group, got, need, satisfied} breakdown computed from the
  open-time approver-group snapshot. Single-read enrichment only, display-only
  (decideNode's tally stays authoritative), best-effort. Typed on the
  ApprovalRequestRow contract.

- Approval notifications now deep-link the inbox: `notify()` centrally
  rewrites the bare `/system/approvals` actionUrl to
  `/system/approvals?request=<id>` from the notification's source, so all
  twelve call sites — and any future one — land the recipient on the exact
  request (the console inbox consumes the param and auto-opens the drawer).

Tests: 4 new (per-group progress updates per approval, quorum clamped
threshold, first_response carries no progress, deep-link rewrite). 136 green
in plugin-approvals.

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

vercel Bot commented Jul 19, 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 19, 2026 1:08pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

102 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/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 packages/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/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.

…contract mapping (#3266)

Browser verification caught a #3268 gap: the `attachments` column landed on
sys_approval_action and decide() persisted it, but rowFromAction never mapped
it — every listActions consumer (REST /actions, the console timeline) saw
none while the raw engine row carried the fileIds. Map it (and type it on the
ApprovalActionRow contract) so the audit trail actually exposes what was
attached. Regression test added: 137 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016ypkQikZ55oWUHUnMebwXA
@os-zhuang
os-zhuang marked this pull request as ready for review July 19, 2026 13:19
@os-zhuang
os-zhuang merged commit d6c3f06 into main Jul 19, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/enterprise-delegation-ooo-proxy-1v3m8l branch July 19, 2026 13:19
os-zhuang added a commit that referenced this pull request Jul 20, 2026
…#3332)

* feat(approvals): declare file attachments on approve/reject decisions

Add an optional multi-file `attachments` param to the declared
`approval_approve` / `approval_reject` actions on sys_approval_request. The
console now renders `type:'file'` action params through the shared upload
widget (objectui ADR-0059) and POSTs the resolved `attachments: string[]`, so
a reviewer can attach supporting files to a decision through the generic
declared-action dialog — no bespoke composer.

No service/route change: the decision route already forwards `body.attachments`
to `ApprovalService.decide`, and the `sys_approval_action.attachments` column
(file, multiple) already persists them (#3266/#3274). This just surfaces the
capability as declared metadata so the approvals inbox can retire its
hand-wired attachment composer (objectui#2698).

Contract test pins the new param (type file, multiple, optional).

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

* chore(changeset): plugin-approvals decision attachments

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

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants