Skip to content

fix(platform-objects): Setup → Users opens on the All Users list view - #21971

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-21960-setup-users-all-users
Oct 6, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-21960-setup-users-all-users

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #21960
Clause-②: no

What changes

Setup → Users now opens on the All Users list view (all_users). Before this, it opened My Profile (me): one row, the administrator, with a page size of 1.

  • packages/platform-objects/src/apps/setup-nav.contributions.ts: nav_users gains viewName: 'all_users'. That key is the one ObjectNavItemSchema already declares on an object navigation item ("Default list view to open"). No new key.
  • packages/platform-objects/src/identity/sys-user.object.ts: a comment only. The me view's comment said the Account App surfaces it. At main it does not (see the readers below), and the comment now names what does reach it.
  • packages/platform-objects/src/apps/setup-users-nav-view.test.ts: the pins.
  • .changeset/21960-setup-users-all-users.md: @objectstack/platform-objects patch.

⛔ No listViews reorder, no view removed, no packages/spec edit, no governed surface.

Implemented by the os-dev run of session session_017ErfyP2Rx7XWHJA27QjyUi on branch claude/issue-21960-setup-users-all-users.

The console honours viewName (measured, H1)

Read in objectui at the commit .objectui-sha pins, 0abd4f9f8769fc4c19ad2f96707684876f74c09f:

  • packages/layout/src/NavigationRenderer.tsx, resolveHref, case object. The precedence is recordId, then filters, then viewName. With viewName the href is OBJECT/view/VIEWNAME; without it, the bare OBJECT path.
  • packages/app-shell/src/views/ObjectView.tsx. The active view is the URL view id, then ?view=, then a view marked isDefault, then views[0]. Only the object's primary list is marked isDefault (buildViewTabs), and sys_user declares none, so views[0] is the first declared list view: me. That is what the card observed.
  • packages/core/src/utils/resolve-view-id.ts matches the short name all_users against the qualified id sys_user.all_users.
  • "Defaults to "all"" in the spec's viewName describe does not hold for an object that declares list views. all is only the id of the fallback tab the console builds for an object with no list views. This is reported on the card; it is not edited here, because it is spec text.

The served path keeps the key as authored. applyNavContributions (packages/objectql/src/registry.ts) pushes contributed items into a structured clone of the app, and filterNav (packages/rest/src/meta-item-read-gate.ts) pushes a passing leaf item unchanged. nav_notifications already ships viewName: 'recent' through the same contribution path.

Who reads me (measured, H2)

  • Navigation items: none. The Account app's profile entry is nav_account_profile: type: 'component', componentRef: 'account:profile_card'. No Account entry routes to sys_user.
  • objectui at the pin: none by name. account:profile_card is registered in apps/console/src/registerAccountComponents.tsx and renders ProfilePage, which reads the signed-in user from useAuth() and writes through updateUser.
  • Pages: none. pages/sys-user.page.ts names no list view.
  • Tests: none.
  • Other references: the four generated translation bundles carry the tab's label, and the spec's ObjectNavItemSchema docblock cites sys_user.me as an example of the {current_user_id} filter vocabulary.

So me is reached only as a tab, and as the default tab when a route names no view. Triage's second pin ("the Account app's My Profile still opens on the caller's own row") is held by the profile component, and it is pinned there.

Pins

setup-users-nav-view.test.ts, 7 cases:

  • nav_users sits in group_people_org with type: 'object', objectName: 'sys_user' and viewName: 'all_users';
  • it carries neither recordId nor filters, both of which resolveHref honours ahead of viewName;
  • sys_user.listViews.all_users exists under that same name;
  • that view is not scoped to the caller: no filter, no {current_user_id} anywhere in it, and a page size above 1;
  • the contribution passes a full NavigationContributionSchema.safeParse, and the parsed item keeps viewName: 'all_users';
  • the Account app's first entry is nav_account_profile, the account:profile_card component;
  • no Account entry routes to sys_user.

No dogfood door pin. The framework half of the door is the served Setup nav, and both sites on that path pass the item through as authored (above). The data half is unchanged by this diff: all_users carries no filter. A door case would assert what the metadata pin already holds.

Reverse verification

On committed 6639eb4a52:

