Skip to content

[maintainer] refactor(app-shell): move Studio's flow rail helpers, pillar kit and Data pillar helpers out of StudioDesignSurface.tsx (slice 4, final: file under 10,000 lines) - #30027

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

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

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Yibo Wang · project thread

Fixes #29988

维护者速读

Clause-②: no
Metadata-protocol: none

Seam table

Carried forward from #30014. Base lines below are at origin/main 9d1a465f0 (this PR's base, after slice 3), where ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx is 10,757 lines. "Left" is the file's line count after that row and every row above it, measured at this PR's commits.

# Seam Base lines Size Reads from the rest of the module · hooks it calls Imported by Status Left
1 Nav item inspector (StudioNavItemInspector, nav target helpers, StudioPicker) — 978 — — landed in #30002 11,884
2 PackageSwitcher with PACKAGE_LIST_TOAST_ID and STUDIO_LANDING_PATH — 663 — — landed in #30008 11,205
3 Interfaces nav tree rail (NavTree … KIND_ICON / navIcon) — 442 — — landed in #30014 10,757
4 Flow rail helpers: FlowRuntimeState … readFlowCopiedArrival, incl. FlowStatusDot, glyph tables, pairIdOf → AutomationsFlowRail.tsx 7614–7960 347 nothing · no hooks AutomationsPillar; AccessPillar (pairIdOf); 2 tests import FlowStatusDot (re-exported) this PR (commit 1) 10,424
6 Data pillar helpers: the OWD option keys, nextFieldName … STUDIO_SYSTEM_FIELD_NAMES, formFieldNames … formPreviewSource → DataPillarHelpers.tsx 443–460, 5135–5202, 5281–5375 181 nothing · no hooks DataPillar only this PR (commit 2); renderStudioGridList (5204–5279, 76 lines) stays, see What changes 10,253
5 Pillar kit: useHeldEditReport, confirmDropUnsavedWork, findTakenIdentifier, CreateTakenNotice, StudioHeldNotice, the "Show me" focus helpers with their section comment, announceDraftSaved, StudioReadOnlyLink, StudioBaseCustomization → StudioPillarKit.tsx 504–643, 663–751, 764–843, 2233–2258 335 nothing · useStudioTabRowFold all four pillars and StudioDesignSurfaceBody; StudioBundleItemEditor.tsx, StudioMetadataArea.tsx and 6 tests import its types or components through ./StudioDesignSurface (re-exported) this PR (commit 3) 9,928 (under 10,000)
7 AccessPillar with its arrival reader and StudioNameLabel 2201–2225, 9814–10755 967 rows 4, 5 · 11 hooks StudioDesignSurfaceBody; 7 test files not moved; available to a later card —
8 AutomationsPillar 7962–9812 1,851 rows 4, 5 (and StudioPicker, row 1) · 21 hooks StudioDesignSurfaceBody; 23+ test files not moved; available to a later card —
9 DataPillar 5379–7611 2,233 rows 2, 5, 6 (and StudioPicker) · 17 hooks StudioDesignSurfaceBody; 38+ test files not moved; available to a later card (holds three clickableNonControlCensus-13461 rows keyed by this file) —
10 InterfacesPillar with interfacesSaveBody, leafKeyOf, customizeItemKey 484–502, 2230–2231, 2260–5133 2,895 rows 1, 3, 5 · 23 hooks StudioDesignSurfaceBody; 29+ test files not moved; available to a later card (holds one census row) —
11 StudioDesignSurface / StudioDesignSurfaceBody, PILLARS, METADATA_AREA_TAB, PILLAR_FOR_SURFACE_TYPE, publish-refusal and backfill helpers the rest — — index.ts, the console's lazy Studio route, 51 test files stays: this is the file —

Rows 4–6 now live outside the file, so a later card can move rows 7–10 without an import cycle back into it.

What changes

