Repository navigation
feat(app-shell): one save model in Studio — the package door's matrix and hooks autosave, one create label, a Changes count that keeps up (objectui#11787) - #12023
Merged
Conversation
…ule (objectui#11787) `useDraftAutoSave`, `DraftSend` and `draftSnapshotKey` move out of StudioDesignSurface.tsx unchanged, so the package door's permission matrix and the hooks panel can run the same autosave as the pillars. The matrix cannot import the surface: the surface imports the matrix. Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
… and hooks autosave, one create label, a Changes count that keeps up (objectui#11787) - The permission matrix autosaves to the package draft on the package door through the shared Studio autosave, with the pillars' status line in place of Save; the environment-admin door keeps its explicit Save (ADR-0086 D7). The api name is fixed on the package door, since the draft is stored under it. - A hook autosaves to its draft; a blocking CEL verdict holds it. "Save hook" is gone, and hook saves and "+ New" report to the surface's count. - New permission set says "Save as draft" like every other create dialog. - A draft save asks for a pending-drafts read of its own, never joined to one already in flight, and the header counts at least that draft until it answers. Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
…f casting to any (objectui#11787) Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
Contributor
✅ 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
|
…hook instead of staging one per pause (objectui#11787) The hook inspector commits the name on every keystroke and the panel stores the draft under that name, so an autosaved rename staged one more hook draft per pause while typing (probe on bfe6edc: hook/audit and hook/audit_trail beside guard_hook). As with the package door's permission-set api name, the name is set once by "+ New": while the buffer's name differs from the hook it holds, the autosave is held and a line says to change it back. Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
Contributor
✅ 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
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #11787
Clause-②: no
What changes
One save model in Studio, per triage's direction (option A on the package door only):
packageIdset; the Studio Access pillar). An edit autosaves to the package draft through the same autosave the Data and Interfaces pillars run (useDraftAutoSave, 1.5s after the last edit). The Save button gives way to the pillars' status line: Saving…, then Saved with the time, or the CEL refusal sentence while a CEL syntax error holds the edit. An edit taken while a save is in flight is kept and sent next (the objectui#11204 rule). The api name input is disabled on this door: the autosave stores the draft underdraft.name, so an editable name would stage a new set at every pause while typing. "+ New" sets it.packageId; the metadata admin route). Unchanged: explicit Save, live write (ADR-0086 D7). The autosave isblockedthere for good.ObjectHooksPanel). A hook autosaves to its draft the same way; Save hook is gone. A blocking CEL verdict holds the autosave (the old button's disabled condition) and the panel says why. A save writes the panel's list in place instead of re-reading it (a re-read blanked the list to "Loading…" at every pause), and the buffer install skips an unsent edit of the same hook. A hook keeps the name it was created with: while its name differs from that, the autosave holds and a line says to change it back. The draft is stored under its name, and the inspector commits the name on every keystroke, so a rename the autosave sent would stage one more hook per pause (commit5e5adb69, after the seat's review). Hook saves and "+ New" now callonDraftSaved; before, they never refreshed the Changes count.engine.studio.createDraft) like the others.refresh({ afterSave: true })on the shared pending-drafts hook: that read is sent fresh, never joined to a_draftsread already on the wire (objectui#11797's sharing would answer it with a pre-save count), andbehindSaveholds until a read sent after the save answers. The header counts at least one whilebehindSaveholds, so "No drafts pending publish" never shows in the same render as an item's "Unpublished draft" chip.useDraftAutoSave/DraftSend/draftSnapshotKeymoved out ofStudioDesignSurface.tsxunchanged, intostudio-design/useDraftAutoSave.ts, because the matrix cannot import the surface: the surface imports the matrix, and the metadata-admin route loads the matrix without the surface. Three en+zh row pairs inmetadata-admin/i18n.ts(engine.studio.create,engine.studio.hooks.save,engine.studio.hooks.saved) are deleted: this diff removed their last readers.File surface. The claim (
6072832674) plus its amendment6073736188, which adds the newuseDraftAutoSave.tsmodule, the moved block's removal fromStudioDesignSurface.tsx, and the three i18n row-pair deletions. No export, prop, type or language-pack key of the package entry changes:src/index.tsnames none of the changed internals, and the exports map is.and./styles.cssonly.Premise checks (measured on
origin/mainf0268ad78)packageId: set only by the Studio Access pillar's embed; the metadata-admin registry renders the page withtypeandnameonly.useDraftAutoSave, module-private inStudioDesignSurface.tsxbefore this change. It is reused, not duplicated.HookTargetScopeContext); the condition-hold is the validation rule's (ObjectValidationsPanel.newRuleWaits-11820). A hook'sconditionis optional. The only hold a hook had is the CEL verdict that disabled Save hook, and that is what holds the autosave now._draftsread sent before the save. Every other draft save path already calledonDraftSaved.Create dialogs and their labels
The five
CreateItemDialogcall sites at this head, all inStudioDesignSurface.tsx, now submit withengine.studio.createDraft: Create app, Interfaces create (page / dashboard / report), New object, New automation, and New permission set (the one that said Create).CreateItemDialog.oneLabel-11787.test.tsenumerates the call sites from source, so a dialog added later is asked too. Not aCreateItemDialog: New package (PackageFormDialog) creates a live package record, not a draft, and keeps its label.Tests
At head
bfe6edc13, under the shared verify lock:pnpm exec vitest run --maxWorkers=2 packages/app-shell/gaveTest Files 1217 passed | 1 skipped (1218),Tests 11780 passed | 9 skipped (11789);pnpm --filter @object-ui/app-shell type-checkexit 0 (its test project lists all five new test files). Gates run locally, each exit 0: eslint on the changed files (0 errors, no new warning against the base),check:control-bytes,check:test-path-roots,check:changeset-claims,check:pending-changeset-literals,check:i18n-keys,check:i18n-drift,check:i18n-designer-parity,check:new-line-citations, the threecheck:vi-mock-*,check:unreferenced-sources,check:metadata-write-doors,check:phantom-deps,check-changeset-presence. Not measured locally: the eager-closure budget (it needs a full console build); CI's Bundle Analysis reads it.New pins:
PermissionMatrixEditor.autosave-11787(package door autosaves; environment door sends nothing after 2.2s, then its Save writes live; in-flight edit kept; name fixed on the package door only),ObjectHooksPanel.autosave-11787(autosave,onDraftSavedon save and "+ New", in-flight edit kept, a renamed hook held and saved again once its name is restored),ObjectHooksPanel.celGate(rewritten: a malformed guard is held and never sent),usePendingDrafts.afterSave-11787,StudioDesignSurface.changesCount-11787,CreateItemDialog.oneLabel-11787. The package-door Save clicks in the existing matrix suites wait for the autosave instead.Reverse verification through
ablation-replace(each restored, blob equal to HEAD,git diff HEADempty):blocked⇒ the environment-door pin red (saved length 1, expected 0), 4 others green;refresh({ afterSave })⇒ 2 red (one request on the wire, expected 2);behindSaveforced false ⇒ 3 red (the header shows "No drafts pending publish" after the save);5e5adb69, the rename hold removed fromblocked⇒ the rename pin red (save called 2 times, expected never). Re-run at5e5adb69: the 248 test files that importObjectHooksPanelor the designer table, all passed (2,259 tests), and the app-shell type-check exited 0; CI runs the full suite.Acceptance notes
_draftsread as zero.usePendingDraftsreturnsnullfor unknown so a surface can tell unknown from zero, and the header'shasPendingtreatsnullas0, so it shows "No drafts pending publish" while the count is unknown. Measured in theStudioDesignSurface.changesCount-11787harness before the save, with the mount read held.bfe6edc1, before the hold, an autosaved rename typed with one 1.6 s pause stagedhook/auditandhook/audit_trailbesideguard_hook. The Actions pillar, measured the same way, overwrote one object draft, so no other pillar stages an item per pause.ObjectListViewInspectorrestates theDraftSendshape locally because the hook was module-private; it can import it now. Not filed.Generated by Claude Code