Skip to content

docs(qa): shell-nav-personalization judges the Pinned section's order; the authored menu is not user-reorderable (objectui 8a55f0ccda) - #22522

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-22487-nav-personalization-pinned-order
Oct 9, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-22487-nav-personalization-pinned-order

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #22487
Clause-②: no

Rewrites one checklist item, platform-core.shell-nav-personalization in docs/qa/platform-checklist/areas/platform-core.json, to the sidebar behaviour that landed in objectui 8a55f0ccda (8a55f0ccdafa0b58e09f5d188641834f7da31521, objectstack-ai/objectui#12066, objectstack-ai/objectui#12059). It moves to revision: 2 and gains a history entry. No other item or file changes. The console pin bump that carries that commit (#22476) can cite this PR.

Authority

  • The maintainer, verbatim (card body): 「右侧浏览器,左侧菜单在用户界面不应该显示拖动按钮,拖动设计是studio的工作。」
  • Scope note on the card: personal ordering survives only inside the sidebar's Pinned section, and the authored menu order is not user-reorderable.
  • Triage's direction on the card: step 3 and acceptance[2] are replaced, not only removed. The negative half: the authored menu draws no grip and keeps the app's authored order whatever per-user key is stored. The positive half: a pinned entry reorders by dragging the row and by keyboard, and the order persists across reload. The positive clause is desktop-only, because a phone draws no Pinned section.

What the code at 8a55f0ccda does (read from source, not from the PR summary)

objectui main is 8a55f0ccda itself at this writing, so there is no drift between the landing and the tip. All links below are pinned to that sha.

  • The authored menu has no reorder. UnifiedSidebar no longer has useNavOrder: it existed at the parent c7b30bd6 and is gone at 8a55f0ccda. The comment at UnifiedSidebar.tsx L169-L173 states the stored key is not read. enableReorder / onReorder are retired at NavigationRenderer.tsx L229-L233. Menu levels are only sorted by byNavOrder (L1732; navOrder.ts L39: order ascending, with ties in array order).
  • The app arm wires the Pinned section's order, in UnifiedSidebar.tsx L531-L534: enablePinning={!isMobile}, pinnedOrder={pinnedIds}, onPinnedReorder={reorderPins}.
  • The Pinned section is PinnedNavigationList (L1192):
    • It has its own DndContext id="nav-pinned" (L1223).
    • A pointer press becomes a drag after 5px (L1137).
    • The keyboard codes are start Space, cancel Escape, end Space, and not Enter (L1145-L1148).
    • The row's own link is the activator: dragActivatorProps applies only aria-describedby, never dnd-kit's role, tabIndex or aria-roledescription (L1258-L1267). The link also gets cursor-grab (L1605).
    • The insertion line is data-drop-indicator (L1325), and the section is ordered by orderPinnedItems (L1859).
    • Its heading is console.nav.favorites (L1758). That reads 'Favorites' in en and 「收藏」 in zh, not "Pinned".
  • The value of aria-describedby is nav-pinned. In @dnd-kit/core 6.3.1 (the version objectui's lockfile resolves), useUniqueId("DndDescribedBy", id) returns the given id unchanged, so a pinned link's aria-describedby is the DndContext id.
  • Persistence. useNavPins.reorderPins → FavoritesProvider.reorderNavPins (useNavPins.ts L145-L156; FavoritesProvider.tsx L496).
    • withNavPinOrder (L182) reorders the type: 'nav' favorites in place.
    • withAddedFavorite (L167) appends a new nav pin.
    • commit (L407) writes localStorage plus the adapter. In the console, that adapter is the sys_user_preference row keyed ui.favorites (ConsoleShell.tsx L329).
  • Phone. isMobile means a viewport under 768px (use-mobile.tsx L11-L22), and there enablePinning is false, so no Pinned section is drawn.

Each changed text (before → after)

Placeholders that the file spells in angle brackets appear here as NAVID / APP / APP_NAME, because GitHub's body sanitizer eats angle-bracket fragments. The file's own spelling is unchanged.

  1. title
    • Before: "Shell nav personalization: sidebar collapse, pin/reorder, favorites, recents and header breadcrumbs each persist and stay navigable across reload"
    • After: "Shell nav personalization: sidebar collapse, pins and the user's pinned order, favorites, recents and header breadcrumbs each persist and stay navigable across reload; the authored menu keeps the app's order"
    • Read from: triage's "drop 'reorder' from the title"; UnifiedSidebar L169-L173.
  2. revision
    • Before: 1. After: 2.
    • A history entry is appended (item 13), per the README's "Change" rule and the validator's revision = last-history check.
  3. fixtures.requires[0]
    • Before: "…something to pin, reorder, favorite, and visit as recents"
    • After: "…something to pin — at least three entries, so the pinned order can change — favorite, and visit as recents"
  4. fixtures.requires[1] (new)
    • After: "a desktop viewport, at least 768px wide, for the Pinned section: below it useIsMobile is true and UnifiedSidebar passes enablePinning={!isMobile}, so a phone draws no Pinned section and has no pinned order to judge"
    • Read from: use-mobile.tsx L11-L22; UnifiedSidebar L531.
  5. steps[2]
    • Before: "drag-reorder: reorder nav items by drag (UnifiedSidebar useNavOrder, enableReorder / onReorder=handleReorder); reload — the order persists (localStorage per-app key)"
    • After: a "pinned order (desktop only)" step. It names:
      • where the section is: above the menu, headed console.nav.favorites, not the content-favorites group below Recent;
      • the pointer drag: the row itself, no grip, cursor-grab, 5px activation, data-drop-indicator;
      • the keyboard path: Space / arrows / Space, Escape cancels, Enter follows the link;
      • persistence: onPinnedReorder → useNavPins.reorderPins → FavoritesProvider reorderNavPins, the order of the type 'nav' favorites;
      • the order rules: a new pin joins the end, an unpin leaves the order.
    • Read from: NavigationRenderer L1137, L1145-L1148, L1192, L1258-L1267, L1325, L1605, L1758; useNavPins L145-L156; FavoritesProvider L167, L182, L496.
  6. steps[6] (new; "authored menu (any viewport)")
    • After: the menu has no grip, no aria-roledescription="sortable", no aria-describedby="nav-pinned" and no cursor-grab, and dragging a menu row moves nothing. Its order is the authored order (byNavOrder).
    • It then plants the retired key objectui-nav-order-APP_NAME (a JSON map of __root__ and each group id to reversed id lists, the shape the retired useNavOrder read) and reloads: the order is unchanged.
    • Read from: NavigationRenderer L229-L233, L1732; navOrder.ts L39; UnifiedSidebar L172; the parent's useNavOrder at c7b30bd6 UnifiedSidebar.tsx L143-L212.
  7. steps[7] (the persistence step)
    • Before: "…pins/favorites/recents are UserDataAdapter-backed (cross-device sync), reorder + collapse are local (localStorage / cookie)"
    • After: "…pins and their order, favorites and recents are UserDataAdapter-backed (cross-device sync; pins, their order and favorites are one sys_user_preference row, key ui.favorites), collapse is local (cookie sidebar_state)"
    • Read from: ConsoleShell L329; FavoritesProvider L407.
  8. acceptance[2]
    • Before: "drag-reorder persists: the reordered nav order is restored after reload (useNavOrder localStorage)", verified as "the nav item order before reload equals the order after reload".
    • After: "pinned order (desktop): a pinned entry is reordered by dragging its row and by keyboard (Space, arrows, Space), and the Pinned section's order is restored after reload (the order of the nav:NAVID favorites …)".
    • Its verify drives:
      • one pointer drag, with intermediate moves past 5px;
      • one keyboard move;
      • an Escape control;
      • a reload;
      • a control that each pinned link does carry aria-describedby="nav-pinned" and cursor-grab.
    • Oracle dom.
  9. acceptance[6] (new, appended so favorites, recents and breadcrumbs keep indexes 3-5)
    • After: "the authored menu is not user-reorderable (any viewport): no row outside the Pinned section draws a drag grip or carries aria-roledescription="sortable", aria-describedby="nav-pinned" or cursor-grab, and the menu renders in the app's authored order even with a stored objectui-nav-order-APP_NAME key".
    • Its verify:
      • counts zero [aria-roledescription="sortable"];
      • checks the menu links, with the pinned-row control;
      • compares the authored and rendered order;
      • plants the reversed key, reloads and re-reads;
      • repeats the probe at a phone viewport.
    • Oracle dom.
    • Read from: the same lines as item 6. The landed pin NavigationRenderer.pinnedOrder-12059.test.tsx asserts the same discriminators (zero sortable role, no aria-describedby / cursor-grab on menu links).
  10. negative
    • Before: "a pin / favorite / reorder / recent that does NOT survive reload is a FAIL of its persistence contract"
    • After: "a pin, the pinned order, a favorite or a recent that does NOT survive reload is a FAIL of its persistence contract"
    • Two lines are added:
      • On the authored menu, a grip, a sortable role, the drag instructions, a grab cursor, a drag that moves a row, or a non-authored order (with or without the stored key) is a FAIL.
      • A phone draws no Pinned section, so its absence there is not a FAIL of the pinned order.
  11. traps
    • Before: hydration-race, shared-browser-tab.
    • After: adds stale-console-bundle and automation-input, both already in RUNNER.md's trap table.
    • stale-console-bundle: this repo's .objectui-sha is still f0268ad784, which predates 8a55f0ccda. A console built at that pin still draws the menu grip, and a run against it must recognise that rather than file a regression.
    • automation-input: a one-jump synthetic pointer move never crosses the 5px activation distance, and reads as a dead drag.
  12. source
    • The UnifiedSidebar line is re-pointed. Before: "(useNavOrder drag-reorder localStorage; Recent section; applyPins)". After: no menu reorder; the stored key is not read; enablePinning={!isMobile} / pinnedOrder / onPinnedReorder; Recent section; applyPins.
    • The useNavPins and FavoritesProvider lines gain pinnedIds order / reorderPins and reorderNavPins / withNavPinOrder / withAddedFavorite.
    • New: objectui NavigationRenderer.tsx, navOrder.ts, use-mobile.tsx and ConsoleShell.tsx (bare: sibling-repo citations stay bare per the README).
    • New: one local anchor, packages/platform-objects/src/identity/sys-user-preference.object.ts#sys_user_preference, which resolves (anchors 719/729 → 720/730).
  13. history: an entry is appended, { revision: 2, date: 2026-10-09, ref: #22487 }. It quotes the ruling verbatim and names 8a55f0ccda.

Unchanged: the collapse, pin/unpin, favorites, recents and breadcrumbs steps and clauses, and the screenshot-first negative.

Gates (head b2a4ab115)

The list was derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, with no paths, from merge base 4e9fe9ff6. It has 13 commands, identical to the dispatch's path-derived lead. Each was run, and each exit code was captured before any pipe:

  • node scripts/check-ci-filter-parity.mjs exit 0
  • node scripts/check-closing-keyword-parity.mjs exit 0
  • node scripts/check-closing-keyword-parity.mjs --self-test exit 0
  • node scripts/check-comment-mask-corpus.mjs exit 0
  • pnpm --filter @objectstack/lint run check:doc-formula-expressions: first exit 3. It printed PREREQUISITE NOT MET (@objectstack/formula / @objectstack/lint unbuilt), so that run measured nothing. After turbo run build --filter=@objectstack/formula --filter=@objectstack/lint under the verify lock, the rerun was exit 0.
  • pnpm check:cross-package-test-inputs exit 0
  • pnpm check:doc-authoring exit 0
  • pnpm check:driver-memory-census exit 0
  • pnpm check:gitlink-declared exit 0
  • pnpm check:nul-bytes exit 0
  • pnpm check:platform-checklist exit 0. Verdict line: "check-platform-checklist: OK — 16 areas, 279 items (275 active, 2 planned) … traps: 19 documented, 19 in use … symbol anchors: 720/730 resolved". On the untouched base it was green too, at 719/729.
  • pnpm check:refd-timer-probe exit 0
  • pnpm check:watch-hint-literal exit 0

dispatch-gates --ran with exit codes recorded: "13 derived famil(ies) accounted for — 13 run, 0 NOT-MEASURED (a DERIVED zero …)". checklist-select platform-core.shell-nav-personalization resolves 1 runnable item. Changed lines: 44 (+30 / -14).

Changeset: none (skip-changeset). The one changed path is under docs/qa/. Zero of the 81 workspace packages' directories contain it, while the control packages/spec/src/index.ts hits @objectstack/spec. The repo root package is private: true. Nothing publishes.

Acceptance notes

  • NOT MEASURED: a live run of this item against a console built at 8a55f0ccda. The clauses are written from the code above. feat(layout,app-shell,i18n): the runtime sidebar menu is not user-reorderable; the Pinned section keeps the user's order (objectui#12059) objectui#12066 records its own Chromium readings (0 grips, 0 aria-roledescription at rest, a pinned link with cursor: grab, the keyboard path, a pointer reorder without navigation). The next checklist-test run of this item is the measurement.
  • The Pinned section's heading reads "Favorites", the same word as the content-favorites group below Recent. The pinned heading is console.nav.favorites; the content group is sidebar.favorites. Both are 'Favorites' in en and 「收藏」 in zh. The item said "the Pinned section" for a heading no user sees by that name. The new step names the heading, and the pin/unpin step's wording is left as it was (out of scope). Whether two same-named groups confuse users is a static reading only, not reproduced in a running console, so it is not filed. Carrier: none.
  • acceptance[1] (pin/unpin) is equally desktop-only in the code, since enablePinning={!isMobile} also hides the pin buttons. Its text is unchanged by the dispatch scope. The new fixture line (a desktop viewport) covers it in practice. Carrier: none.
  • Ordering. Triage set that this lands no later than the console pin bump carrying 8a55f0ccda (chore(objectui): bump the console pin past the fix for objectstack-ai/objectui#12056 (the designer's repeater row writes dataSource). #11509's v18 retirement must ship with it #22476). Until that bump, a console built at f0268ad784 fails acceptance[6], which is what the added stale-console-bundle trap tells a runner.

Generated by Claude Code

…authored menu as not reorderable

The runtime sidebar's menu is no longer user-reorderable; personal
ordering lives only in the Pinned section (objectui 8a55f0ccda). The
item's drag-reorder step and clause, which judged the retired
useNavOrder localStorage order, are replaced by a desktop-only
pinned-order clause (drag the row or Space/arrows/Space; persists with
the nav favorites) and a negative clause on the authored menu (no grip,
no sortable role, no grab cursor; authored order whatever
objectui-nav-order key is stored). Revision 2, with its history entry.

Claude-Session: https://claude.ai/code/session_01LYXc6ckoWuZyVZpWYizdMh
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

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants