Repository navigation
Commit c618e89
[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)
<!-- claude-projects-attribution: {"github_login":"os-support-ai"} -->
_Requested by **Yibo Wang** · [project
thread](https://claude.ai/code/project/chan_01BVUyyPie8gHwoPM18iABwB?thread=cmsg_01BVUyyPie8gHwoPM18iABwBCE4YXYCrA6G1yrthEDVvNJ)_
Fixes #29988
## 维护者速读
- 改了什么:把 Studio 三组辅助代码原样搬进同目录三个新文件——自动化栏的流程辅助(347 行)进
`AutomationsFlowRail.tsx`,各支柱共用的工具集(335 行)进
`StudioPillarKit.tsx`,数据支柱的辅助(181 行)进 `DataPillarHelpers.tsx`;主文件 10,757
→ **9,928 行**,低于 1 万,本卡完成。
- 为什么改:#29988 按裁定 #24586 A 只搬不改,把这个文件拆到 1 万行以下;维护者问 1,000
行一片是否值得,席位决定最后一片合并接缝表第 4–6 行一次做完。第 7–10 行(整个支柱组件)不搬,留给以后的卡。
- 风险与回滚:无行为变化,八段搬走的代码逐字节一致,只多了 import 和 `export`;原来从
`StudioDesignSurface`
导出的五个名字在原处重新导出,所有引用照常工作;对外导出摘要两边一致(只是类型的声明文件变了);回滚即 revert 本 PR。
- 先看:三个新文件的 import 段;`StudioDesignSurface.tsx` 的 import
段和三行重新导出;`studio-authoring.json` 四处出处改动与新加的 history 行。
- 两分钟验证:在本分支跑下方「Verification」里的 `cmp` 命令组,预期全部无输出(逐字节相同)。
- 需要你做的:无,席位落地。
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 #30014
merged and is rebased on `63b86b38`, which touches only `packages/rest/`
and its changeset.
## Verification
<details><summary>Move-only proof: byte-compare of all eight blocks and
the moved-lines diff</summary>
```
B=ui/packages/app-shell/src/views/studio-design
base() { git show 9d1a465:$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 9d1a465 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 {
```
</details>
<details><summary>Declaration files (ruling 6348891160's bar)</summary>
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.
</details>
<details><summary>Build, type-check, lint and tests (head
ec616a8)</summary>
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.
</details>
<details><summary>Ablation of the two extended readers (one-time,
through scripts/ablation-replace.mjs; tree restored, git diff HEAD
empty)</summary>
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).
</details>
<details><summary>Derived gates: node scripts/pm/dispatch-gates.mjs
--commands on head ec616a8 (42 commands)</summary>
```
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".
</details>
<details><summary>Serial check before push</summary>
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.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01WGufzedfGwVcebpN5t3ETE)_
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01WGufzedfGwVcebpN5t3ETE
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 140d8ce commit c618e89
8 files changed
Lines changed: 1048 additions & 893 deletions
File tree
- .changeset
- docs/qa/platform-checklist/areas
- ui
- packages/app-shell/src
- __tests__
- views/studio-design
- scripts/__tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2118 | 2118 | | |
2119 | 2119 | | |
2120 | 2120 | | |
2121 | | - | |
| 2121 | + | |
2122 | 2122 | | |
2123 | 2123 | | |
2124 | 2124 | | |
| |||
2177 | 2177 | | |
2178 | 2178 | | |
2179 | 2179 | | |
2180 | | - | |
| 2180 | + | |
2181 | 2181 | | |
2182 | 2182 | | |
2183 | 2183 | | |
| |||
2213 | 2213 | | |
2214 | 2214 | | |
2215 | 2215 | | |
| 2216 | + | |
| 2217 | + | |
| 2218 | + | |
| 2219 | + | |
| 2220 | + | |
| 2221 | + | |
2216 | 2222 | | |
2217 | 2223 | | |
2218 | 2224 | | |
| |||
2305 | 2311 | | |
2306 | 2312 | | |
2307 | 2313 | | |
2308 | | - | |
| 2314 | + | |
2309 | 2315 | | |
2310 | 2316 | | |
2311 | 2317 | | |
| |||
2365 | 2371 | | |
2366 | 2372 | | |
2367 | 2373 | | |
2368 | | - | |
| 2374 | + | |
2369 | 2375 | | |
2370 | 2376 | | |
2371 | 2377 | | |
| |||
2380 | 2386 | | |
2381 | 2387 | | |
2382 | 2388 | | |
| 2389 | + | |
| 2390 | + | |
| 2391 | + | |
| 2392 | + | |
| 2393 | + | |
| 2394 | + | |
2383 | 2395 | | |
2384 | 2396 | | |
2385 | 2397 | | |
| |||
2567 | 2579 | | |
2568 | 2580 | | |
2569 | 2581 | | |
2570 | | - | |
| 2582 | + | |
2571 | 2583 | | |
2572 | 2584 | | |
2573 | 2585 | | |
| |||
2628 | 2640 | | |
2629 | 2641 | | |
2630 | 2642 | | |
2631 | | - | |
| 2643 | + | |
2632 | 2644 | | |
2633 | 2645 | | |
2634 | 2646 | | |
| |||
2645 | 2657 | | |
2646 | 2658 | | |
2647 | 2659 | | |
| 2660 | + | |
| 2661 | + | |
| 2662 | + | |
| 2663 | + | |
| 2664 | + | |
| 2665 | + | |
2648 | 2666 | | |
2649 | 2667 | | |
2650 | 2668 | | |
| |||
3720 | 3738 | | |
3721 | 3739 | | |
3722 | 3740 | | |
3723 | | - | |
| 3741 | + | |
3724 | 3742 | | |
3725 | 3743 | | |
3726 | 3744 | | |
| |||
3793 | 3811 | | |
3794 | 3812 | | |
3795 | 3813 | | |
3796 | | - | |
| 3814 | + | |
3797 | 3815 | | |
3798 | 3816 | | |
3799 | 3817 | | |
| |||
3841 | 3859 | | |
3842 | 3860 | | |
3843 | 3861 | | |
| 3862 | + | |
| 3863 | + | |
| 3864 | + | |
| 3865 | + | |
| 3866 | + | |
| 3867 | + | |
3844 | 3868 | | |
3845 | 3869 | | |
3846 | 3870 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
140 | 140 | | |
141 | 141 | | |
142 | 142 | | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
143 | 146 | | |
144 | 147 | | |
145 | 148 | | |
| |||
0 commit comments