Skip to content

feat(approvals): point the account app's Approvals nav at the inbox component (#7234) - #7298

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-7234-approvals-nav-component
Aug 10, 2026
Merged

feat(approvals): point the account app's Approvals nav at the inbox component (#7234)#7298
os-zhuang merged 1 commit into
mainfrom
claude/issue-7234-approvals-nav-component

Conversation

@os-zhuang

@os-zhuang os-zhuang commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Fixes #7234

Entry C of #7213: the account app's Approvals entry opened the raw sys_approval_request grid. That surface cannot show an approver a single decision button — all eight actions on the object gate on record.viewer.can_act || record.viewer.can_override, and the viewer block is computed and attached only by the approvals REST path (approval-service.ts, attachViewers()), never by the generic data API an object route reads. The result was a correct-looking, completely inert list of rows nobody could act on.

The entry point does not move. Same id, same label, same icon, in every locale — which is also what keeps #7271's cross-package parity pin (my_pending view label === nav_account_approvals label, zh/ja/es) green. Only the destination changes.

What changed

  • account.app.tsnav_account_approvals becomes { type: 'component', componentRef: 'approvals:inbox' }, gated by requiresService: 'approvals'.
    • requiresObject was the right gate for an object route and is not a gate at all for a component one — it names the object the entry routes to. The capability that must actually be present is the approvals service (ctx.registerService('approvals', …)), which is also what backs the REST path the inbox reads. Verified rather than assumed: requiresService lives on BaseNavItemSchema, which ComponentNavItemSchema spreads, and filterAppForUser/filterNav reads e.requiresService variant-agnostically (rest-server.ts:2731/:2744, ADR-0057 D10) — packages/rest/src/meta-app-area-nav-gate.test.ts:34 already pins that a type: 'component' item is stripped this way. Without the gate the entry would render a dead component route wherever plugin-approvals is absent.
    • No requiredPermissions added anywhere — the account app deliberately has none, and the inbox enforces its own data access through the approvals REST path.
  • approvals-plugin.ts — a new nav_approvals_inbox component entry at the top of group_approvals. Order is array order (applyNavContributions does group.children.push(...c.items)), so its position in the array is the assertion, not a by-product. The three raw tables are untouched and stay behind group_approvals' manage_platform_settings gate as the admin/diagnostic surface.
  • Translationsnav_approvals_inbox in all four locales (en Approvals Inbox, zh-CN 审批中心, ja-JP 承認センター, es-ES Centro de aprobaciones). nav_account_approvals deliberately untouched.
  • Docscontent/docs/automation/approvals.mdx: "The approver finds it in their queue" now names the Approvals Inbox as the canonical approver surface and explains, in a callout, why the raw table is not it; plus a new "Mount the approvals inbox in your app" section with the one-entry snippet. content/docs/ui/setup-app.mdx's group_approvals row now enumerates the entries.
  • Changeset.changeset/approvals-nav-points-at-inbox.md (minor, both packages; user-visible navigation change).

Tests

Reverse verification

Both new gates were taken out and confirmed red, in the predicted direction:

  • Reverted account.app.ts to origin/main → 5 of the 7 new cases fail (expected 'sys_approval_request' to be undefined, expected undefined to be 'approvals', …). The 2 that stay green are the deliberate ones: the label/icon pin (unchanged by design) and the AppSchema.parse case — both shapes are valid metadata, which is precisely why the rest of the file exists.
  • Dropped the zh-CN nav_approvals_inbox label → check:app-nav-i18n goes red naming exactly that id: apps.setup.navigation — locale 'zh-CN' has no label for 1 runtime-merged nav id(s): nav_approvals_inbox contributed by @objectstack/plugin-approvals. This proves the new Setup entry really is merged and judged by the gate, not merely declared.

Local results

pnpm --filter @objectstack/platform-objects --filter @objectstack/plugin-approvals typecheck   Done (both)
platform-objects  test:  Test Files 12 passed (12) | Tests 296 passed (296)
plugin-approvals  test:  Test Files 20 passed (20) | Tests 452 passed (452)
check:app-nav-i18n:  OK (10 contributors, 54 merged `setup` nav ids, 4 locales, every id labelled in every locale)
check:doc-authoring: 374 files clean
check-nul-bytes:     OK (6650 text files)

plugin-approvals' approval-status-vocabulary.test.ts — including the cross-package parity case #7271 added today — is inside that green 452 and was run explicitly against this change.

Not in this PR


Generated by Claude Code

…omponent (#7234)

The account menu's Approvals entry opened the raw sys_approval_request grid,
which cannot show an approver a single decision button: all eight actions on
that object gate on `record.viewer.can_act || record.viewer.can_override`, and
the `viewer` block is attached only by the approvals REST path, never by the
generic data API an object route reads. A correct-looking, completely inert
list.

The entry point does not move — same id, label and icon in every locale, which
also keeps #7271's cross-package parity pin green. Only the DESTINATION changes,
to `{ type: 'component', componentRef: 'approvals:inbox' }`, gated by
`requiresService: 'approvals'` (requiresObject cannot gate a component entry;
the service gate is enforced server-side by filterAppForUser, ADR-0057 D10).

Setup gains an Approvals Inbox entry above the three raw tables, which stay put
as the admin/diagnostic surface. Docs document the approver's real surface and
how to mount the inbox in any business app.

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

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 10, 2026 5:25am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/automation/approvals.mdx (via @objectstack/plugin-approvals)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/plugin-approvals)
  • content/docs/plugins/packages.mdx (via @objectstack/platform-objects, @objectstack/plugin-approvals)
  • content/docs/ui/setup-app.mdx (via @objectstack/platform-objects)

2 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx (via @objectstack/plugin-approvals)
  • content/docs/releases/v9.mdx (via @objectstack/plugin-approvals)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

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.

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.

[approvals] Point the account app's Approvals nav at the inbox component; contribute a Setup inbox entry; document mounting the inbox in business apps

2 participants