One commit per seam row, then one for the readers and the checklist.

  • Row 4 → ui/packages/app-shell/src/views/studio-design/AutomationsFlowRail.tsx (new): base lines 7614–7960, byte for byte, under a copyright line, a module comment and the imports the block reads. export is added on the 18 names the rest of the file reads (FlowRuntimeState, FlowRailState, flowRailState, flowRailStatus, flowDotClass, FLOW_GLYPH_ICON, FLOW_GLYPH_LABEL_KEY, NO_OBJECT_GROUP_KEY, readFlowRegistrations, readFlowDefinitions, readObjectLabels, newFlowTriggerFields, withStartTrigger, NO_OBJECT_ORDER, FlowCopiedStrip, FLOW_COPIED_ARRIVAL_STATE, pairIdOf, readFlowCopiedArrival). FlowStatusDot and NewFlowTrigger were already exported, keep their export, and are re-exported from StudioDesignSurface.tsx. The one-line section comment above the block at 7613 (/** Automations pillar — flows: … */) heads AutomationsPillar and stays.
  • Row 6 → DataPillarHelpers.tsx (new): base lines 443–460, 5135–5202 and 5281–5375, byte for byte; export on the 13 names DataPillar reads. renderStudioGridList (5204–5279) stays in StudioDesignSurface.tsx. It carries // eslint-disable-next-line react-refresh/only-export-components, and in a module that exports no component that line is an unused directive, which this repo's eslint reports as an error (measured on the first cut). Moving it would need that line edited or dropped, which is not a move, and the file is under 10,000 lines without it.
  • Row 5 → StudioPillarKit.tsx (new): base lines 504–643, 663–751 (the "Show me" section comment at 663–671 and its helpers), 764–843 and 2233–2258, byte for byte; export on the 7 names the rest of the file reads (useHeldEditReport, confirmDropUnsavedWork, findTakenIdentifier, CreateTakenNotice, useShowInputFocus, StudioSavedDraft, announceDraftSaved). StudioHeldNotice, StudioReadOnlyLink and StudioBaseCustomization were already exported and are re-exported from StudioDesignSurface.tsx.
  • StudioDesignSurface.tsx: the eight blocks are gone. It imports the names from the three modules beside the slice 3 import, re-exports the five names it exported before in two lines with a comment each (where their first block was), and drops the import bindings only the moved code used (describeFlowRunStatus, FlowRunStatusView, FlowRuntimeRow, fieldsForNodeType, localizeFlowFields, FlowConfigField, Zap, Clock, MousePointerClick, CornerDownRight, FlowTriggerGlyph, customizationPackageId, SpecFlowRuntimeState, romanizeHan, toFieldNameLoose, PackageCustomizationsState, useStudioTabRowFold).
  • Importers through ./StudioDesignSurface are unchanged, by the re-exports: FlowStatusDot.test.tsx, FlowStatusDot.registration-12349.test.tsx, StudioReadOnlyLink.overlay-27356.test.tsx, the five tests that import type StudioBaseCustomization, and the two source files StudioBundleItemEditor.tsx and StudioMetadataArea.tsx (type-only imports, so no runtime cycle). StudioDesignSurface.gridRefresh.test.tsx imports renderStudioGridList, which did not move.
  • By-path readers (ruling 6597555122 item 3): homeRecoveryRedirectsFollowDeclaration-7373.test.ts's Studio site alsoReads and spec-symbol-ledger-app-shell-7265.test.ts's SITES each gain the three modules. The ledger's FlowRuntimeState site (type FlowRuntimeState = Partial<SpecFlowRuntimeState>) now lives in AutomationsFlowRail.tsx.
  • docs/qa/platform-checklist/areas/studio-authoring.json, source re-points only, each with a revision bump and a history row ("No clause changed"):
    • held-edit-leave-guard r5 → r6: confirmDropUnsavedWork and useHeldEditReport now cited in StudioPillarKit.tsx; the rest stays on StudioDesignSurface.tsx.
    • automations-rail-and-new-flow r2 → r3: FLOW_GLYPH_ICON / FLOW_GLYPH_LABEL_KEY, flowDotClass, FlowStatusDot now cited in AutomationsFlowRail.tsx; the rail legend and the New automation presets stay.
    • flow-status-matches-runtime r2 → r3: FlowStatusDot and flowRailStatus now cited in AutomationsFlowRail.tsx.
    • access-permission-set-lifecycle r7 → r8: findTakenIdentifier and CreateTakenNotice now cited in StudioPillarKit.tsx; the Access pillar's use of them stays.
  • .changeset/29988-studio-pillar-helpers-modules.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

  • The bar is met: StudioDesignSurface.tsx is 9,928 lines at this head (wc -l), so this PR says Fixes #29988.
  • No fallback trigger hit: no import cycle (the three modules import nothing from StudioDesignSurface.tsx; the two source files that import a moved type through it do so type-only), no census row to re-key (clickableNonControlCensus-13461's rows for this file are DataPillar and StudioDesignSurfaceBody handlers, which stay; the moved clicks are <button>s), and 1,941 changed lines in all, under 3,000.
  • Readers re-grepped. git grep of StudioDesignSurface.tsx / studio-design/StudioDesignSurface over scripts/**, ui/scripts/** and every ui test file finds the same two whole-text readers as before; both gained the three modules. The directory walkers (noRetiredSetupLink-28578, noSetupMetadataLink-28578, CreateItemDialog.oneLabel-11787) and clickableNonControlCensus-13461 walk the tree themselves. openPathObjectReaders-12811 classifies readers of useMetadata().objects; none of the moved code reads it.
  • Checklist rows re-read. Every StudioDesignSurface.tsx citation under docs/qa/ was read against the moved names: the four above named moved helpers, and the rest cite pillar logic that stays (railPairId, ledgerRow, openCreateFlow, openCreateSet, heldEdit, unsavedPillarWork, each still in the file).
  • Comments that name the file for moved text, left as they are (outside the file surface): spec-symbol-parity.test.ts says "StudioDesignSurface declares type FlowRuntimeState = …" (now in AutomationsFlowRail.tsx; the test pins the types, not the file).
  • Serial check. No open PR touched StudioDesignSurface.tsx, the three new modules, either extended reader or studio-authoring.json when the seat claimed (round 4 Claim) or right before this push (see Verification). The branch was restarted from origin/main after [maintainer] refactor(app-shell): move the Interfaces nav rail out of StudioDesignSurface.tsx (slice 3) #30014 merged and is rebased on 63b86b38, which touches only packages/rest/ and its changeset.

Verification

Move-only proof: byte-compare of all eight blocks and the moved-lines diff
B=ui/packages/app-shell/src/views/studio-design
base() { git show 9d1a465f0:$B/StudioDesignSurface.tsx | sed -n "$1p"; }
UA() { sed -E 's/^export (async function|function|const|type|interface) (FlowRuntimeState|FlowRailState|flowRailState|flowRailStatus|flowDotClass|FLOW_GLYPH_ICON|FLOW_GLYPH_LABEL_KEY|NO_OBJECT_GROUP_KEY|readFlowRegistrations|readFlowDefinitions|readObjectLabels|newFlowTriggerFields|withStartTrigger|NO_OBJECT_ORDER|FlowCopiedStrip|FLOW_COPIED_ARRIVAL_STATE|pairIdOf|readFlowCopiedArrival)\b/\1 \2/'; }
cmp <(base 7614,7960) <(sed -n 38,384p $B/AutomationsFlowRail.tsx | UA)
US() { sed -E 's/^export (async function|function|const|type|interface) (useHeldEditReport|confirmDropUnsavedWork|findTakenIdentifier|CreateTakenNotice|useShowInputFocus|StudioSavedDraft|announceDraftSaved)\b/\1 \2/'; }
cmp <(base 504,643) <(sed -n 25,164p $B/StudioPillarKit.tsx | US)
cmp <(base 663,751) <(sed -n 166,254p $B/StudioPillarKit.tsx | US)
cmp <(base 764,843) <(sed -n 256,335p $B/StudioPillarKit.tsx | US)
cmp <(base 2233,2258) <(sed -n 337,362p $B/StudioPillarKit.tsx | US)
UD() { sed -E 's/^export (async function|function|const|type|interface) (OWD_OPTION_LABEL_KEY|OWD_OPTION_DESC_KEY|addedFieldName|addedExtensionFieldName|extensionFieldsOf|STUDIO_SYSTEM_FIELD_NAMES|formFieldNames|fieldCelStamp|CelPanelTab|isCelPanelTab|panelItemOf|panelItemCelStamp|formPreviewSource)\b/\1 \2/'; }
cmp <(base 443,460) <(sed -n 16,33p $B/DataPillarHelpers.tsx | UD)
cmp <(base 5135,5202) <(sed -n 35,102p $B/DataPillarHelpers.tsx | UD)
cmp <(base 5281,5375) <(sed -n 104,198p $B/DataPillarHelpers.tsx | UD)

All eight silent (identical). Per block (base range → module range, sha256 of the base text):

  • AutomationsFlowRail.tsx:
    • base 7614-7960 (347 lines) → module 38-384: IDENTICAL sha256 35d1751a7b947f16
  • StudioPillarKit.tsx:
    • base 504-643 (140 lines) → module 25-164: IDENTICAL sha256 228dce9ca39416f0
    • base 663-751 (89 lines) → module 166-254: IDENTICAL sha256 c3000f03708e8df6
    • base 764-843 (80 lines) → module 256-335: IDENTICAL sha256 7c9f296df5ef288d
    • base 2233-2258 (26 lines) → module 337-362: IDENTICAL sha256 8f83e8151f8048dd
  • DataPillarHelpers.tsx:
    • base 443-460 (18 lines) → module 16-33: IDENTICAL sha256 e37505603b438c90
    • base 5135-5202 (68 lines) → module 35-102: IDENTICAL sha256 72733d7c8975ec8b
    • base 5281-5375 (95 lines) → module 104-198: IDENTICAL sha256 963dcd6c0b037958

git diff --color=always --color-moved=zebra 9d1a465f0 HEAD -- ui/packages/app-shell/src/views/studio-design/: 1,678 lines are coloured as moved. Apart from import lines and the three module headers, every line NOT coloured as moved is below: the 38 declarations that gained export (each once without, once with), the two re-export lines with their comments, and the two blank separators of cut blocks.

+export type FlowRuntimeState = Partial<SpecFlowRuntimeState>;
+export type FlowRailState = FlowRuntimeRow & { unboundCause?: string };
+export function flowRailState(s: FlowRuntimeState): FlowRailState {
+export function flowRailStatus(
+export function flowDotClass(status: FlowRunStatus): string {
+export const FLOW_GLYPH_ICON: Record<FlowTriggerGlyph, LucideIcon> = {
+export const FLOW_GLYPH_LABEL_KEY: Record<FlowTriggerGlyph, string> = {
+export const NO_OBJECT_GROUP_KEY = '__no_object__';
+export async function readFlowRegistrations(
+export async function readFlowDefinitions(client: MetadataClient, packageId: string | null): Promise<Map<string, unknown>> {
+export async function readObjectLabels(client: MetadataClient, packageId: string | null, locale: string): Promise<Map<string, string>> {
+export function newFlowTriggerFields(locale: string): { trigger?: FlowConfigField; object?: FlowConfigField } {
+export function withStartTrigger(
+export const NO_OBJECT_ORDER: readonly string[] = [];
+export type FlowCopiedStrip =
+export const FLOW_COPIED_ARRIVAL_STATE = 'flowNewSimilar';
+export function pairIdOf(base: string): string | null {
+export function readFlowCopiedArrival(state: unknown, packageId: string | null): FlowCopiedStrip | null {
+export const OWD_OPTION_LABEL_KEY: Record<OwdCreateModel, string> = {
+export const OWD_OPTION_DESC_KEY: Record<OwdCreateModel, string> = {
+export function addedFieldName(label: string, existing: string[]): string {
+export function addedExtensionFieldName(label: string, existing: string[]): string {
+export function extensionFieldsOf(body: ObjectExtensionBody): Record<string, unknown> {
+export const STUDIO_SYSTEM_FIELD_NAMES = new Set<string>([
+export function formFieldNames(fields: unknown): string[] {
+export function fieldCelStamp(draft: Record<string, unknown>, field: string): string | null {
+export type CelPanelTab = 'rules' | 'actions';
+export const isCelPanelTab = (v: string): v is CelPanelTab => v === 'rules' || v === 'actions';
+export function panelItemOf(draft: Record<string, unknown>, tab: CelPanelTab, item: string): Record<string, unknown> | undefined {
+export function panelItemCelStamp(draft: Record<string, unknown>, tab: CelPanelTab, item: string): string | null {
+export function formPreviewSource(
-const OWD_OPTION_LABEL_KEY: Record<OwdCreateModel, string> = {
-const OWD_OPTION_DESC_KEY: Record<OwdCreateModel, string> = {
-function useHeldEditReport(report: ((clause: string | null) => void) | undefined, clause: string | null): void {
-function confirmDropUnsavedWork(unsaved: boolean, locale: string): boolean {
-async function findTakenIdentifier(
-function CreateTakenNotice({
+// objectstack#29988 — the pillar kit moved to its own module; re-exported for its pins.
+export { StudioHeldNotice, StudioReadOnlyLink, type StudioBaseCustomization };
-function useShowInputFocus(rootRef: React.RefObject<HTMLElement | null>): (input: string | undefined) => void {
-interface StudioSavedDraft {
-function announceDraftSaved(saved: StudioSavedDraft, label: string, locale: string): void {
-
-function addedFieldName(label: string, existing: string[]): string {
-function addedExtensionFieldName(label: string, existing: string[]): string {
-function extensionFieldsOf(body: ObjectExtensionBody): Record<string, unknown> {
-const STUDIO_SYSTEM_FIELD_NAMES = new Set<string>([
-function formFieldNames(fields: unknown): string[] {
-function fieldCelStamp(draft: Record<string, unknown>, field: string): string | null {
-type CelPanelTab = 'rules' | 'actions';
-const isCelPanelTab = (v: string): v is CelPanelTab => v === 'rules' || v === 'actions';
-function panelItemOf(draft: Record<string, unknown>, tab: CelPanelTab, item: string): Record<string, unknown> | undefined {
-function panelItemCelStamp(draft: Record<string, unknown>, tab: CelPanelTab, item: string): string | null {
-function formPreviewSource(
-
-type FlowRuntimeState = Partial<SpecFlowRuntimeState>;
-type FlowRailState = FlowRuntimeRow & { unboundCause?: string };
-function flowRailState(s: FlowRuntimeState): FlowRailState {
-function flowRailStatus(
-function flowDotClass(status: FlowRunStatus): string {
-const FLOW_GLYPH_ICON: Record<FlowTriggerGlyph, LucideIcon> = {
-const FLOW_GLYPH_LABEL_KEY: Record<FlowTriggerGlyph, string> = {
-const NO_OBJECT_GROUP_KEY = '__no_object__';
-async function readFlowRegistrations(
-async function readFlowDefinitions(client: MetadataClient, packageId: string | null): Promise<Map<string, unknown>> {
-async function readObjectLabels(client: MetadataClient, packageId: string | null, locale: string): Promise<Map<string, string>> {
-function newFlowTriggerFields(locale: string): { trigger?: FlowConfigField; object?: FlowConfigField } {
-function withStartTrigger(
-const NO_OBJECT_ORDER: readonly string[] = [];
-type FlowCopiedStrip =
-const FLOW_COPIED_ARRIVAL_STATE = 'flowNewSimilar';
-function pairIdOf(base: string): string | null {
-function readFlowCopiedArrival(state: unknown, packageId: string | null): FlowCopiedStrip | null {
+// objectstack#29988 — the Automations rail's flow helpers moved to their own module; re-exported for their pins.
+export { FlowStatusDot, type NewFlowTrigger };
+export function useHeldEditReport(report: ((clause: string | null) => void) | undefined, clause: string | null): void {
+export function confirmDropUnsavedWork(unsaved: boolean, locale: string): boolean {
+export async function findTakenIdentifier(
+export function CreateTakenNotice({
+export function useShowInputFocus(rootRef: React.RefObject<HTMLElement | null>): (input: string | undefined) => void {
+export interface StudioSavedDraft {
+export function announceDraftSaved(saved: StudioSavedDraft, label: string, locale: string): void {
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 702 base .d.ts files, 699 are byte-identical at head. The three that differ:

  • chrome/ErrorBoundary.d.ts and console/ai/outboundAgentText.d.ts: the members of one union print in another order (render()'s return union; Record<"en" | "zh", string> against Record<"zh" | "en", string>), program-order type creation; with union members sorted (the ruling's normalisation) each pair is identical.
  • views/studio-design/StudioDesignSurface.d.ts (base sha256 d0f9665b1e9ed6fc…): the moved declarations now come from the three new .d.ts files, and the five names it exported before are re-exported (export { FlowStatusDot, type NewFlowTrigger }, export { StudioHeldNotice, StudioReadOnlyLink, type StudioBaseCustomization }). Every declaration chunk of the base file appears verbatim in the head file or in one of AutomationsFlowRail.d.ts, StudioPillarKit.d.ts, DataPillarHelpers.d.ts; the one chunk that does not is the file's header comment with its import block, whose import lines are what changed.

The three new .d.ts / .js pairs are reachable from no entry in the package's exports map ("." only). Declared-surface digest of dist/index.d.ts plus StudioDesignSurface.d.ts: 245 exports on both sides. Read with the declaring file of a type masked (the printer qualifies StudioBaseCustomization, StudioSavedDraft, FlowRailState and NewFlowTrigger by the file that declares them, which is now the new module), the two dumps are identical (6e1f795576824163…); unmasked, they differ in exactly those 7 qualified names.

Build, type-check, lint and tests (head ec616a8)

All under scripts/pm/os-verify-lock.sh, one locked chain at 16500092 (ui/, docs/qa/ and the changeset are identical at head ec616a8, checked with git diff --quiet): 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 four source files and the 7373 test, from packages/app-shell: exit 0 (warnings only: react-refresh/only-export-components on the new modules' function exports, as on the file before). The first cut, which also moved renderStudioGridList, failed here with one error (an unused eslint-disable directive in DataPillarHelpers.tsx); that is why it stays.
  • 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, spec-symbol-parity and spec-symbol-ledger-app-shell-7265: exit 0, Test Files 330 passed (330), Tests 2490 passed (2490).
  • 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 a moved helper. 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)

Each new module gets two legs per reader: a planted needle in the module (expected red), then the same needle with the module taken out of the reader's list (expected green, i.e. blind).

Leg Mutation Expected Observed
B0 none: both readers as in this PR green 7373 exit 0, 15 passed; 7265 exit 0, 25 passed
AutomationsFlowRail.tsx · 7373 a '/home' literal planted in AutomationsFlowRail.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
AutomationsFlowRail.tsx · 7373b same mutation, AutomationsFlowRail.tsx removed from the Studio site's alsoReads green, i.e. blind exit 0: 15 passed
AutomationsFlowRail.tsx · 7265 a local interface ObjectLike planted in AutomationsFlowRail.tsx, SITES as in this PR red exit 1: "rule 1 finds no spec-named local declaration in …AutomationsFlowRail.tsx" fails; 1 failed, 24 passed
AutomationsFlowRail.tsx · 7265b same mutation, the AutomationsFlowRail.tsx SITES row removed green, i.e. blind exit 0: 24 passed
StudioPillarKit.tsx · 7373 a '/home' literal planted in StudioPillarKit.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
StudioPillarKit.tsx · 7373b same mutation, StudioPillarKit.tsx removed from the Studio site's alsoReads green, i.e. blind exit 0: 15 passed
StudioPillarKit.tsx · 7265 a local interface ObjectLike planted in StudioPillarKit.tsx, SITES as in this PR red exit 1: "rule 1 finds no spec-named local declaration in …StudioPillarKit.tsx" fails; 1 failed, 24 passed
StudioPillarKit.tsx · 7265b same mutation, the StudioPillarKit.tsx SITES row removed green, i.e. blind exit 0: 24 passed
DataPillarHelpers.tsx · 7373 a '/home' literal planted in DataPillarHelpers.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
DataPillarHelpers.tsx · 7373b same mutation, DataPillarHelpers.tsx removed from the Studio site's alsoReads green, i.e. blind exit 0: 15 passed
DataPillarHelpers.tsx · 7265 a local interface ObjectLike planted in DataPillarHelpers.tsx, SITES as in this PR red exit 1: "rule 1 finds no spec-named local declaration in …DataPillarHelpers.tsx" fails; 1 failed, 24 passed
DataPillarHelpers.tsx · 7265b same mutation, the DataPillarHelpers.tsx SITES row removed green, i.e. blind exit 0: 24 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; the tree was restored after every module (git diff HEAD empty).

Derived gates: node scripts/pm/dispatch-gates.mjs --commands on head ec616a8 (42 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-adr-links.mjs :: exit 0
node scripts/check-adr-links.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/ci/plan-test-core-shards.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/lint run check:doc-formula-expressions :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: 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:doc-authoring :: 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:platform-checklist :: 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: "42 derived famil(ies) accounted for — 42 run, 0 NOT-MEASURED".

Serial check before push

The 19 open PRs at 2026-11-04T08:05Z, each PR's changed files read: the ten branch PRs (#30021, #30015, #30012, #30010, #30004, #29998, #29993, #29984, #29968, #29963) from their fetched heads against origin/main, three-dot, re-fetched for the two updated since the first pass; #29979, #29866 and the seven pm:mailbox PRs through the PR file list, none updated since it was read (every mailbox PR lists no file). None touches StudioDesignSurface.tsx, AutomationsFlowRail.tsx, StudioPillarKit.tsx, DataPillarHelpers.tsx, homeRecoveryRedirectsFollowDeclaration-7373.test.ts, spec-symbol-ledger-app-shell-7265.test.ts or studio-authoring.json.


Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01WGufzedfGwVcebpN5t3ETE

…ers out of StudioDesignSurface.tsx

Move-only (ruling #24586 A), seam row 4: the flow runtime and rail states,
FlowStatusDot with its glyph tables, the flow, definition and object-label
reads, the new-flow trigger helpers, pairIdOf and the copied-flow arrival
reader move byte for byte into views/studio-design/AutomationsFlowRail.tsx.
Only imports and `export` change; FlowStatusDot and NewFlowTrigger are
re-exported from StudioDesignSurface.tsx, so their importers keep working.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufzedfGwVcebpN5t3ETE
… StudioDesignSurface.tsx

Move-only (ruling #24586 A), seam row 6: the create dialog's OWD option keys,
the added-field names, the system field names, the CEL panel stamps and
formPreviewSource move byte for byte into
views/studio-design/DataPillarHelpers.tsx. Only imports and `export` change.
renderStudioGridList stays where it is: its eslint-disable line for
react-refresh/only-export-components would be an unused directive (an error)
in a module that exports no component, so moving it unchanged is not possible.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufzedfGwVcebpN5t3ETE
…tudioDesignSurface.tsx

Move-only (ruling #24586 A), seam row 5: useHeldEditReport,
confirmDropUnsavedWork, findTakenIdentifier, CreateTakenNotice,
StudioHeldNotice, the "Show me" focus helpers with their section comment,
announceDraftSaved, StudioReadOnlyLink and StudioBaseCustomization move byte
for byte into views/studio-design/StudioPillarKit.tsx. Only imports and
`export` change; StudioHeldNotice, StudioReadOnlyLink and
StudioBaseCustomization are re-exported from StudioDesignSurface.tsx, so
their importers keep working.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufzedfGwVcebpN5t3ETE
… sources at the slice 4 modules

The 7373 Studio site's alsoReads and the 7265 spec-symbol ledger's SITES gain
AutomationsFlowRail.tsx, StudioPillarKit.tsx and DataPillarHelpers.tsx. Four
studio-authoring checklist sources that named moved helpers now name the
module that holds them, each with a revision row; no clause changed.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WGufzedfGwVcebpN5t3ETE
@github-actions github-actions Bot added size/xl 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

维护者速读(席位终稿)

  • 改了什么:剩下三块低风险代码原样搬进三个新文件:「自动化」栏的流程侧栏辅助、各栏共用的小工具、「数据」栏的辅助函数。StudioDesignSurface.tsx 从 10,757 行降到 9,928 行,低于 1 万行,本 PR 结卡 [maintainer] refactor(app-shell): split StudioDesignSurface.tsx (12,882 lines) along its component seams, one move-only slice per PR #29988。
  • 为什么改:你问过一刀 1,000 行是否值得,所以把剩下三块合成这一个 PR 收尾,不再继续切。整页级的四个栏位(表中第 7–10 行)不搬,留给以后的卡。
  • 风险与回滚:极低。没有行为变化,八个代码块逐行一致,只改了 import 和 export。主文件再导出原来对外的五个名字,已有 import 不受影响。回滚就是 revert 本 PR。
  • 先看:主文件顶部从三个新模块导入的部分,以及两行再导出。
  • 两分钟验证:按 PR 正文 Verification 的比对命令运行,没有输出即一致。席位已按顺序复核过。
  • 需要你做的:无。这是非受管面,CI 全绿后由席位落地。
  • 席位意见:ACCEPT。

席位核对 · ACCEPT · head ec616a8a0 · 2026-11-04T08:27Z

  1. Shape: draft, base main 63b86b38. Attribution block, then Fixes #29988: the card's bar is met at 9,928 lines (wc -l at the head, re-read by the seat). One commit per row plus one reader commit. One changeset (@object-ui/app-shell patch, internal move-only). 8 files, +1048/−893. check-governed-merges --pr 30027: NOT governed, 1,941 lines ≤ 3,000. No fallback trigger was hit.
  2. Move-only, re-measured by the seat per module:
    • AutomationsFlowRail.tsx (row 4) and StudioPillarKit.tsx (row 5): with export stripped, their lines occur in StudioDesignSurface.tsx's removed lines in the same order.
    • DataPillarHelpers.tsx (row 6): two contiguous runs of 19 and 164 lines, each found verbatim in the removed text. The module orders the two blocks differently from the base file. Moving whole blocks is still move-only.
    • Every other line that differs is an import, a module header, or export.
    • The surface file gains the three imports, customizationNamespace / usePackageCustomizations narrowed from their former combined imports, two re-export lines (5 names, so imports through ./StudioDesignSurface keep working), and the Automations pillar — flows section comment, which keeps its text and now sits above the pillar it names.
  3. Deviation, accepted: renderStudioGridList (row 6, 76 lines) stays in the file. Its react-refresh eslint-disable line would become an unused directive in a module that exports no component, and eslint reports that as an error. Moving it would mean dropping that line, which is not move-only, and the bar is met without it.
  4. Readers and checklist: 7373 alsoReads and 7265 SITES gain the three modules. All 12 ablation legs came out as expected. Four studio-authoring.json sources are re-pointed with revision rows, and check-platform-checklist exits 0 full and with --changed-from.
  5. Gates (dev, local at the head): 330 test files / 2,490 tests green; 42/42 derived gates exit 0. .d.ts: 699/702 identical; two differ only by union order, and StudioDesignSurface.d.ts carries the re-exports; the surface digest is equal once the declaring file is masked. 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 and follow to MERGED. Fixes #29988 closes the card. The seat then posts the landing record and removes pm:dispatched. Rows 7–10 stay in the seam table in this PR's body as "not moved; available to a later card".

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[maintainer] refactor(app-shell): split StudioDesignSurface.tsx (12,882 lines) along its component seams, one move-only slice per PR

1 participant