Repository navigation
Commit f0022c4
fix(plugin-approvals): Setup → Approvals → Requests opens the tenant-wide list, and a merged-app pin closes the caller-scoped first-view family (#21991)
Fixes #21984
Clause-②: no
## What changes
An administrator who opens Setup → Approvals → Requests now lands on the
"All" list of approval requests. Before this, `nav_approval_requests`
named no view, and `sys_approval_request` declared the caller-scoped "My
Pending" view first (`pending_approvers contains {current_user_id}`).
The console opens an object's first declared list view when a route
names none, so the administrator saw only the requests pending on
themselves.
This applies the family's rule from triage `6012877503`: a caller-scoped
list view is never an object's first, and every entry that wants one
names it.
- **`sys_approval_request` declares `all_requests` first.**
`my_pending`, `submitted_by_me` and `completed` follow it in their
previous order. No view is added, removed or edited. A short comment at
`listViews` states why the position is the contract.
- **`nav_approval_requests` names `viewName: 'all_requests'`.** That is
the key the spec already declares on an object navigation item, so there
is no new key.
- **The four generated translation bundles follow the new order.** They
were regenerated with `node scripts/check-i18n-bundles.mjs --write`. The
change is a pure reorder of `_views` keys (+12 / −12 over four files),
and no translated text changed.
- **Unit pin:** `nav-contribution.test.ts` gains one case for this
entry.
- **Merged-app pin (new):**
`packages/qa/dogfood/test/platform-app-object-entry-views.test.ts`, with
56 cases. Triage `6015714713` requires it; it is described below. It
closes the family, because the `platform-objects` pins cannot see plugin
entries.
- **One changeset:** `@objectstack/plugin-approvals` at `patch`,
carrying `Clause-②: no`.
⛔ No new key, no `packages/spec` edit, no `platform-objects` edit, no
objectui change, no new `check:*` gate, and no governed surface.
## Supporting edits outside the declared file surface (so the pin can
exist)
- **`packages/qa/dogfood/vitest.config.ts`:** anchored source aliases
for `@objectstack/setup`, `@objectstack/account` and
`@objectstack/plugin-sharing` in the `isolated` project.
- The pin imports all three as values.
- Without an alias, each would resolve through `dist/` and would have to
be added to dogfood's `check:test-source-alias` row, which is
shrink-only and set-equal. An alias is that gate's prescribed fix.
- The other contributors are already aliased (`plugin-approvals`,
`service-datasource`, `cloud-connection`) or already in that row
(`plugin-security`, `plugin-audit`, `plugin-webhooks`,
`service-messaging`, `mcp`, `objectql`, `platform-objects`).
- **`packages/qa/dogfood/package.json`** gains `@objectstack/setup` and
`@objectstack/account` as devDependencies, and **`pnpm-lock.yaml`**
changes by 6 lines.
- With these, `turbo ls --affected` reaches this pin when either app
shell changes.
- Without them, the pin would read two packages outside its dependency
graph.
## The dispatch's hypotheses, measured
At `origin/main` `1c563af40e`. PR #21983 merged at 12:31Z, before this
branch was cut.
- **H1 holds.** `listViews` declared `my_pending` (`:62`),
`submitted_by_me` (`:76`), `completed` (`:86`) and `all_requests`
(`:99`). `nav_approval_requests` (`approvals-plugin.ts:147`) named no
view.
- **H2: no reader in this repository relies on `sys_approval_request`'s
first view.**
- **Account:** `nav_account_approvals` (`account.app.ts:114`–`:117`)
routes to the `approvals:inbox` component. No Account entry names
`sys_approval_request`; the merged pin enumerates all six Account object
entries.
- **Setup:** `nav_approval_requests` is the only entry naming the
object.
- **Code:** no code reads this object's first list-view key.
`packages/cli/src/commands/lint.ts:168` (`firstListViewKey`) reads a
first key only to place a label diagnostic. No view on this object sets
`isDefault`.
- **objectui at the `.objectui-sha` pin `0abd4f9f87`:** read, not
edited.
- `ApprovalsInboxPage.tsx` reads the approvals REST path
(`services/approvalsApi`) and names `sys_approval_request` only for its
declared actions (`:2494`, `:2541`).
- `recordApprovalActions.ts:36` and `deadRecordReference.ts:66` name the
object, not a view.
- No objectui file names `my_pending`, `submitted_by_me` or
`all_requests`.
- The generic `views[0]` doors (the object breadcrumb and the object
switcher) are fixed by the reorder itself.
- **H3 holds: the reorder moved four generated files.** These are
`plugin-approvals/src/translations/{en,es-ES,ja-JP,zh-CN}.objects.generated.ts`.
`check-i18n-bundles` first read "plugins/plugin-approvals: 4 bundle(s)
drifted", and after `--write` it exits 0. The
`*.source-hashes.generated.ts` files did not move.
- **H4 holds.** The merged pin is red on `main` for this entry only; see
"Red on main" below.
## Stop conditions (from `6012877503`)
- **Access difference: not met.** This was read from source; no
real-door read was run.
- The view filter is a presentation predicate added to the generic data
query. Row-level security and sharing decide which rows a caller reads,
whichever view is open.
- `all_requests` was already a tab on this page, and the diff changes
neither who can read nor which rows they get.
- The Setup entry also sits behind `group_approvals`'
`manage_platform_settings` gate.
- **No unscoped view: not met.** `all_requests` carries no filter at
all.
## The merged-app pin
`platform-app-object-entry-views.test.ts` boots the composition the way
`packages/cli/scripts/check-app-nav-i18n.mjs` does, which was read and
not edited:
- the same 11 contributors;
- a fake context whose only real service is `manifest`;
- each manifest handed to `ObjectQL.registerApp`;
- the apps read back through `registry.getApp`, which applies the same
`applyNavContributions` merge that `/api/v1/meta/app` serves.
Its population is every `type: 'object'` entry of the merged apps, with
nothing hand-listed: 26 Setup entries and 6 Account entries, naming 27
objects.
- **(a), 25 cases:** every object an entry names declares a first list
view without `{current_user_id}`.
- **(b), 14 cases:** every entry whose object declares a caller-scoped
view names a `viewName` that the object declares. On a Setup entry, that
view must not be caller-scoped.
- **Composition and non-vacuity, 17 cases:**
- both apps are merged;
- each contributor lands at least one id in each app it declares;
- the population contains plugin entries (`nav_approval_requests`,
`nav_record_shares`);
- the eight objects this family reordered are judged by both rules;
- no named object is unresolved;
- the objects that declare only caller-scoped views are exactly
`sys_inbox_message` and `sys_member`, and only Account entries name
them, each with `mine`.
- **Where an object's definition is read:**
- first, the composition's own registry (`registry.getObject`);
- otherwise, `@objectstack/platform-objects/identity`, which is the
barrel `plugin-auth` registers its identity objects from
(`authIdentityObjects`). `AuthPlugin` cannot boot without a secret,
which is also why check-app-nav-i18n leaves it out.
- At this head, 10 named objects come from the identity barrel and 17
from the composition.
- **Reach:**
- The roster mirrors check-app-nav-i18n's `CONTRIBUTORS` by hand, so a
contributor added there and not here is not seen. Reading that script's
text from this test would have needed a `CROSS_PACKAGE_TEST_INPUTS`
declaration and a turbo.json edit, so that was not done.
- `plugin-auth`'s `nav_sso_providers` is merged only when an external
IdP is wired, so no boot here merges it. Its object `sys_sso_provider`
declares no caller-scoped view.
## Red on main, green on the head
- **Main (`1c563af40e`):** the exact base blobs of the two subject files
were restored into the tree with `git restore --source`. The pin reads
`plugin-approvals` through its source alias.
- Pin: **2 failed | 54 passed (56)**. The failures are `(a) … ›
sys_approval_request` ("declares the caller-scoped list view
"my_pending" first; it is opened by setup/nav_approval_requests") and
`(b) … › setup/nav_approval_requests` ("names no viewName").
- Unit: **1 failed | 1 passed (2)**.
- No other entry was red, because PR #21983 had already landed. The
restore was proven by blob equality with HEAD and an empty `git diff
HEAD`.
- **Head (`10ff7b0044`):** pin **56 passed (56)**; unit **2 passed
(2)**.
## Ablation (on committed `079304d6ec`, through
`scripts/ablation-replace.mjs`, restores proven by blob)
The direction of each leg was predicted before it ran: one red pin case
and one red unit case per leg.
| leg | mutation | pin | unit |
|:--|:--|:--|:--|
| A | literal swap of the adjacent `all_requests` / `my_pending` blocks
(blob `17a36501e7fd → 014a8acaa16d`) | 1 failed / 55: `(a) ›
sys_approval_request` | 1 failed: "declares all_requests first" |
| B | drop `viewName: 'all_requests'` (blob `70ec3ecd12af →
9fb934b3be07`) | 1 failed / 55: `(b) › setup/nav_approval_requests`
"names no viewName" | 1 failed: "names its view" |
| C | `viewName: 'my_pending'` | 1 failed / 55: `(b)` "is an
administrator's entry and names the caller-scoped view" | 1 failed |
| D | `viewName: 'all_requestz'` | 1 failed / 55: `(b)` "names
"all_requestz", which the object does not declare" | 1 failed |
- No `dist/` sits between the mutation and the run: the pin reaches
`@objectstack/plugin-approvals` through the source alias
(`vitest.config.ts`), and the unit test imports it relatively.
- `plugin-approvals` was rebuilt afterwards for its built readers.
`ablation-dist-preflight` found the marker in both built files.
- The composition guards (a contributor landing nothing, an unresolved
object) were not ablated.
## Tests and gates (head `10ff7b0044`)
- `pnpm --filter @objectstack/plugin-approvals test`: 61 files, 899
tests passed.
- `typecheck` passed for both packages.
- `plugin-approvals`: its main program excludes tests.
`check:test-typecheck` compiles `nav-contribution.test.ts` through
`tsconfig.test.json` (`--listFiles`: 1), and the debt ledger has no
entry for it.
- `dogfood`: its `tsc` compiles the pin (`--listFiles`: 1).
- `pnpm check:app-nav-i18n` reports OK: 11 contributors, setup 55 and
account 12 merged nav ids.
- **Gates:** `node scripts/pm/dispatch-gates.mjs --commands` was
re-derived on this change with no paths, giving 79 commands. That is the
dispatch's 51 plus 28 from the changeset, manifest, lockfile and dogfood
files. All 79 exit 0.
- `--ran` reconciliation: "79 derived, 79 run, 0 NOT-MEASURED, 0 UNRUN",
a derived zero.
- `check:dual-build-cjs-loads` first exited 3 (PREREQUISITE NOT MET: 8
packages outside this closure had no `dist/`). Those were built (41
tasks, all turbo cache hits) and it re-ran to exit 0.
- **Lint, narrowed:** eslint `--no-inline-config --format json` over the
9 touched lintable files reported 9 files, 0 ignored, 0 errors and 0
warnings.
- The population is the repo's one `eslint.config.mjs`, and none of the
9 is ignored.
- That config never enables type-aware linting (no
`parserOptions.project`), so this diff cannot move a verdict on an
untouched file.
- The repo-wide `pnpm lint` is CI's.
## Acceptance notes (observed, not filed)
- `packages/platform-objects/src/apps/account.app.ts:79` says the
Account inbox entries "rely on pre-existing `*.mine` / `*.my_pending`
listViews". The Approvals entry has opened the inbox component instead.
This is comment drift in a file outside this card.
- `docs/qa/platform-checklist/areas/platform-core.json:525` lists the
Account destination as "Approvals (sys_approval_request/my_pending)",
but that entry is the `approvals:inbox` component. This is checklist
drift.
- The pin's roster and check-app-nav-i18n's `CONTRIBUTORS` are two
hand-kept copies of one composition, and nothing mechanical holds them
equal.
Implemented by the os-dev run of session
`session_01WMQprn46CND82KmY8sZWBu` on branch
`claude/issue-21984-approvals-requests-all-first`.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01WMQprn46CND82KmY8sZWBu)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 6befe19 commit f0022c4
12 files changed
Lines changed: 519 additions & 22 deletions
File tree
- .changeset
- packages
- plugins/plugin-approvals/src
- translations
- qa/dogfood
- test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
137 | 137 | | |
138 | 138 | | |
139 | 139 | | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
140 | 147 | | |
141 | 148 | | |
142 | 149 | | |
143 | 150 | | |
144 | 151 | | |
145 | 152 | | |
146 | 153 | | |
147 | | - | |
| 154 | + | |
148 | 155 | | |
149 | 156 | | |
150 | 157 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
5 | 23 | | |
6 | 24 | | |
7 | 25 | | |
| |||
80 | 98 | | |
81 | 99 | | |
82 | 100 | | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
83 | 128 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
58 | 58 | | |
59 | 59 | | |
60 | 60 | | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
61 | 71 | | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
62 | 81 | | |
63 | 82 | | |
64 | 83 | | |
| |||
96 | 115 | | |
97 | 116 | | |
98 | 117 | | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
104 | | - | |
105 | | - | |
106 | | - | |
107 | | - | |
108 | 118 | | |
109 | 119 | | |
110 | 120 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
107 | 110 | | |
108 | 111 | | |
109 | 112 | | |
| |||
116 | 119 | | |
117 | 120 | | |
118 | 121 | | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | 122 | | |
123 | 123 | | |
124 | 124 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
107 | 110 | | |
108 | 111 | | |
109 | 112 | | |
| |||
116 | 119 | | |
117 | 120 | | |
118 | 121 | | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | 122 | | |
123 | 123 | | |
124 | 124 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
107 | 110 | | |
108 | 111 | | |
109 | 112 | | |
| |||
116 | 119 | | |
117 | 120 | | |
118 | 121 | | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | 122 | | |
123 | 123 | | |
124 | 124 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
107 | 110 | | |
108 | 111 | | |
109 | 112 | | |
| |||
116 | 119 | | |
117 | 120 | | |
118 | 121 | | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | 122 | | |
123 | 123 | | |
124 | 124 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
| 44 | + | |
44 | 45 | | |
45 | 46 | | |
46 | 47 | | |
47 | 48 | | |
48 | 49 | | |
49 | 50 | | |
| 51 | + | |
50 | 52 | | |
51 | 53 | | |
52 | 54 | | |
| |||
0 commit comments