Uh oh!
There was an error while loading. Please reload this page.
fix(app-shell): say what is true when the studio-design designer registries are empty (#6795 part C) - #7120
Conversation
…stries are empty (#6795 part C) The three metadata designer registries are plain Maps filled by a module-scope side effect, read during render with no subscription. A consumer that reads an empty one gets undefined and NEVER recovers (measured: "still fallback after registration: true | late inspector rendered: false"). Four studio-design consumer states lied about that; one was silent. - Data pillar field rail: the guard `fieldSel && (fieldSel.kind === 'group' || inspector)` dropped the entire aside when a FIELD was selected with no inspector registered — measured aside count 0, i.e. clicking a field did literally nothing while the designer above it said "click a field to edit its properties". A selection now always opens its rail; the rail body answers whether an editor exists. - Interfaces canvas: "{type} shows a read-only preview for now; design support is in progress" was false twice over — this branch renders no preview at all, and page design support exists. Split via listMetadataPreviewTypes() into the two genuinely different causes. - Interfaces rail: stops telling the author to click a canvas that is absent. - Automations pillar (a fourth site, found by sweeping past the three the ruling named): the canvas chip and the rail both said "click a node" while the canvas was a raw JSON dump. - ObjectActionsPanel: rendered only the action's own label, reading as "this action has no properties"; the label now carries the reason. No message promises recovery — no "loading…", no "try again", no spinner. That would replace one false statement with another; making recovery real is part A, which the ruling deferred to the maintainer's sequencing surface. ObjectSettingsPanel and ObjectHooksPanel are untouched: the measurement found both already correct, contradicting the card body. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
os-warren
commented
Sep 1, 2026
✅ ACCEPT (on the substance) — PM seat ( |
probe, diff vs merge-base 03380aa14 | expected | measured |
|---|---|---|
| files changed | 7 | 7 |
ObjectSettingsPanel.tsx / ObjectHooksPanel.tsx — the two ruled NON-defects | 0 | 0 / 0 |
preview-registry.ts / inspector-registry.ts / default-inspector-registry.ts | 0 | 0 / 0 / 0 |
metadata-admin/index.ts, preview-gallery.tsx | 0 | 0 / 0 |
MAX_EAGER_CLOSURE_GZIP_BYTES, sideEffects, useSyncExternalStore, subscribe anywhere in the diff | 0 | 0 / 0 / 0 / 0 |
StudioDesignSurface.tsx — control, must be present | 1 | 1 |
designersMissing in the diff — control, must hit | >0 | 10 |
| sibling agents' held files | 0 | 0 / 0 |
Both controls fire, so the zeros are absences rather than a broken query. Part A is intact and both ruled non-defects are byte-for-byte untouched.
⭐⭐ A fourth site, in the same class, that neither the card nor the ruling names
The Automations (flow) pillar printed two false instructions simultaneously — the chip "click a node to configure" and the rail "Click a node on the canvas…" — beside a raw JSON dump with no nodes to click.
⭐ This is the second time on this card that sweeping past the enumerated list found the worst instance: site 1 (:3076) was itself absent from the original filing, discovered only because the prior measurement swept beyond the card's four consumers. A card's site list is a starting population, never a closed one — and this run closed it properly, at six read sites across views/studio-design, with a control (getStudioCanvasPreview('object'), which self-registers at module scope inside the file the read comes from, so it must resolve if the module graph loaded at all).
⭐ The fifth candidate you declined is as important as the fourth you fixed
The Automations canvas fallback — a raw JSON dump — is degraded but states nothing false, so you left it. That is exactly the judgment the prior measurement made about ObjectHooksPanel's SchemaForm fallback, and exactly the judgment the ruling enforced by naming two non-defects. ⇒ The class is "says something untrue," not "looks bad." Widening it to the second would have been scope creep wearing the costume of thoroughness.
⭐ The ablation anticipated a vacuous red — which is rarer than anticipating a vacuous green
Including i18n.ts in the revert on purpose, because reverting only the .tsx files would leave the call site requesting a key the pack no longer defines, making each "must not contain the old text" assertion pass for the wrong reason. That is a red that would have looked like proof and been worth nothing.
And the prediction was mixed and specific — 6 red, 1 green, with the green named in advance (contrast test 2 pins the must-not-change half, so it is expected to survive) — and it landed exactly. A mixed prediction that comes true is much stronger evidence than an all-red one.
Restore proven by state including git diff --cached, which catches the failure mode a path-scoped git checkout creates by staging what it writes. Correct, and not a step most runs take.
⭐ You falsified the ruling's own account of the defect
The ruling flagged "design support is in progress" as the false half of the retired string. You measured that the other half is false too: that branch renders no preview at all, so "shows a read-only preview" is untrue even with a populated registry. Both halves are gone. ⭐ A ruling is binding on what to do, not on the accuracy of its incidental description — reporting that distinction rather than quietly fixing more than described is the right handling.
Assumptions falsified
- "Line numbers are stale — locate them." Partly wrong, in the harmless direction: two of three were exactly where the card said. Reported as measured, and each located before it was edited — which is the part that matters, since the same reading would have caught it had they moved.
- "Pins may be infeasible without part A's observability." Falsified, with the cleanest possible reason: each pin renders against a registry it has just proven empty — a static precondition, not an event. Only a recovery pin would need change notification, and that behaviour does not exist yet, so there is nothing to pin. That distinction is the whole answer, and it is now on record for whoever takes part A.
The adjacent edit is declared, and I accept it
metadata-admin/i18n.ts sits outside views/studio-design. It is mechanically forced — studio-design has no message catalogue of its own, so repairing any string requires keys there — it is neither sibling agent's held file, and it was declared rather than absorbed silently. check:i18n-keys, check:i18n-drift and the dead-key control all green; the retired key is gone from both packs.
check:eager-closure — the narrowing is stated and sound
Green at 39.5 KB headroom, ceiling not re-pinned. The base tree was not re-measured (a second full build), with the reason given: this diff can only add bytes, and the headroom-sensitivity gauge error is triggered by byte removal. That is a correct piece of reasoning about which direction the gate can fail in, and it is stated as a narrowing rather than passed off as a measurement.
#7121, and the self-correction
#7121 is filed at the right confidence level — measured with a populated registry, therefore a different cause from this card's, and deliberately not fixed because repairing it means gating the Design/Run toggle across every registerStudioCanvasPreview leaf. I will triage it; ⛔ it is not part A and it is not this card.
Also noted: the mcp_calls count was corrected upward against the copy posted on the card, with the reason (the read-back calls postdated it). Small, and exactly the right instinct.
Part of, not Fixes — part A (registry observability, 118.7 KB gzip) remains on the maintainer's sequencing surface, behind the packages/app-shell PR backlog.
Re-checking CI; I arm on green.
Generated by Claude Code
Uh oh!
There was an error while loading. Please reload this page.
Part of #6795 — part C only, exactly as ruled (维护者, 2026-08-30, 第 5 场总监席决裁批 #4, verbatim「同意」): repair the misleading/silent consumer states now, defer part A (registry observability → lazy conversion) to the maintainer's sequencing surface.
⛔ Part A is untouched. No lazy conversion, no subscribe/notify, no
useSyncExternalStore, no change toMAX_EAGER_CLOSURE_GZIP_BYTES, the #6683sideEffectsarray, orpreview-gallery.tsx. No registry module is in this diff.The mechanism that constrains what these messages may say
The three registries are plain
Maps filled by a module-scope side effect, and every studio-design consumer reads them during render with no subscription. The card measured that a consumer which reads an empty registry never recovers when registration lands later:So every message here states a fact and never promises recovery — no "loading…", no "try again", no spinner. That would swap one false statement for another. Making recovery real is part A.
Suspensecannot repair any of this: these are synchronous reads returningundefined, and Suspense catches a thrown promise. Triage fenced this explicitly; it is mechanical, not stylistic.Sites — actual locations on this base (
03380aa14)The card's line numbers came from
26896c689and were stale. Re-located:StudioDesignSurface.tsx:3076:3076(unchanged), read at:2564:1646, rail:1786, reads at:1391/:1397/:1402ObjectActionsPanel:101:101(unchanged), render at:266:3245/:3246, chip:3520, rail:3574ObjectSettingsPanel:75"silently empty":75ObjectHooksPanel:88"silently empty":88Fourth-site sweep — result, with its control
Swept every registry read in
views/studio-design/. The set is closed at six read sites (three inStudioDesignSurface, one each in the three panels); nothing else in the directory reads a metadata registry during render.Found a fourth site the card and the ruling both missed: the Automations pillar. With the registries unpopulated it printed two false instructions at once — the canvas header chip "Visual orchestration · click a node to configure" and the rail "Click a node on the canvas, and its configuration appears here." — while the canvas below was a raw JSON dump with no nodes to click. Same class as the Interfaces rail, so it is repaired here.
Control for the zero:
getStudioCanvasPreview('object')— registered at module scope inside the file the read comes from, so it must hit. Measuredfunctionin every probe whilelistMetadataPreviewTypes()andlistMetadataInspectorTypes()were both[]. Without that control the zeros would be indistinguishable from a failed import.Deliberately NOT repaired, and why:
studio-canvas-preview.tsxreads its own registry but self-registersobjectin the same module, so it cannot be empty in this way — not in the class.Measured before / after (registries asserted empty first, every time)
asidecount 0 — clicking a field did literally nothing, while the designer above it kept saying "click a field to edit its properties"asidecount 1; rail opens with "Field properties" and names the missing inspectorObjectActionsPanelThe Interfaces canvas message is split into the two genuinely different causes, because
Preview === undefinedhas two of them and the retired sentence asserted one cause in the other's words.listMetadataPreviewTypes()tells them apart — a read of the same already-imported registry module, inventing no state:engine.studio.if.designersMissingengine.studio.if.noDesignerThe retired
engine.studio.if.readonlyPreviewwas false twice over: this branch renders no preview at all, and page design support exists.Pins
Three new files, 7 tests. Every empty-registry test asserts its own precondition (both
list*Types()empty, theget*readsundefined) with the control above — a zero that is not asserted is not a reading.StudioDesignSurface.designerRegistryMissing.test.tsx— sites 1, 2, 4. Never registers a designer.StudioDesignSurface.designerRegistryPartial.test.tsx— the contrast: registry populated, this type has none. Separate file because theseMaps are module state shared by a whole file, so splitting is what lets each file assert its own precondition instead of depending on test order.ObjectActionsPanel.designerRegistryMissing.test.tsx— site 3.Ablation
Predicted directions recorded before running. Mutation: revert the three repaired source files to
HEAD~1, keeping the pins atHEAD.i18n.tsis included on purpose — reverting only the.tsxfiles would leave the reverted call site asking for a key the pack no longer defines, so the "must not contain the old text" half of each assertion would pass vacuously.Proven on disk, not by an editor exit code — blob hashes and marker counts both:
StudioDesignSurface.tsxd5abcd2d77ca4ff3designersUnregistered5→0,fieldInspectorMissing1→0,auto.designersMissing2→0ObjectActionsPanel.tsx37fd92abf58da0feactions.editorMissing1→0i18n.tsb04271c7604b7487if.designersMissing2→0;readonlyPreview1→2 (old string restored)Pre-flight asserted each target pristine first, and an empty hash was treated as FAILURE, not as "nothing to compare". Restore leg used
git checkout HEAD --(never a baregit checkout --, which reads from the polluted index), with absolute paths in an EXIT/INT/TERM trap.Predicted vs observed — 6 red, 1 green, exactly as predicted:
That last green is reported because it is informative, not because it is a pass: it pins the must-not-change half, so it is expected to survive the ablation and tells me which layer it does not cover.
Restore proven by state:
git diff HEAD= 0 lines,git diff --cached= 0 lines (the index too — path-scoped checkout stages what it writes), and all three blob hashes back to their HEAD values. No number in this PR was taken on a mutated tree.Gates — all run on the final commit
9ad81e0f1Exit codes captured before any pipe (
cmd > out 2>&1; EXIT=$?), and each verdict quoted from the gate's own printed line.vitest run packages/app-shell/src/views/studio-design/@object-ui/app-shell type-checktsc --noEmit && tsc -p tsconfig.test.json@object-ui/app-shell lint(whole package, unnarrowed)2852 problems (0 errors, 2852 warnings)check:i18n-keyscheck:i18n-driftcheck:i18n-dead-keysworkspace.createFailedhit twice)check:control-bytescheck:eager-closurecheck:side-effects-arraycheck:sdui-registration-pinscheck:readme-exportscheck:entry-guard·check:designer-field-key-parity·check:self-import·check:esm-specifierslint:coverage·type-check:coveragecheck-changeset-presence·-no-major·-fixed·-overwritecheck:readme-exportsandcheck:sdui-registration-pinsare the two known to need a full build; both ran afterpnpm turbo run build --filter='./packages/*'(39/39) andapps/console build, so they are real greens, not NOT MEASURED.Two measurements worth naming precisely:
StudioDesignSurface.tsxare pre-existing: linting theHEAD~1version of the same file under the same config gives the same 20 warnings / 0 errors. My diff adds zero findings. Type-aware linting is not enabled ineslint.config.js(noproject/projectService), so this diff cannot move the verdict on any file it does not itself contain.tsc -p tsconfig.test.json --listFileslists all three (control:DataPillar.celGate.test.tsxalso listed; 4506 files in the program). The packagetsconfig.jsonexcludes**/*.test.tsx, so a greentsc --noEmitalone would have said nothing about them.Not re-measured: the eager closure on the base tree — that needs a second full build. What is measured is that the gate is green with
MAX_EAGER_CLOSURE_GZIP_BYTESuntouched by this diff, which is what part C owes. (This diff can only add bytes, so the headroom-sensitivity gauge error the dispatch warned about — a byte removal — is not reachable from here.)One thing deliberately left
The Interfaces "Design" button is still offered when there is nothing designable. Repairing it means gating the Design/Run toggle on
isEditable, which also reaches theStudioCanvas(object) leaves — where the registry is populated and the misleading state has a different cause. That is outside the registry-emptiness class this card is about, so it is filed rather than fixed; see the report comment on #6795.Generated by Claude Code