node scripts/ablation-replace.mjs --file packages/platform-objects/src/apps/setup-nav.contributions.ts \
  --anchor "viewName: 'all_users', " --delete -- \
  pnpm --filter @objectstack/platform-objects exec vitest run --maxWorkers=2 src/apps/setup-users-nav-view.test.ts
  • Mutation landed: anchor 1 → 0, blob 72a3adcc1066 → 7c70ade946a9.
  • Result: 4 failed | 3 passed (7). The four cases that read nav_users.viewName went red. The recordId / filters case and both Account cases stayed green, as expected, because none of them reads viewName.
  • Restored: the blob after restore, 72a3adcc1066, equals the HEAD blob, and git diff HEAD is empty. git status --porcelain was empty afterwards.
  • Resolution path: the test imports its subject by relative path from src/, not through the package exports, so no dist/ sits between the mutation and the run.
  • First attempt: it was refused by the tool before anything ran, because its replacement text already appeared inside the anchor. It ran nothing and restored.

Tests and gates, at eb9b315b28

That is the merge of origin/main at 2a22177ae7. packages/spec was rebuilt after the merge, then the whole workspace (turbo run build --concurrency=2, 72 tasks).

  • pnpm --filter @objectstack/platform-objects exec vitest run --maxWorkers=2: Test Files 61 passed (61), Tests 967 passed (967).
  • pnpm --filter @objectstack/platform-objects typecheck: exit 0, with check:test-typecheck: OK.
    • The new test file is in the tsconfig.test.json program, counted with --listFiles.
    • The three errors that program reports are the ledgered feature-gate-guard.test.ts debt.
  • node scripts/pm/dispatch-gates.mjs --commands derived 63 commands, and all 63 exited 0.
    • The --ran reconciliation reads 63 derived, 63 run, 0 NOT-MEASURED, 0 UNRUN. That is a derived zero: every line carried its exit code.
    • 14 are new against the dispatch-time list: check-adr-0087-registration (base and self-test), check-empty-changeset (base and self-test), release-rehearsal-clone --self-test, release-pending-publish --self-test, check:engine-double-contract, check:objectql-double-limit, check:objectui-changeset, check:pm-changeset-deadline-census, check:query-options-erasure, check:type-check-coverage, check:type-check-debt and check:where-matcher.
  • Artifact-roster block (55 families, all run): 52 exited 0.
    • check-closing-target-claim.mjs, check-partof-closing-keyword.mjs and check-single-claim-paths.mjs answer NOT WIRED without a PR context. CI runs them with one.
    • check-partof-closing-keyword.mjs also exits 0 on this body when PR_BODY is set locally. The other two need the PR number.
  • Symbol-anchor sweeps: check:adr-symbol-anchors, check:scripts-symbol-anchors, check:spec-docblock-symbol-anchors and check:adr-anchors all exited 0.
  • Prerequisite refusals, then reruns:
    • check:dual-build-cjs-loads, check:i18n, check:lean-entry-closure and check:published-readme-exports first refused with exit 3 (no dist/). After the full build all four exited 0, and check:i18n reads "all bundles in sync".
    • check:type-check-debt first hit the runner's 420 s cap while it refreshed its closure. The rerun exited 0, with "1 ledger entr(ies) re-measured ... none above its recorded number".
  • ESLint, narrowed to the three touched TypeScript files: 3 files, 0 errors, 0 warnings.
    • The population is read from eslint.config.mjs: its packages/**/*.{ts,tsx,mts,cts} blocks match all three, and none was reported as ignored.
    • The count comes from --format json.
    • The config enables no 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

  • Other doors still open on me. objectui's AppHeader links the object breadcrumb segment and the object switcher to the bare object path, and a bare path opens views[0]. So from a user's record page, the "Users" breadcrumb still lands on My Profile. This PR closes the Setup nav door, as dispatched. Changing which view sys_user opens by default is the declaration-order route, which the dispatch reserved. It is reported on the card.
  • Sidebar highlight. objectui's resolveActiveNavItem scores a viewName item only on its own view. So nav_users no longer lights up on a user's record page or on the other tabs, where the bare item used to claim every sys_user sub-route. Every viewName entry behaves this way today (nav_notifications, and the Account mine entries). This was read from source at the pin, not measured in a browser.
  • Six other Setup entries land an admin on a caller-scoped first view. nav_api_keys, nav_sessions, nav_oauth_apps, nav_accounts and nav_user_preferences each open a mine view first, and plugin-sharing's nav_record_shares opens granted_to_me first. Their page sizes are 50 to 100, so these are not one-row views. They are reported on the card, not fixed here.

