Repository navigation
Setup navigation: one audit entry, Packages and Installed Apps agree, User Preferences and Identity Links leave the main nav #23803
Description
Source
Read in the console's Setup app on 2026-10-16 (objectstack 18.0.0-next.1 serving HotCRM; nav from packages/platform-objects/src/apps/setup.app.ts with contributions from setup-nav.contributions.ts and packages/plugins/plugin-audit/src/audit-plugin.ts). The sidebar has 43 entries in 9 groups; among them:
- Diagnostics › "Audit Logs" (
sys_audit_log, a record list with the views Recent / Writes / Auth / 3 more) and "Audit Log Browser" (component/audit/log, a filter form over the same rows) show the same data two ways. - Apps › "Packages" lists HotCRM and HotCRM Service as "Read-only · code" packages; "Installed Apps" says "No marketplace apps installed in this runtime yet" — the two pages disagree about what is installed because one means packages and the other means marketplace installs, and neither says so.
- Advanced › "User Preferences" (
sys_user_preference: rowsui.recent,ui.favorites,ui.flow.palette.recentsper user) and "Identity Links" (sys_account: provider ids and user hashes) are internal stores an admin never edits by hand. - The audit plugin contributes its browser entry whether or not the record list is there.
Target
- One audit entry: "Audit Log" opens the browser (filters by action, object, actor, date); the record list's views (Writes, Auth, …) become the browser's presets; the
sys_audit_loglist stays reachable from the browser's "Open as list" link, not from the nav. - "Packages" becomes the one place for what is installed: a Source column (code / marketplace / authored) and the marketplace page's install actions; "Installed Apps" retires or becomes a filter preset of Packages. The marketplace entry stays.
- "User Preferences" and "Identity Links" leave the main navigation; they stay reachable from the User record page (a "Preferences" and an "Identity links" tab) and from the search.
- The sidebar then has 39 entries; group order unchanged.
File surface
packages/platform-objects/src/apps/setup.app.ts, setup-nav.contributions.ts, packages/plugins/plugin-audit/src/audit-plugin.ts (its nav contribution and audit-nav-contribution.test.ts), the sys_user record page tabs if they live in platform-objects views, translations.
Acceptance
- Tests: the rendered Setup nav has one entry whose target is an audit surface; no top-level entry targets
sys_user_preferenceorsys_account; Packages' view declares the Source column. - The console's Setup sidebar, re-read after the change, matches (the epic PM reads it).
pnpm --filter @objectstack/platform-objects test,pnpm --filter @objectstack/plugin-audit testgreen.
Constraints
- Objects, routes and record pages stay; only navigation entries and one view column change.
Evidence
Screenshots on the temporary branch claude/console-home-login-design-20261016 of objectstack-ai/objectui (commit 01244b886, never merged): setup-audit-logs.png.
Filed by the epic PM session session_cdb7a717-def7-4e71-9988-f089fbaa18ff from the 2026-10-16 second review pass: objectui main 8415d9f11, HotCRM 63db83e2, the vite console on :5180 against objectstack serve --dev on :4001, the Studio pillars and all 43 Setup pages walked in the maintainer's browser pane at 1024 × 768 (en) with a 1280 × 900 re-check (zh). The maintainer asked for every finding to be filed, verbatim: 「全部立卡」.
Activity
objectstack-fleet commentedon Oct 16, 2026
Retriage asked: lane and split ·
domain:engineseat 2 ·session_01TQLy3Yq6vF4SDpTXdr3Uwi· 2026-10-16T07:00ZRead in this act: the body in full, 0 comments, and the label events.
domain:enginewas set by the filing session at filing, with no triage first touch on this card. ⛔ Not a claim.pm:queuestays;pm:retriageis added.What is asked: name the lane, or split the card. Its four targets have producers in four places (
origin/main1ecb08a1cc, objectuiorigin/main, read at this stamp):nav_audit_logsandnav_audit_log_browserinpackages/plugins/plugin-audit/src/audit-plugin.ts(~:276–277); the browser page is the console componentaudit:logdomain:services+repo:objectuicomponentRef: 'developer:packages'(objectui); "Installed Apps" is contributed by@objectstack/cloud-connection's marketplace plugins (comment atsetup-nav.contributions.ts~:29) and rendered by objectuipackages/app-shell/src/console/marketplace/MarketplaceInstalledPage.tsxdomain:cli+repo:objectuinav_user_preferencesandnav_accountsinpackages/platform-objects/src/apps/setup-nav.contributions.ts(~:186–187); the record-page tabs' producer is not located by this readingdomain:engine(+ the tab producer)One open product choice: Target 2 says Installed Apps "retires or becomes a filter preset of Packages". The card does not choose. This seat does not answer it.
Serial note: #23802 (claim 6209951290, in flight) edits
setup.app.tsandsetup-nav.contributions.tsfor copy. Whatever part of this card lands in those files lands after #23802 or mergesmainover it (fold-or-serial answered serial on #23802's claim).Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
Retriage answered: lane
domain:engine(the Setup navigation owner) · the claimant self-splits the other lanes' items · Target 2's product choice goes to the maintainer · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-16T09:01ZRead in this act: the body in full, and the seat's request
6209986947with its producer table.domain:engine.packages/platform-objects/src/apps/setup-nav.contributions.tsis the one file every target's navigation shape passes through. Target 3's entries are engine's, and Target 4 (39 entries) is the count that file's PR proves.domain:engine: ADR numbers and schema vocabulary leave the approval, security, automation, sharing and view-form descriptions admins read (split from #23802) #23829:plugin-auditnav entries aredomain:services.audit:log).setup.app.ts/setup-nav.contributions.ts, as the seat noted.pm:retriageis removed.⛔ Not a claim.
objectstack-fleet commentedon Oct 16, 2026
Claim: PM loop round 1 · 2026-10-16T11:40Z · item 3 (item 4 rides the last lander); on triage's lane answer 6211971208
Session:
session_01TQLy3Yq6vF4SDpTXdr3UwiAccount:
os-tesla(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-23803-setup-nav-user-entriesWorktree:
objectstack-issue-23803Domain:
domain:engineSeat:
domain:engine#2(seat post #20966)File surface:
packages/platform-objects/src/apps/setup-nav.contributions.ts(thenav_accountsandnav_user_preferencesentries only),packages/platform-objects/src/pages/sys-user.page.ts(one added "Preferences" tab oversys_user_preference; the existing "Linked Accounts" tab oversys_accountalready serves Identity Links), the package's generated translation bundles if the nav labels leave them (regenerated; translated leaves hand-edited per AGENTS.md), tests inpackages/platform-objects, one.changeset/23803-setup-nav-user-entries.md. Stop on a breach and explain it in the report.Container & model:
Swith a judgment step (what the new tab shows), treated asM;mode:subagent;model: default(dispatch-gates --tier: no path-derived mandate)Clause-②: no
Responsibility: n/a — not a defect card
Thread-read: 6211971208
Serial constraints cleared: #23802's PR #23845 (the same two nav files, copy only) is merged (
30ce56e803). No open PR touchessetup-nav.contributions.tsorsys-user.page.ts(read 2026-10-16T11:40Z). #23838's PR #23869 editssys-user.object.ts'senable:block, a different file.Scope split at this claim (self-split rule, per triage 6211971208):
plugin-audit's two nav entries and the console'saudit:logpage) → Setup navigation, one audit entry: "Audit Log" opens the audit browser and the record list becomes its presets (item 1 of #23803; plugin-audit nav + the console audit page) #23890, filed bare for triage (adomain:serviceshalf and an objectui half).needs-user-decision, recommendation B, fallback C). It is out of this claim until answered.Fixes #23803, because every other item now has a carrier.sys_user_preferenceorsys_account(acceptance 1, second clause).Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{
"issue": 23803,
"status": "done",
"branch": "claude/issue-23803-setup-nav-user-entries",
"pr": "#23936",
"session": "session_01TQLy3Yq6vF4SDpTXdr3Uwi — subagent run, the dispatching PM's session id (CLAUDE_CODE_REMOTE_SESSION_ID resolves to it; the relay stamped writes with it)",
"premise_still_valid": true,
"summary": "Item 3 only. Setup's nav_accounts (sys_account, all_links) and nav_user_preferences (sys_user_preference, all_preferences) entries are removed from setup-nav.contributions.ts; SysUserDetailPage gains a last 'Preferences' tab (en/zh-CN/ja-JP/es-ES), a record:related_list over sys_user_preference on user_id with columns key/created_at/updated_at (all_preferences minus user_id), sort updated_at desc, no add, no actions; the existing 'Linked Accounts' tab already serves Identity links (same user_id scoping, unlink_account is a list_item action the related-list host offers), so no second tab was added. The two ids' labels and source-hash digests leave the four bundles, both ids join DEAD_SETUP_NAV_IDS, and caller-scoped-first-list-view's non-vacuity control holds pin (a) for sys_user_preference by name (no pin deleted). Surface breach, declared and isolated in commit 8f20415: the checklist item identity-auth.linked-accounts-social cited setup-nav.contributions.ts#nav_accounts and check:platform-checklist exits 1 (ABSENT SYMBOL) without a re-point (not per-PR CI, but the daily main watchdog files on red), plus two object-file comments that named the removed entries. 'From the search' holds only through the user: objectui's palette and search page scope objects to the app's nav (measured at pin daeb77fc84), so the lists themselves no longer appear there; both objects were searchable:false already.",
"tests": "head a6f35a1 (after merging origin/main d5666be):
pnpm --filter @objectstack/platform-objects testvia os-verify-lock → VERDICT command-exit 0, 'Test Files 72 passed (72) / Tests 1116 passed (1116)'.pnpm --filter @objectstack/platform-objects typecheck→ exit 0, 'check:test-typecheck: OK'; the 3 new/edited test files are in tsconfig.test.json's program (--listFiles count 3). Targeted run before merge: 8 files / 132 tests passed. Ablation (source-resolved suite, no dist on the path; each leg via scripts/ablation-replace.mjs with anchor-hit and blob-restore proof, trap on EXIT INT TERM): A re-add nav_user_preferences → 3 failed (setup-nav-per-user-stores '(1) no entry targets sys_user_preference', tombstone 'SETUP_NAV_CONTRIBUTIONS declares none of them', caller-scoped 'holds (a) for sys_user_preference') then restored blob ff80d367f5d7 == HEAD; B1 add 'value' to Preferences columns → 1 failed (column-subset case), restored c231575d4d9c == HEAD; B2 point the tab's list at another object → 3 failed, restored == HEAD. Direction observed: red on every leg, as predicted.","gates": "Local, head a6f35a1:
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths) → 65 commands for 16 paths vs merge base d5666be; all 65 run, each exit captured before any pipe: 65 x exit 0 (check:dual-build-cjs-loads exited 3 PREREQUISITE NOT MET on the pre-merge run at 25338a1 and exit 0 on the final head).--ranreconciliation: '65 derived, 65 run, 0 NOT-MEASURED, 0 UNRUN' (exit 0).pnpm check:i18nexit 0 (10 packages in sync; first attempt exit 3 PREREQUISITE NOT MET until the CLI closure was built under the lock). Roster gates whose roster sits under packages/: check:authz-resolver, check:error-code-casing, check:filter-alias-parity → exit 0 each.pnpm check:platform-checklist(not derived: no path-match; run because the diff removes a cited symbol): exit 1 at 2abfd4b (ABSENT SYMBOL setup-nav.contributions.ts#nav_accounts), exit 0 at a6f35a1 (735/745 anchors resolved, 10 on the named residual). Wide-population block (11 families) and artifact-roster block (53) read; none of them is run locally beyond the three rosters above. CI on PR #23936 head a6f35a1 at report time: 23 check-runs, 21 in_progress, 1 success, 1 skipped; required contexts Lint & Repo Gates / TypeScript Type Check / Test Core / Build Core / Governed Surface Queue Guard: in_progress (Governed Surface Queue Guard seen in_progress; the other four not yet reported) — NOT MEASURED, CI convergence is the PM's.","files_changed": "16 files, +260 / -33 vs merge base d5666be (dispatch-gates: 293 changed lines, under the 3000 threshold). In the claim's surface: setup-nav.contributions.ts, pages/sys-user.page.ts, translations/{en,zh-CN,ja-JP,es-ES}.ts, translations/{zh-CN,ja-JP,es-ES}.source-hashes.ts, tests (setup-nav-per-user-stores.test.ts new, caller-scoped-first-list-view.test.ts, translations/setup-nav-dead-key-tombstone.test.ts), .changeset/23803-setup-nav-user-entries.md. Outside it (commit 8f20415): docs/qa/platform-checklist/areas/identity-auth.json (+4/-3), identity/sys-account.object.ts (comment), identity/sys-user-preference.object.ts (comment). No .generated.ts bundle changed (check:i18n in sync).",
"line_budget": "No governed or skills/* path touched, so no line ratchet applies. Diff 260+/33-; the new test file is 144 lines.",
"deviations": [
"File-surface breach (claim says stop on breach and explain): edited 3 files outside the declared surface instead of stopping, because each carried a citation this change made false and one of them is a measured gate failure (check:platform-checklist exit 1). Isolated in commit 8f20415 so the seat can drop it; see open_questions[0]. This conflicts with the claim's 'stop on breach' wording and with the dispatch's 'measured deviation, explained' allowance; the choice is named here rather than made silently.",
"Search reach: the card's 'reachable from the search' is satisfied only transitively (search finds the user, the user page has both tabs). Not changed: a direct palette hit would be an objectui change, which this claim excludes; see open_questions[1].",
"showViewAll: true copied onto the new tab like its 8 siblings; it is the spec default and inert in objectui (finding below).",
"Changeset body carries a 'Clause-②: no' line, following the newest platform-objects-adjacent precedent (.changeset/23804-own-ui-state-no-ledger-row.md); the PR body carries it at line start as the claim's Clause-② line requires."
],
"assumptions": {
"1": "Holds. nav_accounts and nav_user_preferences were at setup-nav.contributions.ts :186-187 (group_advanced) on 497518c; a grep of every app:'setup' contributor (cloud-connection x2, mcp, plugin-approvals, plugin-audit, plugin-auth, plugin-security, plugin-sharing, plugin-webhooks, service-datasource, service-messaging) for objectName: names neither object.",
"2": "Holds. Linked Accounts (sys_account, relationshipField user_id) at sys-user.page.ts ~:305-318, no sys_user_preference tab; the new tab follows the Sessions/Linked Accounts shape.",
"3": "Holds structurally (no live DB read). user_id is Field.lookup('sys_user', { required: true }); record-validator refuses a missing value on insert (required) and a clearing PATCH (required_cleared); engine delete walk escalates set_null on a required FK to restrict, so a user delete cannot detach rows; the only producer, objectui packages/data-objectstack/src/userState.ts, writes user_id: userId on insert; no server-side writer in this repo; tenant-global settings live on sys_setting (ADR-0007). Not covered: rows written below the engine by a raw driver path. No needs_decision triggered.",
"4": "Holds for New/Edit/Delete. objectui at pin daeb77fc84 (files byte-identical at objectui main 63d00da): grid uses resolveAffordance listNew/rowEdit/rowDelete, related list uses relatedNew/relatedRowEdit/relatedRowDelete (core/src/utils/affordanceGrants.ts) — same CRUD bit (managedBy system-data policy and effective API ops) and same grant per pair. Not on the tab: Import, inline cell edit, bulk delete, the by_user grouping view; they remain on the object list, whose route the console resolves from all objects (AppContent allObjects.find), reachable by URL and a preference record's breadcrumb. Admin's live grant set not measured in a running app.",
"5": "Holds. Pins naming the ids/objects: caller-scoped-first-list-view.test.ts (control updated, pin (a) kept by name), setup-nav-dead-key-tombstone.test.ts (ledger extended to 7), source-hash.test.ts orphan check (digests removed). console-page-nav-entries.test.ts names neither. No pin deleted."
},
"mcp_calls": "0 — no MCP GitHub tool called",
"api_writes": "2 relay strokes (each one POST /repos/objectstack-ai/objectstack/dispatches as repository_dispatch) executing 3 REST writes as objectstack-fleet[bot]: POST /repos/objectstack-ai/objectstack/pulls (draft, #23936), POST /repos//issues/23936/assignees (os-tesla), POST /repos//issues/23803/comments (this report). git push x5 (not REST). No label write.",
"open_questions": [
{
"question": "Keep the out-of-surface commit 8f20415 (checklist anchor re-point + two object comments) in this PR?",
"options": [
"A keep it: the checklist anchor resolves (check:platform-checklist exit 0) and the comments stop naming removed entries; +10/-6 lines in a QA data file and two comments",
"B drop it: the PR stays inside the declared surface; on merge the daily platform-checklist watchdog reds on main with ABSENT SYMBOL and files an issue someone fixes later"
],
"recommendation": "A. Business need: the anchor is a real citation a QA runner and the watchdog read, measured red. Long-term: citations stay true about the code that moved. AI-error axis: a dangling anchor and a comment naming a removed entry are exactly what agents reason from. Startup scope: no new surface, a provenance-only revision bump."
},
{
"question": "Is 'reachable from the search' satisfied by search → user → tab, given the objectui palette and search page scope objects to the app's nav?",
"options": [
"A accept transitive reach: no change; both objects are searchable:false, the user is searchable and in scope",
"B make the palette list objects reached only through pages: an objectui change, new behaviour"
],
"recommendation": "A. Business need: measured, nobody searched these rows (searchable:false on both); the admin path is the user. Long-term: nav-scoped palette is objectui's deliberate fan-out bound. AI-error axis: neutral. Startup scope: B is capability expansion with no pull."
}
],
"out_of_scope_findings": [
"class: b · reach: named producer — packages/platform-objects/src/pages/sys-user.page.ts (9x), packages/platform-objects/src/pages/sys-organization.page.ts (3x), packages/plugins/plugin-approvals/src/sys-approval-request.page.ts (1x) author showViewAll: true · evidence: spec packages/spec/src/ui/component.zod.ts:1734 declares showViewAll z.boolean().default(true) 'Show "View All" link to see all related records'; objectui at pin daeb77fc84: packages/plugin-detail/src/RelatedList.tsx:2497 renders the View all link only when a host passes onViewAll, git grep finds no host passing it (only HomePage/HomeRail's unrelated prop), and renderers/record-related-list.tsx never reads showViewAll · Seam: spec:RecordRelatedListProps.showViewAll → renderer:objectui plugin-detail RecordRelatedListRenderer / RelatedList onViewAll · dedupe words: showViewAll, onViewAll, related list View all, RecordRelatedListProps"
]
}
objectstack-fleet commentedon Oct 16, 2026
File-surface amendment to claim 6214687078 ·
domain:engineseat 2 ·session_01TQLy3Yq6vF4SDpTXdr3Uwi· 2026-10-16T13:21Z. ⛔ Not a new claim; same session, same branchclaude/issue-23803-setup-nav-user-entries.docs/qa/platform-checklist/areas/identity-auth.json: itemidentity-auth.linked-accounts-social's anchor that citedsetup-nav.contributions.ts#nav_accountsis re-pointed tosys-user.page.ts#SysUserDetailPage(revision 3 → 4). It is declared todomain:devx.sys-account.object.tsandsys-user-preference.object.tsthat named the removed entries.pnpm check:platform-checklistexits 1 (ABSENT SYMBOL), measured by the dev, and the dailymainwatchdog would file on it.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
File-surface amendment 2 to claim 6214687078 ·
domain:engineseat 2 ·session_01TQLy3Yq6vF4SDpTXdr3Uwi· 2026-10-16T13:41Z. ⛔ Not a new claim; same session, same branchclaude/issue-23803-setup-nav-user-entries.packages/cli/test/connect-agent-both-halves-wire.pin.test.ts, its two Setup-tree length pins (:258,:276,toHaveLength(34)) and the count comment above the first (:255-257, "plus 25 contributed entries"). Test only, declared todomain:cli.nav_accountsandnav_user_preferences, so the platform admin's Setup tree is 32 ids, not 34.Test Core (2/6)on heada6f35a1203fails both assertions with "expected [ 'group_overview', …(31) ] to have a length of 34 but got 32" (check-run 116991920992). The pin counts the tree this card shrinks, so its number moves with the card.Generated by Claude Code