Repository navigation
fix(plugin-approvals): Setup → Approvals → Requests opens the tenant-wide list, and a merged-app pin closes the caller-scoped first-view family - #21991
Conversation
…wide list; all_requests is declared first sys_approval_request declared the caller-scoped my_pending first, and nav_approval_requests named no view, so the console opened my_pending: an administrator saw only the requests pending on themselves. - all_requests moves to first in listViews; the other views keep their relative order and nothing in them changes. - nav_approval_requests names viewName: 'all_requests'. - The four generated translation bundles follow the new order (node scripts/check-i18n-bundles.mjs --write): a pure reorder of the _views keys, no translated text changed. - nav-contribution.test.ts pins both halves for this entry. Claude-Session: https://claude.ai/code/session_01WMQprn46CND82KmY8sZWBu Co-authored-by: Claude <noreply@anthropic.com>
…-merged Setup and Account apps platform-objects pins the rule over its own Setup contributions and Account app, and cannot see a plugin's entries: the plugins depend on it, and their Setup entries arrive only at runtime. nav_approval_requests opened the caller-scoped my_pending inside that blind spot. platform-app-object-entry-views.test.ts boots the composition the way packages/cli/scripts/check-app-nav-i18n.mjs does (same roster, same manifest-service seam, read back through ObjectQL's registry getApp merge) and judges every type: 'object' entry of the merged Setup and Account apps: (a) the named object's first declared list view is not caller-scoped; (b) an entry whose object declares a caller-scoped view names a declared view, and a Setup entry names an unscoped one. The Setup and Account shells and plugin-sharing are aliased to source in the isolated project (check:test-source-alias is shrink-only), and the two shells become devDependencies so the affected graph reaches this pin. Claude-Session: https://claude.ai/code/session_01WMQprn46CND82KmY8sZWBu Co-authored-by: Claude <noreply@anthropic.com>
…ant-wide list Claude-Session: https://claude.ai/code/session_01WMQprn46CND82KmY8sZWBu Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 30 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 5 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 8 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 36a65b1eb82b5e5ef7d7939977b881459ca44f0e && git checkout 36a65b1eb82b5e5ef7d7939977b881459ca44f0e
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin aa09db58c9d688ce01e2abc1cbf543a6ecf193e4 10ff7b00440e9c25d1cc38873e4ddf1940c55193 && git checkout -B drift-repro aa09db58c9d688ce01e2abc1cbf543a6ecf193e4 && git merge --no-ff 10ff7b00440e9c25d1cc38873e4ddf1940c55193
node scripts/docs-audit/affected-docs.mjs --json aa09db58c9d688ce01e2abc1cbf543a6ecf193e4
|
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_requestsnamed no view, andsys_approval_requestdeclared 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_requestdeclaresall_requestsfirst.my_pending,submitted_by_meandcompletedfollow it in their previous order. No view is added, removed or edited. A short comment atlistViewsstates why the position is the contract.nav_approval_requestsnamesviewName: 'all_requests'. That is the key the spec already declares on an object navigation item, so there is no new key.node scripts/check-i18n-bundles.mjs --write. The change is a pure reorder of_viewskeys (+12 / −12 over four files), and no translated text changed.nav-contribution.test.tsgains one case for this entry.packages/qa/dogfood/test/platform-app-object-entry-views.test.ts, with 56 cases. Triage6015714713requires it; it is described below. It closes the family, because theplatform-objectspins cannot see plugin entries.@objectstack/plugin-approvalsatpatch, carryingClause-②: no.⛔ No new key, no
packages/specedit, noplatform-objectsedit, no objectui change, no newcheck:*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/accountand@objectstack/plugin-sharingin theisolatedproject.dist/and would have to be added to dogfood'scheck:test-source-aliasrow, which is shrink-only and set-equal. An alias is that gate's prescribed fix.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.jsongains@objectstack/setupand@objectstack/accountas devDependencies, andpnpm-lock.yamlchanges by 6 lines.turbo ls --affectedreaches this pin when either app shell changes.The dispatch's hypotheses, measured
At
origin/main1c563af40e. PR #21983 merged at 12:31Z, before this branch was cut.listViewsdeclaredmy_pending(:62),submitted_by_me(:76),completed(:86) andall_requests(:99).nav_approval_requests(approvals-plugin.ts:147) named no view.sys_approval_request's first view.nav_account_approvals(account.app.ts:114–:117) routes to theapprovals:inboxcomponent. No Account entry namessys_approval_request; the merged pin enumerates all six Account object entries.nav_approval_requestsis the only entry naming the object.packages/cli/src/commands/lint.ts:168(firstListViewKey) reads a first key only to place a label diagnostic. No view on this object setsisDefault..objectui-shapin0abd4f9f87: read, not edited.ApprovalsInboxPage.tsxreads the approvals REST path (services/approvalsApi) and namessys_approval_requestonly for its declared actions (:2494,:2541).recordApprovalActions.ts:36anddeadRecordReference.ts:66name the object, not a view.my_pending,submitted_by_meorall_requests.views[0]doors (the object breadcrumb and the object switcher) are fixed by the reorder itself.plugin-approvals/src/translations/{en,es-ES,ja-JP,zh-CN}.objects.generated.ts.check-i18n-bundlesfirst read "plugins/plugin-approvals: 4 bundle(s) drifted", and after--writeit exits 0. The*.source-hashes.generated.tsfiles did not move.mainfor this entry only; see "Red on main" below.Stop conditions (from
6012877503)all_requestswas already a tab on this page, and the diff changes neither who can read nor which rows they get.group_approvals'manage_platform_settingsgate.all_requestscarries no filter at all.The merged-app pin
platform-app-object-entry-views.test.tsboots the composition the waypackages/cli/scripts/check-app-nav-i18n.mjsdoes, which was read and not edited:manifest;ObjectQL.registerApp;registry.getApp, which applies the sameapplyNavContributionsmerge that/api/v1/meta/appserves.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.{current_user_id}.viewNamethat the object declares. On a Setup entry, that view must not be caller-scoped.nav_approval_requests,nav_record_shares);sys_inbox_messageandsys_member, and only Account entries name them, each withmine.registry.getObject);@objectstack/platform-objects/identity, which is the barrelplugin-authregisters its identity objects from (authIdentityObjects).AuthPlugincannot boot without a secret, which is also why check-app-nav-i18n leaves it out.CONTRIBUTORSby hand, so a contributor added there and not here is not seen. Reading that script's text from this test would have needed aCROSS_PACKAGE_TEST_INPUTSdeclaration and a turbo.json edit, so that was not done.plugin-auth'snav_sso_providersis merged only when an external IdP is wired, so no boot here merges it. Its objectsys_sso_providerdeclares no caller-scoped view.Red on main, green on the head
1c563af40e): the exact base blobs of the two subject files were restored into the tree withgit restore --source. The pin readsplugin-approvalsthrough its source alias.(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").git diff HEAD.10ff7b0044): pin 56 passed (56); unit 2 passed (2).Ablation (on committed
079304d6ec, throughscripts/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.
all_requests/my_pendingblocks (blob17a36501e7fd → 014a8acaa16d)(a) › sys_approval_requestviewName: 'all_requests'(blob70ec3ecd12af → 9fb934b3be07)(b) › setup/nav_approval_requests"names no viewName"viewName: 'my_pending'(b)"is an administrator's entry and names the caller-scoped view"viewName: 'all_requestz'(b)"names "all_requestz", which the object does not declare"dist/sits between the mutation and the run: the pin reaches@objectstack/plugin-approvalsthrough the source alias (vitest.config.ts), and the unit test imports it relatively.plugin-approvalswas rebuilt afterwards for its built readers.ablation-dist-preflightfound the marker in both built files.Tests and gates (head
10ff7b0044)pnpm --filter @objectstack/plugin-approvals test: 61 files, 899 tests passed.typecheckpassed for both packages.plugin-approvals: its main program excludes tests.check:test-typecheckcompilesnav-contribution.test.tsthroughtsconfig.test.json(--listFiles: 1), and the debt ledger has no entry for it.dogfood: itstsccompiles the pin (--listFiles: 1).pnpm check:app-nav-i18nreports OK: 11 contributors, setup 55 and account 12 merged nav ids.node scripts/pm/dispatch-gates.mjs --commandswas 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.--ranreconciliation: "79 derived, 79 run, 0 NOT-MEASURED, 0 UNRUN", a derived zero.check:dual-build-cjs-loadsfirst exited 3 (PREREQUISITE NOT MET: 8 packages outside this closure had nodist/). Those were built (41 tasks, all turbo cache hits) and it re-ran to exit 0.--no-inline-config --format jsonover the 9 touched lintable files reported 9 files, 0 ignored, 0 errors and 0 warnings.eslint.config.mjs, and none of the 9 is ignored.parserOptions.project), so this diff cannot move a verdict on an untouched file.pnpm lintis CI's.Acceptance notes (observed, not filed)
packages/platform-objects/src/apps/account.app.ts:79says the Account inbox entries "rely on pre-existing*.mine/*.my_pendinglistViews". 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:525lists the Account destination as "Approvals (sys_approval_request/my_pending)", but that entry is theapprovals:inboxcomponent. This is checklist drift.CONTRIBUTORSare two hand-kept copies of one composition, and nothing mechanical holds them equal.Implemented by the os-dev run of session
session_01WMQprn46CND82KmY8sZWBuon branchclaude/issue-21984-approvals-requests-all-first.Generated by Claude Code