Generated by Claude Code

claude added 2 commits October 6, 2026 07:38
The Setup app's nav_users entry named no view, so the console opened
sys_user's first declared list view, `me` (the caller's own row, page
size 1). nav_users now names `all_users` through the object navigation
item's declared `viewName` key. The `me` view's comment no longer says
the Account app surfaces it: the profile entry is the
`account:profile_card` component.

Claude-Session: https://claude.ai/code/session_017ErfyP2Rx7XWHJA27QjyUi
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 6, 2026
@github-actions

github-actions Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/platform-objects, touching 4 documentable anchor(s).

11 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/data-modeling/object-extensions.mdx (via listViews (symbol, a field of const object SysUser))
  • content/docs/getting-started/build-with-claude-code.mdx (via listViews (symbol, a field of const object SysUser))
  • content/docs/kernel/contracts/metadata-service.mdx (via listViews (symbol, a field of const object SysUser))
  • content/docs/kernel/services-checklist.mdx (via listViews (symbol, a field of const object SysUser))
  • content/docs/protocol/kernel/i18n-standard.mdx (via listViews (symbol, a field of const object SysUser))
  • content/docs/protocol/kernel/plugin-spec.mdx (via listViews (symbol, a field of const object SysUser))
  • content/docs/protocol/objectui/layout-dsl.mdx (via listViews (symbol, a field of const object SysUser))
  • content/docs/ui/actions.mdx (via listViews (symbol, a field of const object SysUser))
  • content/docs/ui/forms.mdx (via listViews (symbol, a field of const object SysUser))
  • content/docs/ui/setup-app.mdx (via SETUP_NAV_CONTRIBUTIONS (symbol, a top-level const object))
  • content/docs/ui/views.mdx (via listViews (symbol, a field of const object SysUser))

⛔ 2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-5.mdx (via listViews (symbol, a field of const object SysUser))
  • content/docs/releases/v17/17-6.mdx (via listViews (symbol, a field of const object SysUser))

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.

What this run could not see
  • 1 anchor(s) matched too much of the corpus to be a work list: sys_user (literal, 37 pages)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 3 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 787104baa9ecca77ff13a8e83a4b7349728feadc → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 070347c82aa320e21b4e1d96c18ba68805c525a6 — the merge of head eb9b315b285665aeb9c950758c74220e806c9d50 into base 787104baa9ecca77ff13a8e83a4b7349728feadc, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 070347c82aa320e21b4e1d96c18ba68805c525a6 && git checkout 070347c82aa320e21b4e1d96c18ba68805c525a6
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 787104baa9ecca77ff13a8e83a4b7349728feadc eb9b315b285665aeb9c950758c74220e806c9d50 && git checkout -B drift-repro 787104baa9ecca77ff13a8e83a4b7349728feadc && git merge --no-ff eb9b315b285665aeb9c950758c74220e806c9d50

node scripts/docs-audit/affected-docs.mjs --json 787104baa9ecca77ff13a8e83a4b7349728feadc

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 787104baa9ecca77ff13a8e83a4b7349728feadc → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

ACCEPT (seat review) — PR #21971 at head eb9b315b28

