Skip to content

[maintainer] refactor(app-shell): move the Interfaces nav rail out of StudioDesignSurface.tsx (slice 3) - #30014

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-1cis03
Nov 4, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-1cis03

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Yibo Wang · project thread

Refs #29988

维护者速读

Clause-②: no
Metadata-protocol: none

Seam table

Carried forward from #30008. Base lines below are at origin/main eee41490 (this PR's base; StudioDesignSurface.tsx is byte-identical at 73a3ef9f, where #30008 landed), where ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx is 11,205 lines. "Left" is the file's line count after that row and every row above it: rows 1–3 are measured, the rest are estimates (lines removed, less about two import lines per slice). Every seam is module-scope; no seam closes over another component's state.

# Seam Base lines Size Reads from the rest of the module · hooks it calls Imported by Risk Left
1 Nav item inspector (StudioNavItemInspector, nav target helpers, StudioPicker) landed 978 — — landed in #30002 11,884 (measured)
2 PackageSwitcher with PACKAGE_LIST_TOAST_ID and STUDIO_LANDING_PATH landed 663 — — landed in #30008 11,205 (measured)
3 Interfaces nav tree rail: NavTree, NavTreeGroup, ContributedNavRows, OffNavSections, the two rail contexts, navKindName, navOpenedItems, with KIND_ICON / navIcon 663–679, 2249–2673 442 (17 + 425) nothing · useMetadataLocale, useNavTargetLabel, useOpenContributor InterfacesPillar only low: self-contained 10,757 (measured, this PR)
4 Flow rail helpers: FlowRuntimeState … readFlowCopiedArrival, incl. FlowStatusDot, glyph tables, pairIdOf 8061–8408 348 nothing · no hooks AutomationsPillar; AccessPillar (pairIdOf); 2 tests import FlowStatusDot low: pure helpers plus one prop-only component; carries the spec-symbol ledger's FlowRuntimeState site ≈ 10,411
5 Pillar kit: useHeldEditReport, confirmDropUnsavedWork, findTakenIdentifier, CreateTakenNotice, StudioHeldNotice, the "Show me" focus helpers, announceDraftSaved, StudioReadOnlyLink, StudioBaseCustomization 508–647, 695–773, 786–816, 818–865, 2681–2706 320 nothing · useStudioTabRowFold all four pillars and StudioDesignSurfaceBody low: five non-contiguous blocks; lets rows 7–10 move without an import cycle ≈ 10,093
6 Data pillar helpers (nextFieldName … formPreviewSource, the OWD option keys) 447–464, 5583–5823 259 nothing · no hooks DataPillar only; 1 test imports renderStudioGridList low: pure helpers ≈ 9,836 (under 10,000)
7 AccessPillar with its arrival reader and StudioNameLabel 2223–2247, 10262–11203 967 rows 4, 5 · 11 hooks StudioDesignSurfaceBody; 7 test files medium: whole stateful component; after rows 4–5 ≈ 8,871
8 AutomationsPillar 8410–10260 1,851 rows 4, 5 (and StudioPicker, row 1) · 21 hooks StudioDesignSurfaceBody; 23+ test files medium: after rows 4–5 ≈ 7,022
9 DataPillar 5827–8059 2,233 rows 2, 5, 6 (and StudioPicker) · 17 hooks StudioDesignSurfaceBody; 38+ test files medium: holds three clickableNonControlCensus-13461 rows keyed by this file ≈ 4,791
10 InterfacesPillar with interfacesSaveBody, leafKeyOf, customizeItemKey 488–506, 2678–2679, 2708–5581 2,895 rows 1, 3, 5 · 23 hooks StudioDesignSurfaceBody; 29+ test files medium: close to the 3,000-line mark; holds one census row ≈ 1,898
11 StudioDesignSurface / StudioDesignSurfaceBody, PILLARS, METADATA_AREA_TAB, PILLAR_FOR_SURFACE_TYPE, publish-refusal and backfill helpers 423–446, 465–487, 649–661, 775–784, 867–2221 ≈ 1,425 rows 2, 5, 7–10 · 11 hooks index.ts, the console's lazy Studio route, 51 test files stays: this is the file —

Unchanged from #30002: nothing below the whole-component level of rows 7–11 moves without a logic change, and none of it is needed, because rows 3–6 bring the file under 10,000. Serial order: rows 4–6 in any order, then rows 7–10 only after row 5.