domain:engine#1 · session_017ErfyP2Rx7XWHJA27QjyUi · read at 2026-10-06T08:48Z. The dev's report is os-dev-report on #21960.

  • The change:
    • setup-nav.contributions.ts's nav_users sets viewName: 'all_users', the key ObjectNavItemSchema already declares (packages/spec/src/ui/app.zod.ts:432).
    • The me view's comment in sys-user.object.ts now says what is true at main: no navigation item names me, and the Account app's profile entry is the account:profile_card component.
    • ⛔ No listViews reorder, no view removed, and no spec edit.
  • H1 (console): the dev read the pinned objectui source at 0abd4f9f87, not in a browser:
    • NavigationRenderer.tsx's resolveHref routes a viewName item to OBJECT/view/VIEWNAME.
    • ObjectView.tsx:2151 opens resolvedViewId || defaultViewId || views[0].
    • The served nav path passes the key through unchanged (registry.ts:4635, meta-item-read-gate.ts:774). nav_notifications already ships a viewName on the same path.
  • Pins: setup-users-nav-view.test.ts has 7 cases.
    • Reverse verification on 6639eb4a52 deleted the key: the 4 viewName cases went red, and the 3 controls stayed green. The restore was proven by blob equality and an empty git diff HEAD.
    • The platform-objects suite passes, 967 tests.
  • CI on eb9b315b28: 31 success, and the 3 skips are all in the roster. Readings against main at 230e4944b0: NOT governed (0 of 4 paths), and git merge-tree is clean.
  • Clause-②: no, at patch. No contract review is owed: there is no spec or governed path, and no exported type moves.
  • The dev's open question (bare-object doors and six more Setup entries): the seat takes option A. This PR stays as is, and the family is filed as finding(platform-objects): six more Setup object entries open their object's caller-scoped first list view (mine / granted_to_me), and sys_user's bare-object doors still open me (#21960's family, from PR #21971) #21972 for triage to decide per object. That card includes the warning that nav_account_linked relies on declaration order.
  • Filed from the report: finding(spec): ObjectNavItemSchema.viewName says the default is "all", but the console opens the object's primary list view, else its FIRST declared list view; "all" exists only for an object with no listViews #21973. viewName's describe text says the default is "all", while the runtime opens the default list view, else the first declared one.
  • Not filed: objectui's active-nav scoring for a lone viewName item, which is not highlighted on the object's other sub-routes. It is read from source only and has no carrier here.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 6, 2026 08:49
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 6, 2026 08:50
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 6, 2026
Merged via the queue into main with commit f76c622 Oct 6, 2026
36 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-21960-setup-users-all-users branch October 6, 2026 09:27
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Oct 7, 2026
…ist; a caller-scoped list view is never first (objectstack-ai#21983)

Fixes objectstack-ai#21972
Clause-②: no

## What changes

An administrator who opens Setup → API Keys, Sessions, OAuth
Applications, Identity Links, User Preferences or Record Shares now
lands on the tenant-wide "All" list. Before this, each of those entries
named no view. The console then opened the object's first declared list
view, and on every one of them that view was filtered to the caller
(`user_id = {current_user_id}`, or `recipient_id` for record shares).
The administrator saw only their own rows.

This applies triage's rule for the family (6012877503): a caller-scoped
list view is never an object's first, and every entry that wants one
names it.

- **Seven objects are reordered, and nothing else in them changes.** In
each, the unscoped "All" view moves to first place and the caller-scoped
view follows it. The other views keep their relative order. No view is
added, removed or edited.
- **Six Setup entries name their unscoped view with `viewName`,** as
`nav_users` already does. That is the key the spec already declares on
an object navigation item, so there is no new key.
- **The Account app's Linked Accounts entry (`nav_account_linked`) now
names `mine`.** It was the one Account object entry that relied on the
declared order. With `mine` no longer first, it would otherwise have
opened `all_links`.
- **Two comments are corrected.**
- `sys_user`'s `me` comment said RLS stops non-admins reading other
users' rows. `member_default` admits the caller's organization's users
through `sys_user_org_members`, and the comment now says so.
  - `nav_users`' comment now says `me` *used to be* first.
- **The generated translation bundles follow the new order.** Eight
files changed (four in each package), regenerated with `node
scripts/check-i18n-bundles.mjs --write`. These are pure reorders of the
`_views` keys: no translated text changed (+84 / −84).
- **New pins:**
`packages/platform-objects/src/apps/caller-scoped-first-list-view.test.ts`,
with 29 cases (described below).
- **Two changesets, both `patch`:** `@objectstack/platform-objects` and
`@objectstack/plugin-sharing`. Each carries the `Clause-②: no` line.

⛔ No new key, no objectui change, no `packages/spec` edit, and no
governed surface. In `plugin-sharing`, only the two declared files
change (`sys-record-share.object.ts` and `sharing-plugin.ts`), plus
their four regenerated bundles.

Implemented by the os-dev run of session
`session_017ErfyP2Rx7XWHJA27QjyUi` on branch
`claude/issue-21972-caller-scoped-views-not-first`.

| object | listViews at | old first | new order | Setup entry ·
`viewName` | Account entry · `viewName` |
|:--|:--|:--|:--|:--|:--|
| `sys_user` | `sys-user.object.ts:603` | `me` | `all_users`, `me`,
`unverified`, `two_factor`, `banned` | `nav_users` · `all_users`
(already set by objectstack-ai#21971) | none routes to `sys_user` |
| `sys_api_key` | `sys-api-key.object.ts:121` | `mine` | `all_keys`,
`mine`, `active`, `revoked` | `nav_api_keys` · `all_keys` |
`nav_account_api_keys` · `mine` (unchanged) |
| `sys_session` | `sys-session.object.ts:113` | `mine` | `all_sessions`,
`mine`, `revoked` | `nav_sessions` · `all_sessions` |
`nav_account_sessions` · `mine` (unchanged) |
| `sys_oauth_application` | `sys-oauth-application.object.ts:210` |
`mine` | `all_apps`, `mine`, `active`, `disabled_apps` |
`nav_oauth_apps` · `all_apps` | `nav_account_oauth_apps` · `mine`
(unchanged) |
| `sys_account` | `sys-account.object.ts:80` | `mine` | `all_links`,
`mine`, `by_provider` | `nav_accounts` · `all_links` |
`nav_account_linked` · `mine` (**new**) |
| `sys_user_preference` | `sys-user-preference.object.ts:36` | `mine` |
`all_preferences`, `mine`, `by_user` | `nav_user_preferences` ·
`all_preferences` | none |
| `sys_record_share` |
`plugin-sharing/src/objects/sys-record-share.object.ts:46` |
`granted_to_me` | `all_shares`, `granted_to_me`, `granted_by_me`,
`by_object`, `manual_grants`, `rule_grants` | `nav_record_shares`
(`sharing-plugin.ts:591`) · `all_shares` | none |

## The dispatch's hypotheses, measured

- **H1 holds: each of the seven objects declares an unscoped list
view.** Each one's "All" view carries no filter, except `all_sessions`,
whose only filter is `revoked_at is_null`. That view is the one now
first (table above). Stop condition 2 does not fire.
- **H2 holds: the order changes which view opens, not which rows a
caller may read.** This was read from `member_default`'s row-level
security in
`packages/plugins/plugin-security/src/objects/default-permission-sets.ts`.
No real-door read was run.
- **`sys_api_key`, `sys_session`, `sys_oauth_application` and
`sys_user_preference`** carry `_self` policies with `user_id ==
current_user.id`, operation `all` (`:921`, `:891`, `:955`, `:915`).
**`sys_account`** carries one for `select` (`:897`). Each is the same
predicate as `mine`, so a member's "All" view returns exactly the rows
`mine` returns.
- **`sys_user`** carries `sys_user_self` (`:871`) and
`sys_user_org_members`, `id in current_user.org_user_ids` (`:885`). So a
member's "All Users" view returns their organization's users, and `me`
did not. That is the declared staff-directory policy, and the header of
the same file names it as intended. It is not an access defect: the
member already had those rows through the existing "All Users" tab and
through `GET /data/sys_user`, and this diff changes neither. I read stop
condition 1 as not met. The reasoning is stated here so the seat can
disagree.
- **`sys_record_share`:** `member_default` has no wildcard object grant
and names no `sys_record_share` permission, so a member reads no rows
through either view. The Setup entry also requires
`manage_platform_settings`.
- **H3 holds: these are the readers of the declared order in this
repository.**
- **Account entries:** `nav_account_linked` was the only object entry
without a view. All six Account object entries now name `mine`.
- **Setup entries:** after this change, no Setup entry in
`platform-objects` names an object whose first view is caller-scoped.
The pin's (a) cases enumerate all of them.
- **Code:** `packages/cli/src/commands/lint.ts:168` (`firstListViewKey`)
reads the first key only to place a label diagnostic when no list view
has a label. Every view here has one.
- **Pages:** `sys-user.page.ts` related lists name these objects with
`showViewAll: true` and no view. How objectui's "View all" picks a view
was not read (NOT MEASURED).
- **Dashboards:** `system.datasets.ts` reads `sys_user` and
`sys_session` by object, not by view.
- **Tests:** none assert a view index. `setup-users-nav-view.test.ts`
and `i18n-resolver.object-list-views.test.ts` read views by name.
- **objectui (out of scope):** the `views[0]` fallback, the breadcrumb
and the object switcher are covered by the reorder itself.
- **H4 holds: the reorder moved eight generated files.** These are
`packages/platform-objects/src/apps/translations/{en,es-ES,ja-JP,zh-CN}.objects.generated.ts`
and
`packages/plugins/plugin-sharing/src/translations/{en,es-ES,ja-JP,zh-CN}.objects.generated.ts`.
`pnpm check:i18n` first read "platform-objects DRIFTED (4)" and
"plugins/plugin-sharing DRIFTED (4)". After `--write` it exits 0. The
other seven packages regenerated with no diff. No count or order pin
moved.

## Pins

`caller-scoped-first-list-view.test.ts`, 29 cases. The population is
derived from this package's navigation, not hand-listed. It is every
`type: 'object'` entry of `SETUP_NAV_CONTRIBUTIONS` and `ACCOUNT_APP`
(18 entries, 13 objects), looked up in this package's own exports.

- **(a), 11 cases: a named object's first declared list view carries no
`{current_user_id}`.** That is checked anywhere in the view, so the
`${current_user_id}` spelling is included.
- **(b), 12 cases:** every entry whose object declares a caller-scoped
view names a `viewName`. The object must declare that view under that
same name, and on a Setup entry the named view must not be
caller-scoped.
- **Population and non-vacuity, 4 cases:**
  - both apps contribute object entries;
- the six objects this card reordered in this package are judged by both
(a) and (b);
- the named objects this package cannot read are exactly
`sys_inbox_message`, owned by `service-messaging`. (b) therefore
requires its entry to name a view, and it names `mine`;
- the named objects that declare only caller-scoped views are exactly
`sys_member`. Only the Account app names it, with `mine`.
- **Parse, 2 cases:** every Setup contribution parses through
`NavigationContributionSchema` and the Account app through `AppSchema`,
and each object entry keeps its `viewName`.
- **objectstack-ai#21960's `setup-users-nav-view.test.ts`** stays green (7 of 7).

**Reach of the pins.**
- **Plugin-contributed entries are outside them.** Setup entries
contributed by plugins at runtime (`nav_record_shares`,
`nav_approval_requests`, …) are not visible from `platform-objects`,
which cannot import the plugins because they depend on it.
`plugin-sharing` gets no test file here: the dispatch declares only its
two files.
- **The sharing half was measured once instead.** Each built plugin was
booted with a fake manifest context, the way `check-app-nav-i18n` boots
them, at `a4d4688cfd`. That read gives `nav_record_shares →
sys_record_share | first=all_shares (unscoped) | viewName=all_shares
(unscoped)`.

## Reverse verification (on committed `f757947e6c`, through
`scripts/ablation-replace.mjs`)

The test imports its subjects by relative path from `src/`, so no
`dist/` sits between the mutation and the run. Directions were predicted
before each run: one red case each.

- **(a): `sys_session` restored to its old order** (`mine`,
`all_sessions`, `revoked`), by one literal swap of the two adjacent view
blocks.
- **Mutation landed:** anchor 1 → 0, replacement 0 → 1, blob
`4e46fda0773c` → `884ccaa2b537`.
- **Result: 1 failed | 28 passed (29).** The failing case is `(a) … ›
sys_session`: "sys_session declares the caller-scoped list view "mine"
first".
- **Restored:** blob `4e46fda0773c` equals HEAD, `git diff HEAD` is
empty, and `git status --porcelain` is empty.
- **(b): `viewName: 'all_sessions', ` deleted from `nav_sessions`.**
- **Mutation landed:** anchor 1 → 0, blob `17f1725c3cad` →
`2d7a16c6a894`.
- **Result: 1 failed | 28 passed (29).** The failing case is `(b) … ›
setup nav_sessions → sys_session`: "nav_sessions names no view".
- **Restored:** blob `17f1725c3cad` equals HEAD, `git diff HEAD` is
empty, and `git status --porcelain` is empty.

## Tests and gates, at `a4d4688cfd`

That commit is the merge of `origin/main` at `dcf3eb494a`, which brought
only `packages/spec` test files. The whole workspace was built before it
(`turbo run build --concurrency=2`, 72 tasks).

- **`pnpm --filter @objectstack/platform-objects exec vitest run
--maxWorkers=2`: Test Files 62 passed (62), Tests 996 passed (996).**
- **`pnpm --filter @objectstack/plugin-sharing exec vitest run
--maxWorkers=2`: Test Files 40 passed (40), Tests 980 passed (980).**
- **`pnpm --filter @objectstack/platform-objects typecheck` and `pnpm
--filter @objectstack/plugin-sharing typecheck`: both exit 0, with
`check:test-typecheck: OK`.** The new test file is in the
`tsconfig.test.json` program, counted with `--listFiles`.
- **`node scripts/pm/dispatch-gates.mjs --commands` derived 66 commands,
and all 66 exited 0.** `--ran` reads "66 derived, 66 run, 0
NOT-MEASURED, 0 UNRUN". That zero is derived: every line carried its
exit code.
- **14 are new against the dispatch-time list:**
`check-adr-0087-registration` (base and self-test),
`check-empty-changeset` (base and self-test), `release-rehearsal-clone
--self-test`, `release-pending-publish --self-test`,
`check:engine-double-contract`, `check:objectql-double-limit`,
`check:objectui-changeset`, `check:pm-changeset-deadline-census`,
`check:query-options-erasure`, `check:type-check-coverage`,
`check:type-check-debt` and `check:where-matcher`.
- **`check:type-check-debt` ran twice.** The first run was killed by my
own batch timeout. The rerun exited 0 in 221 s: "1 ledger entr(ies)
re-measured … none above its recorded number".
- **Artifact-roster block: 55 families, all run. 52 exited 0.**
- `check-closing-target-claim.mjs`, `check-partof-closing-keyword.mjs`
and `check-single-claim-paths.mjs` answer NOT WIRED without a PR
context. CI runs them with one.
- **Symbol-anchor sweeps:** `check:adr-symbol-anchors`,
`check:scripts-symbol-anchors`, `check:spec-docblock-symbol-anchors` and
`check:adr-anchors` all exited 0.
- **ESLint, narrowed to the 19 touched TypeScript files:** 19 files, 0
errors, 0 warnings, none reported as ignored.
- The population is read from `eslint.config.mjs`: its
`packages/**/*.{ts,tsx,mts,cts}` blocks match all 19.
  - The count comes from `--format json`.
- The config enables no 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

- **One more member of the family sits outside this card's lane.**
- **The defect:** Setup → Approvals → Requests (`nav_approval_requests`,
`packages/plugins/plugin-approvals/src/approvals-plugin.ts:147`) names
no view. `sys_approval_request` declares `my_pending` first, filtered on
`pending_approvers contains {current_user_id}`, so an administrator sees
only the requests pending on them.
  - **How it was measured:** by the same built-plugin read as above.
- **Why it is not here:** it is in `domain:services`, and the dispatch
allows nothing in that lane beyond the two `plugin-sharing` files. It is
reported to the seat on the card.
- **Two named objects declare only caller-scoped list views:**
`sys_member` (`mine`) and `sys_inbox_message` (`mine`). Only Account
entries name them, and those entries name `mine`. No unscoped view is
added here; that is triage's decision. Both are pinned as exact sets, so
a third object cannot join unnoticed.
- **The bare-object doors now open "All" for members too.** RLS scopes
the rows (H2). On `sys_user`, a member's object breadcrumb now opens
their organization's user list rather than My Profile.
- **Sidebar highlight.** Each entry that now names a view lights up only
on that view, as `nav_users`, `nav_notifications` and the Account `mine`
entries already do. This was not measured in a browser.

---
_Generated by [Claude
Code](https://claude.ai/code/session_017ErfyP2Rx7XWHJA27QjyUi)_

---------

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

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

platform-objects: Setup → Users opens on the "My Profile" view — an admin sees one row (themselves, page size 1) instead of the user list

2 participants