What changes

  • ui/packages/app-shell/src/views/studio-design/InterfacesNavRail.tsx (new): row 3, base lines 663–679 and 2249–2673, each byte for byte, under a copyright line, a module comment and the imports the two blocks read. The block-level edits are export on the eleven names InterfacesPillar reads: navIcon, navKindName, InterfacesRailMarksContext, InterfacesNavContributionsContext, ContributedNavRows, NavTree, OFF_NAV_TYPES, OffNavType, itemKeyOf, navOpenedItems, OffNavSections. KIND_ICON, navTreeHoldsActive, NavTreeGroup and OffNavSection are read only inside the module and stay unexported. The two contexts move as they were (React.createContext with the same defaults); InterfacesPillar still provides them, so no logic changed.
  • ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx: both blocks are gone. It imports the eleven names from the new module beside the slice 2 import, and drops the import bindings only the rail used (Badge, Separator, FileText, BarChart3, Table2, Folder, Compass, Link as LinkIcon, Puzzle, BookOpen, Minus, listViewTargetOf, isSameSurface, inMenuOrder, isNavEntryType, NavTargetLabelResolver, RailCustomizations, NavContributionPlacement, TopLevelContributedNav). Nothing in the row was exported before, and no file outside InterfacesPillar reads any of it (the other mentions are comments in servedNavText.ts, AppNavCanvas.tsx and StudioDesignSurface.railLabels.test.tsx), so there is no re-export.
  • By-path readers (ruling 6597555122 item 3):
    • ui/packages/app-shell/src/__tests__/homeRecoveryRedirectsFollowDeclaration-7373.test.ts: the Studio site's alsoReads gains InterfacesNavRail.tsx.
    • ui/scripts/__tests__/spec-symbol-ledger-app-shell-7265.test.ts: SITES gains InterfacesNavRail.tsx.
  • Two test comments now name the module that holds the text they cite, which moved in slice 2 ([maintainer] refactor(app-shell): move Studio's package switcher out of StudioDesignSurface.tsx (slice 2) #30008 flagged them): PackagesPage.confirmFlows-11784.test.tsx ("PackageSwitcher's eviction in PackageSwitcher.tsx") and packages-io.inflightShare-11797.test.tsx ("the PACKAGE_LIST_TOAST_ID doc in PackageSwitcher.tsx"). Comment text only.
  • .changeset/29988-studio-interfaces-nav-rail-module.md: @object-ui/app-shell patch, "internal: move-only".

Tests

No behaviour changes, so no new behaviour test. The existing suites run unchanged; results are in Verification.

Acceptance notes

  • Readers re-grepped. git grep of StudioDesignSurface.tsx / studio-design/StudioDesignSurface over scripts/**, ui/scripts/** and every ui test file finds the same by-path readers as slices 1–2. The two whole-text scanners gained the new module (above). clickableNonControlCensus-13461 keys rows by file and handler; none of its rows is a rail handler (the rail's clicks are on StudioRailRow, a control), so it needs no edit. openPathObjectReaders-12811 classifies readers of useMetadata().objects; the rail reads none. The directory walkers (noRetiredSetupLink-28578, noSetupMetadataLink-28578, CreateItemDialog.oneLabel-11787) pick the new file up by themselves. No gate under scripts/** names the file.
  • Checklist rows re-read, none moved. Every StudioDesignSurface.tsx citation under docs/qa/ was read against the moved blocks, and none cites rail text: the Interfaces citations name the pillar's app load, its save into the pair and its unsaved-work guard, and the dashboards citation quotes the file's header comment ("Interfaces: the real App navigation tree"), all of which stay. So docs/qa/ is untouched and no revision row is added; check-platform-checklist still exits 0 full and with --changed-from origin/main.
  • Serial check. No open PR touched StudioDesignSurface.tsx, the new module, either extended reader or the two commented test files when the seat claimed (round 3 Claim) or right before this push (see Verification). The branch was restarted from origin/main after [maintainer] refactor(app-shell): move Studio's package switcher out of StudioDesignSurface.tsx (slice 2) #30008 merged and is rebased on eee41490, which touches only packages/spec/.

Verification

Move-only proof: byte-compare and the moved-lines diff
B=ui/packages/app-shell/src/views/studio-design
git show eee41490:$B/StudioDesignSurface.tsx | sed -n 663,679p > /tmp/blockA
git show eee41490:$B/StudioDesignSurface.tsx | sed -n 2249,2673p > /tmp/blockB
sed -n 47,63p $B/InterfacesNavRail.tsx | sed 's/^export const navIcon = /const navIcon = /' | cmp /tmp/blockA -
tail -n +65 $B/InterfacesNavRail.tsx | sed -E 's/^export (function|const|type) /\1 /' | cmp /tmp/blockB -

Both silent (identical): block A 17 lines, sha256 44978201e446c1bb…; block B 425 lines, sha256 73d3528850a14d73….

git diff --color=always --color-moved=zebra eee41490 HEAD -- ui/packages/app-shell/src/views/studio-design/: 440 lines removed and 440 added are coloured as moved (the block lines less the eleven export lines, plus import specifier lines that recur). Apart from import lines, the new module's header comment and the packages-io comment line, every line NOT coloured as moved:

+export const navIcon = (type?: string): LucideIcon => KIND_ICON[type ?? ''] ?? Compass;
+
+export function navKindName(node: Pick<NavNode, 'type' | 'viewName'>, locale: string): string {
+export const InterfacesRailMarksContext = React.createContext<RailCustomizations | null>(null);
+export const InterfacesNavContributionsContext = React.createContext<NavContributionPlacement>({ byGroup: new Map(), top: [] });
+export function ContributedNavRows({
+export function NavTree({
+export const OFF_NAV_TYPES = ['page', 'dashboard', 'report', 'view'] as const;
+export type OffNavType = (typeof OFF_NAV_TYPES)[number];
+export const itemKeyOf = (type: string, name: string): string => `${type}:${name}`;
+export function navOpenedItems(nodes: readonly NavNode[], locale: string): Set<string> {
+export function OffNavSections({
-const navIcon = (type?: string): LucideIcon => KIND_ICON[type ?? ''] ?? Compass;
-
-function navKindName(node: Pick<NavNode, 'type' | 'viewName'>, locale: string): string {
-const InterfacesRailMarksContext = React.createContext<RailCustomizations | null>(null);
-const InterfacesNavContributionsContext = React.createContext<NavContributionPlacement>({ byGroup: new Map(), top: [] });
-function ContributedNavRows({
-function NavTree({
-const OFF_NAV_TYPES = ['page', 'dashboard', 'report', 'view'] as const;
-type OffNavType = (typeof OFF_NAV_TYPES)[number];
-const itemKeyOf = (type: string, name: string): string => `${type}:${name}`;
-function navOpenedItems(nodes: readonly NavNode[], locale: string): Set<string> {
-function OffNavSections({
-

(the added blank line joins the two blocks in the new module; the removed blank lines are the separators after each cut block).

Declaration files (ruling 6348891160's bar)

Base declarations emitted from the base source into a scratch outDir under the lock, tree restored (git diff HEAD empty). Of the 701 base .d.ts files, 700 are byte-identical at head, StudioDesignSurface.d.ts included (sha256 d0f9665b1e9ed6fc…, the same as at slice 2): the rail was never exported, so no declaration moved. The one difference is chrome/ErrorBoundary.d.ts, where the members of the render() return union print in another order; with every union's members sorted (the ruling's normalisation) the two files are identical. The one addition is views/studio-design/InterfacesNavRail.d.ts / .js, reachable from no entry in the package's exports map ("." only). Declared-surface digest of dist/index.d.ts plus StudioDesignSurface.d.ts, root path masked: 245 exports, ce63368a1cf8becf… on both sides.

Build, type-check, lint and tests (head 376a48cf; the rebased head 36954c6 has a byte-identical ui/ and changeset, checked with git diff --quiet)

All under scripts/pm/os-verify-lock.sh, one locked chain: VERDICT command-exit 0.

  • npx turbo run build --filter='@object-ui/app-shell...' --concurrency=2: exit 0.
  • pnpm --dir ui --filter @object-ui/app-shell type-check (tsc --noEmit, then tsconfig.test.json): exit 0.
  • pnpm --dir ui run type-check:scripts: exit 0.
  • npx eslint on the two source files, the 7373 test and PackagesPage.confirmFlows-11784, from packages/app-shell: exit 0.
  • pnpm --dir ui exec vitest run --maxWorkers=3 packages/app-shell/src/views/studio-design/ plus the 7373 test, PermissionMatrixEditor.saveSurface-12725, builtinDesigners.lateArrival-11939, observableRegistries-11939, PackagesPage.confirmFlows-11784, clickableNonControlCensus-13461 and spec-symbol-ledger-app-shell-7265: exit 0, Test Files 329 passed (329), Tests 2446 passed (2446).
  • Narrowed, and declared: the full @object-ui/app-shell suite does not finish locally on the shared box; the run covers every test in the moved file's directory and every test outside it that imports the file, reads it by path or names the moved rail. CI's Test UI runs the rest.
  • node scripts/check-platform-checklist.mjs: exit 0; with --changed-from origin/main: exit 0.
Ablation of the two extended readers (one-time, through scripts/ablation-replace.mjs; tree restored, git diff HEAD empty)

The moved rail holds neither reader's needle, so each leg plants one in the new module and then takes the module out of the reader's list.

Leg Mutation Expected Observed
B0 none: the 7373 test as in this PR green exit 0: 15 passed
B1 a '/home' literal planted in InterfacesNavRail.tsx, alsoReads as in this PR red on the Studio site exit 1: "does not hard-code the launcher path" fails for the Studio site; 1 failed, 14 passed
B1b same mutation, InterfacesNavRail.tsx removed from the Studio site's alsoReads green, i.e. blind exit 0: 15 passed
B2 a local interface ObjectLike added to InterfacesNavRail.tsx, SITES as in this PR red exit 1: "rule 1 finds no spec-named local declaration in ...InterfacesNavRail.tsx" fails; 1 failed, 21 passed
B2b same mutation, the InterfacesNavRail.tsx SITES row removed green, i.e. blind exit 0: 21 passed

Each mutation's landing was proven by the tool (anchor count 1 to 0, blob hashes) and by a grep count of the changed text.

Derived gates: node scripts/pm/dispatch-gates.mjs --commands, run at b4129e71, whose tree head 36954c6 keeps (the amend rewrote only the commit trailer) (35 commands)
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: 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
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-sdui-manifest.mjs :: exit 0
node scripts/check-sdui-manifest.mjs --self-test :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --dir ui exec vitest run scripts/ :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0

node scripts/pm/dispatch-gates.mjs --ran: "35 derived famil(ies) accounted for — 35 run, 0 NOT-MEASURED". No docs/adr or docs/qa path changed, so the ADR-link and platform-checklist families are not derived this time; check-platform-checklist was still run by hand (Build, type-check, lint and tests, above).

Serial check before push

The 17 open PRs at 2026-11-04T05:50Z: each PR's changed files were read (seven PRs from their fetched heads against origin/main, three-dot; #29979, #29866 and the eight pm:mailbox PRs through the PR file list or their fetched head; every mailbox PR lists no file). None touches StudioDesignSurface.tsx, InterfacesNavRail.tsx, homeRecoveryRedirectsFollowDeclaration-7373.test.ts, spec-symbol-ledger-app-shell-7265.test.ts, PackagesPage.confirmFlows-11784.test.tsx or packages-io.inflightShare-11797.test.tsx.


Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01WGufzedfGwVcebpN5t3ETE

… StudioDesignSurface.tsx (slice 3)

Move-only (ruling #24586 A): the Interfaces pillar's navigation rail
(NavTree, NavTreeGroup, ContributedNavRows, OffNavSections, the two rail
contexts, navKindName, navOpenedItems, KIND_ICON / navIcon) moves byte for
byte into views/studio-design/InterfacesNavRail.tsx. Only imports and eleven
`export`s change. The two by-path readers gain the new module, and two test
comments now name PackageSwitcher.tsx, where slice 2 moved the text they cite.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufzedfGwVcebpN5t3ETE
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Nov 4, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · os-support-ai · session_01WGufzjDyeFwq6AuFu3vwTd

维护者速读(席位终稿)

席位核对 · ACCEPT · head 36954c629 · 2026-11-04T06:04Z

  1. Shape:
    • draft, base main eee41490b
    • attribution block, then Refs #29988; title says slice 3
    • one changeset (@object-ui/app-shell patch, internal move-only)
    • 7 files, +518/−465
    • check-governed-merges --pr 30014: NOT governed, 983 lines ≤ 3000
  2. Move-only, re-measured by the seat in order: I took the lines that StudioDesignSurface.tsx drops and the new module with export taken off. Their shared lines appear in the same order on both sides. The only lines the new module adds are its header and blank separators. Every other line that differs is an import specifier, or one of the 11 exports that InterfacesPillar needs. The surface file's added lines are only the import from ./InterfacesNavRail.js and its two narrowed import lines. The contexts moved as declarations, with no provider or logic change. The file is now 10,757 lines.
  3. By-path readers: both readers gained the new module, 7373 through alsoReads and 7265 through SITES. Both ablation legs came out as expected. No checklist text cites the moved code, and check-platform-checklist exits 0 both full and --changed-from. Accepted.
  4. Slice-2 follow-up done: the comments in PackagesPage.confirmFlows-11784 and packages-io.inflightShare-11797 now name PackageSwitcher.tsx. These are comment-only edits.
  5. Gates (dev, local at the head):
    • 329 test files and 2,446 tests green
    • dispatch-gates 35/35 exit 0, with --ran showing 0 NOT-MEASURED
    • 700/701 .d.ts byte-identical; ErrorBoundary.d.ts differs only in union member order, and the surface digest is equal
    • The push hook refused the first commit's model-naming trailer; only the trailer was amended, on the same tree
    • CI on this head was in progress at this reading
  6. Landing: ordinary queue. When every check on this head is green or expected-skip: relay pr_ready + automerge_enable, follow to MERGED, and record the landing on [maintainer] refactor(app-shell): split StudioDesignSurface.tsx (12,882 lines) along its component seams, one move-only slice per PR #29988. The card stays open. Slice 4 (row 4, the flow rail helpers) follows serially.

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/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant