Skip to content

fix(app-shell): take a served metadata draft as-is instead of spreading it over the published layer (objectui#10765) - #10775

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-10765-draft-merge-deletion
Sep 27, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-10765-draft-merge-deletion

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #10765
Clause-②: no — the Studio editor's draft rebuild; no declared surface moves

What changed

Every editor that rebuilt its draft as the served draft SPREAD over layered.effective now takes the served draft as-is and falls back to the baseline (effective, then code) only when no draft body was read (see H2 for what that covers). effective is the PUBLISHED layer and a spread cannot express deletion, so a key the author cleared in the draft came back from the published layer on the next refresh and the next save sent it again.

Nine sites in three files of @object-ui/app-shell, one spelling each (served draft ?? baseline):

  • ResourceEditPage.tsx — the load effect (rawInitial), the post-save refresh in doSave (rawFresh), the post-publish refresh in doPublish (rawFresh; config.toDraft now runs there too, as the other two refreshes already did).
  • StudioDesignSurface.tsx — the Interfaces pillar's app load (appDraft), its surface-leaf load (setDraft), the Data pillar's object load (setObjDraft), the Automations pillar's flow load (setDraft). The triage named the surface-leaf site; the other three are the same spread in the same file, and the triage's rule is "no per-type exception".
  • PackageOwdOverviewPanel.tsx — the list load that derives each row's OWD values and the save that splices the edit into the object's body. Bounded in-place exemption, declared below.

Three pin files, one changeset ('@object-ui/app-shell': patch), and a prose amendment to the pending objectui#10746 changeset, whose reach sentence this change made false (declared in Acceptance notes). No objectstack change.

H1 — the premise: a served draft is always a whole document

Answered from objectstack origin/main source (packages/metadata-protocol/src/protocol.ts, packages/metadata-protocol/src/sys-metadata-repository.ts, packages/rest/src/rest-server.ts), cited by symbol. A stock boot is NOT MEASURED: /home/user/objectstack/node_modules is empty in this container.

  • The write stores the body raw. saveMetaItem ends in repo.put(ref, request.item, { state: mode === 'draft' ? 'draft' : 'active', … }), and SysMetadataRepository.put stores metadata: JSON.stringify(body) with body = spec ?? {} — no read-modify-write against the existing row (the existing row is read only for the parent hash, the skipped short-circuit and, on a draft save, package_id inheritance). Before the put, saveMetaItem never merges the prior draft or the active row into the body. It subtracts only server-owned keys, in three strips: stripReadDecorations (the read-time decorations), stripDerivedProvenance (_packageId / _packageVersion / _provenance, which the read side recomputes) and stripServedObjectColumns (the system columns the server injects into a served object). None of the three reads another row. Everything else it does ADDS to the body or respells it, and the one pass that reads a stored row copies back redacted credential values only: normalizeViewMetadata grafts a view's missing viewKind / object / label from the registry baseline (viewIdentityPatch, "The overlay's own fields always win"); withDeclaredPageTypeDefault fills an absent page type with the schema's declared default; carryForwardRedactedCredentials restores a datasource's redacted credential values from the stored draft-or-active row; graftNormalizedOperators / graftFoldedFormSections rewrite spellings the schema already normalised.
  • The draft read returns that row raw. getMetaItem with state: 'draft' reads the state: 'draft' row (org-scoped, then env-wide; package-scoped, then global), convertStoredItems it, and returns decorateMetadataItem(type, governServedObject(type, item)); when the row is absent it throws NO_DRAFT (404) — "Draft reads stop here — they intentionally do NOT fall through to the runtime registry". On this strict path decorateMetadataItem adds _diagnostics only; _draft: true is stamped on the previewDrafts arms, not here. extractDraftBody strips both on the client (the spec's METADATA_READ_DECORATIONS, through stripReadDecorations).
  • effective never sees a draft. getMetaItemLayered composes effective as governServedObject(overlay ?? code) where overlay is the state: 'active' row only.
  • Publish promotes the draft raw. promoteDraft reads the draft row and calls this.put(ref, draft.body, { state: 'active', opType: 'publish', … }), then drains the draft row.
  • No partial-write door exists. The REST save door is PUT /meta/:type/:name (?mode=draft maps to { mode: 'draft' } and the body is passed as item); a grep of packages/rest/src/rest-server.ts for a PATCH route on /meta finds none. @objectstack/client's meta.saveItem(…, { mode: 'draft' }) sends the caller's body. packages/cli/src holds no mode: 'draft' writer.

Every writer of a mode: 'draft' body, enumerated (git grep over both trees at the stated refs, non-test source):

objectui (63835d998f):

  1. ResourceEditPage.doSave — itemToSave = config.fromDraft ? fromDraft(draft) : draft: the whole editor draft (no type config defines fromDraft today).
  2. StudioDesignSurface Interfaces pillar — doCreateApp (buildAppSkeleton, whole), surface doSave (draft, whole), doNavSave ({ ...appDraft, navigation: cleanedNav }, whole).
  3. StudioDesignSurface Data pillar — object create (buildObjectSkeleton, whole), doSave (objDraft, whole), the field-order save ({ ...objDraft, fields }, whole).
  4. StudioDesignSurface Automations pillar — flow create (buildFlowSkeleton, whole), doSave (draft, whole), toggleEnabled ({ ...draft, status }, whole); Access pillar — permission create (buildPermissionSkeleton, whole).
  5. ObjectHooksPanel — save (draft, a whole hook item) and addHook (fresh, whole).
  6. PackageOwdOverviewPanel.doSave — next, the whole object body with the two OWD keys set or deleted.
  7. PermissionMatrixEditor — mergePermissionSlice(base, payload, scope) with base = fresh.effective ?? payload: a whole permission-set body (draft mode only when a packageId is bound).
  8. runtime-metadata-persistence.ts — persistRuntimeMetadata / createRuntimeMetadata PUT the body their callers hold: the whole report from ReportConfigPanel through ReportView.saveSchema, the whole view from the runtime view persisters.
  9. @object-ui/data-objectstack ObjectStackAdapter.updateView(object, view, partial) — the one API that TAKES a partial: it reads the pending draft (or the published view) and saves mergeViewPatch(current, partial, …) = { ...current, ...partial, name, object }, a whole document.

objectstack (4df101c383):
10. REST PUT /meta/:type/:name?mode=draft — passes the request body raw (above).
11. ObjectStackClient.meta.saveItem — the caller's body, raw.
12. migrateStoredMetadata --apply (protocol.ts, source: 'migrate-stored') — re-saves the CONVERTED WHOLE stored row with mode: state === 'draft' ? 'draft' : 'publish'.

No writer sends a subset of a document. The one caveat is not a writer but a door: any REST client can PUT a hand-written partial body with or without ?mode=draft, and the server stores it as the whole draft or the whole published body alike — the editor then shows exactly what the server holds, which is the truthful reading. ⇒ Premise holds: always full; the "can be partial" stop in the triage did not fire, so no needs_decision.

H2 — why the merge existed

The comment at the load site said the spread kept "inherited fields like type" for ADR-0047 visibleOn predicates, treating the draft as a "partial draft overlay". A type is absent from a served draft only when the writer omitted it, and H1 shows no writer sends a subset: the editor's own saves carry type because the editor's draft was loaded from effective or the previous draft, both of which carry it, and for a page the server fills a missing type on the way in (withDeclaredPageTypeDefault in saveMetaItem) and on the way out (convertStoredItem). The inheritance the spread protected is inside the draft. The pins' CONTROL assertions read type back from the served draft at every step.

When the baseline is taken, as the code reads it. Every site reads client.getDraft(…).catch(() => null). MetadataClient.getDraft sits on readItemResponse, which returns null on a 404 (NO_DRAFT) and throws on any other non-OK answer; the .catch then folds a 403, a 5xx or a network failure into null too. So the baseline is taken when no draft body was READ, which covers "no draft exists" and also "the draft read failed". Base did the same at every site, and this change does not touch it. The hazard that leaves is named in Acceptance notes.

H3 — the fix, per site

# File · site Before After
1 ResourceEditPage load effect draftReal ? { ...baseline, ...draftReal } : baseline draftReal ?? baseline
2 ResourceEditPage.doSave post-save refresh draftReal ? { ...freshBaseline, ...draftReal } : freshBaseline draftReal ?? freshBaseline
3 ResourceEditPage.doPublish post-publish refresh same spread, no toDraft draftReal ?? freshBaseline, then config.toDraft
4 StudioDesignSurface Interfaces — app load (appDraft) appDraftBody ? { ...eff, ...appDraftBody } : eff appDraftBody ?? eff
5 StudioDesignSurface Interfaces — surface leaf load body ? { ...baseline, ...body } : baseline body ?? baseline
6 StudioDesignSurface Data — object load (setObjDraft) draftBody ? { ...baseline, ...draftBody } : baseline draftBody ?? baseline
7 StudioDesignSurface Automations — flow load draftBody ? { ...baseline, ...draftBody } : baseline draftBody ?? baseline
8 PackageOwdOverviewPanel list load (body) draftBody ? { ...baseline, ...draftBody } : baseline draftBody ?? baseline
9 PackageOwdOverviewPanel.doSave (next) { ...baseline, ...(draftBody ?? {}) } { ...(draftBody ?? baseline) }

config.toDraft still runs on whatever is taken at sites 1 and 2, and now at 3; it is inert today (no registered type config defines toDraft / fromDraft — the registry.ts interface declares them, builtinComponents.tsx states the view previews read draft.config directly and use none).

Also checked, unchanged: EmbeddedItemEditor.doSave reads layered.effective ?? layered.code ALONE, splices the embedded item in and saves in publish mode — no draft is spread. ResourceEditPage.doReset and doDiscardDraft read the baseline alone after deleting the row — correct, unchanged.

H4 — edges

  • A type with no draft: baseline, unchanged (pinned: EDGE / CONTROL in the ResourceEditPage file and the Interfaces CONTROL in the design-surface file — toStrictEqual(published)).
  • First draft save of a never-drafted item: the draft PUT is the whole itemToSave, and the refresh shows exactly that (pinned).
  • A draft written by an older client or server that was partial: the server cannot compose one (H1) and no writer in either tree's history reachable here sends one; a hand-written partial REST PUT stores a partial DRAFT or a partial PUBLISHED body alike, and the editor now shows what the server holds. NOT MEASURED against historical databases.
  • Publish: the promoted body is the draft raw (promoteDraft → put(ref, draft.body, { state: 'active' })); the triage's checklist item ("the published body lacks the cleared key") is pinned in both ResourceEditPage cases against the server double.

Pins and ablations

Three pin files (10 tests), all green on head 85f1b9e91a (re-run this round under the verify lock: Test Files 3 passed (3), Tests 10 passed (10), VERDICT command-exit 0); 7 red on base with the three source files at 63835d998f and the pin files present (the 3 that stay green are the controls). The base leg and the ablations below were measured at 5f3b9c73fd; the round-2 commit 85f1b9e91a changes comments and changeset prose only (a grep of its .ts/.tsx hunks for any changed line that is not a comment answers none), so no statement they read moved.

  • ResourceEditPage.servedDraftTakenAsIs-10765.test.tsx (4): mounts the REAL MetadataResourceEditPage with registerBuiltinInspectors() so the REAL ReportDefaultInspector type picker is the producer; a server double stores draft rows raw, serves effective as the active row only, promotes on publish, throws NO_DRAFT when no row. THE TRIAGE PIN: bound published matrix report → pick "Joined" → first PUT clean → post-save refresh has none of the six keys under type: 'joined' → an unrelated edit's second PUT has none → Publish → published body and post-publish editor draft have none. GENERIC: a published page's description deleted in the draft stays deleted on load, across the next save and refresh, and through publish, with type as the CONTROL. Two EDGE / CONTROL cases (no draft; first draft save is the whole document).
  • StudioDesignSurface.servedDraftTakenAsIs-10765.test.tsx (4): InterfacesPillar (app label deleted in the draft → heading reads acme_app · Navigation; page description deleted → the canvas draft lacks it), DataPillar (object sharingModel set back to unset → Settings shows unset), AutomationsPillar (flow description deleted → canvas draft lacks it, and the enable switch's save carries the draft alone). Plus the Interfaces CONTROL.
  • PackageOwdOverviewPanel.servedDraftTakenAsIs-10765.test.tsx (2): LIST LOAD (a cleared model reads unset) and SAVE (an internal-only edit does not resurrect the published externalSharingModel or description).

Base leg, quoted: Tests 7 failed | 3 passed (10); AssertionError: after the post-save refresh the editor draft has no dataset: expected { name: 'pipeline', …(8) } to not have property "dataset"; expected { name: 'home', …(5) } to not have property "description"; expected 'private' to be ''; Unable to find an element with the text: acme_app · Navigation. Restore proven: git diff HEAD empty, each path's blob equal to HEAD.

Per-site ablations on the committed head through objectstack's scripts/ablation-replace.mjs (anchor hits exactly once, blob change proven, restore proven per leg, diff-HEAD-lines=0 after each), prediction written before each run:

Site Mutation Predicted red Observed
1 load draftReal ?? baseline → spread GENERIC `1 failed
2 post-save draftReal ?? freshBaseline (save) → spread TRIAGE, GENERIC `2 failed
3 post-publish draftReal ?? freshBaseline (publish) → spread none 4 passed — none, as predicted (see Acceptance notes)
4 app appDraftBody ?? eff → spread Interfaces (heading) `1 failed
5 surface body ?? baseline → spread Interfaces (canvas) `1 failed
6 object setObjDraft(draftBody ?? baseline) → spread Data `1 failed
7 flow setDraft(draftBody ?? baseline) → spread Automations `1 failed
8 OWD list body = draftBody ?? baseline → spread LIST LOAD 2 failed (2) — LIST LOAD and SAVE: MORE than predicted; the list-load merge also feeds the row's external edit, so the save wrote the resurrected externalSharingModel
9 OWD save { ...(draftBody ?? baseline) } → spread SAVE `1 failed

Direction observed as predicted at eight sites; site 8 reddened one assertion more than predicted (reported as observed); site 3 reddened nothing, as predicted.

Gates (local; objectui has no dispatch-gates.mjs — derived from package.json and .github/workflows)

Round 2, at head 85f1b9e91a, run after the final commit:

  • vitest, the three pin files, under the verify lock: Test Files 3 passed (3), Tests 10 passed (10), VERDICT command-exit 0.
  • node scripts/check-changeset-presence.mjs: exit 0 — ✅ "6 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/10765-draft-merge-deletion.md".
  • node scripts/check-changeset-no-major.mjs: exit 0 — ✅ "No changeset declares a major bump."
  • node scripts/check-changeset-claims.mjs: exit 0 (report-only). It names one pending changeset, 6681-declared-lazy-marketplace-routes.md, for spelling StudioDesignSurface.tsx; that is a chunking claim, and this branch changes no import there.
  • node scripts/check-changeset-overwrite.mjs: exit 0 (report-only). It reports M .changeset/10746-joined-report-clears-binding.md, declared @object-ui/app-shell: patch at base and now: the intended prose correction declared in Acceptance notes (the gate's own case 2).
  • pnpm check:pending-changeset-literals: exit 0 — ✅ "No test source names a pending changeset."
  • pnpm check:new-line-citations: exit 0 — VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0.
  • pnpm check:control-bytes: exit 0 — ✅ "OK (scanned 8957 tracked text file(s); skipped 85 binary)". The role file's control-byte grep over the four files this round edits: no match.
  • node scripts/check-governed-queue-guard.mjs --test over the branch's eight paths: ✅ NOT GOVERNED.

Round 1, at 5f3b9c73fd, not re-run: round 2 changes comments and changeset prose only.

  • ① closure build turbo run build --filter='@object-ui/app-shell^...' --concurrency=2 under the verify lock: Tasks: 28 successful, 28 total, VERDICT command-exit 0.
  • ② pnpm --filter @object-ui/app-shell type-check (tsc --noEmit && tsc -p tsconfig.test.json): VERDICT command-exit 0; tsc -p tsconfig.test.json --listFiles lists the three pin files (3 hits among 4677 files).
  • ② sibling suites (every ResourceEditPage.*, PackageOwdOverviewPanel*, ObjectSettingsPanel.owdAgreement, StudioDesignSurface.*, DataPillar.*, ObjectHooksPanel.*, ReportDefaultInspector.* test): Test Files 57 passed (57) · Tests 271 passed (271), VERDICT command-exit 0.
  • pnpm check:test-path-roots, pnpm check:vi-mock-specifiers, pnpm check:vi-mock-inherit, pnpm check:vi-mock-override-shape: ✅ OK each.
  • eslint with the app-shell package config on the six touched .tsx files: exit 0, 0 errors, 76 pre-existing warnings, 0 of them on a line this branch changed (checked against the committed hunks).

NOT MEASURED locally, CI-owned: pnpm lint (repo-wide), the test shards, Build & E2E, check:i18n-* (no t() key added or changed), Governed Surface Queue Guard. CI on 85f1b9e91a was still running at the report read (every completed run a success or an expected skip); its converged state is the seat's reading.

Serial

Branch fast-forwarded to 63835d998f (PR objectui#10762 landed) before the first edit, so the pins use its commitType as the real producer. Re-derived at round 2 against a fresh origin/main, fetched into a private ref: 17b323e5ca, +4 over BASE (objectui#10754, objectui#10766, objectui#10763, objectui#10774). git diff --stat 63835d998f 17b323e5ca over the branch's eight paths is empty, and git merge-tree --write-tree HEAD 17b323e5ca exits 0, clean, tree fcd7226887. No merge commit needed. Open PRs were not re-read this round; the seat's review read 10 and found none on ResourceEditPage.tsx, StudioDesignSurface.tsx, EmbeddedItemEditor.tsx, PackageOwdOverviewPanel.tsx or PermissionMatrixEditor.tsx.

Acceptance notes

  • Amends another card's pending changeset. .changeset/10746-joined-report-clears-binding.md (objectui#10746, pending, same release) ended with a reach sentence this change made false: that a report whose PUBLISHED version was bound gets the cleared keys back in the draft after the first draft save until publish, through the host's draft-over-baseline merge (objectui#10765). Its sentences are now scoped "At this change", and a ⚠️ **Dated note, 2026-09-27 — that merge has since been removed — objectui#10765.** in the objectui#10156 / objectui#10392 form states what holds now: the clear holds across the refresh after the save and through publish, because the editor takes a served draft as-is; a report already saved joined with stale keys is still not repaired on load, and the author deletes them from the JSON tab. The frontmatter is byte-identical: md5 of its three lines is 4e8ce55021fe3b14329c8b4cb3f9f354 at base 63835d998f, at 5f3b9c73fd and at 85f1b9e91a. check-changeset-overwrite reports the file as modified; that is this intended correction. The objectui#10765 changeset gains one sentence naming the reach limit it removes.
  • Pending-changeset sweep (git grep of .changeset/*.md at fresh main 17b323e5ca and at this head, for layered.effective, draft-over, spread over, resurrect, objectui#10765 and published layer). Outside the objectui#10746 and objectui#10765 files the only hits are "resurrect" in four unrelated senses: 4919-retire-mobile-overrides.md (a declaration), 6799-authored-columns-fls.md (a grid column), 7499-gantt-non-axis-floors-omitted.md (a gantt config) and active-org-clear-removal-failure-5731.md (a storage entry). None is falsified. A wider read for draft-over-baseline wording found 7603-resource-edit-read-decoration-strip.md and 8181-draft-envelope-read-decoration-strip.md. Both describe the merge in the past tense, as the state before their change. 7603's "the chokepoint all three merge sites go through" still holds of its subject, since all three sites still go through extractDraftBody; "merge sites" is that change's label. Nothing else needed reconciling.
  • Stale comments, comment-only. Fixed the two the review named: the Data pillar's projection-baseline comment in StudioDesignSurface.tsx and the OwdRow docblock in PackageOwdOverviewPanel.tsx. Fixed two more in PackageOwdOverviewPanel.tsx with the same stale wording: the list-load comment ("merged body (layered ∪ its draft)") and the save comment ("fresh merged body"). The file header's "(published ∪ pending drafts, the same merge the pillars do)" is about which OBJECTS are listed, a union of lists, so it stays.
  • Pre-existing hazard, not this card's (see H2). The .catch(() => null) on every draft read folds a failed read (403, 5xx, network) into "no draft", so the site shows the published baseline as the editable draft. The next save then writes that baseline-derived body as the draft, whether it is manual or the metadata-admin auto-save after an edit, and it replaces the pending draft the failed read never fetched. Base had the same shape at every site; this change neither adds nor removes it. Not filed. carrier: none.
  • Bounded in-place exemption, declared: PackageOwdOverviewPanel.tsx was not in the dispatch's file surface. Same defect class (a served object draft spread over effective), mechanical fix in the pinned shape, no other claim on the file (open-PR file lists read), same gate family (app-shell vitest / type-check / changeset), no new verification surface. Pinned with two red-on-base assertions; the file surface is stated here.
  • Site 3 (post-publish): changed for the triage's "same rule at every site". Its ablation reddens nothing, as predicted: today's server drains the draft row on publish (promoteDraft → dropPromotedDraftRow), so getDraft answers NO_DRAFT after a publish and the spread had no draft to resurrect from; a draft that survives (a drain failure, or an autosave racing the publish) is a whole document equal to or newer than the promoted body. The spread there was inert in practice, not wrong — and it is now the same spelling as the other two refreshes.
  • config.toDraft on the post-publish refresh: added for uniformity; inert today (no type config defines the hook).
  • Observation, not filed (read-only inference, no reproduction): PermissionMatrixEditor's package-scoped save merges the edited slice into fresh.effective (the PUBLISHED body) and never reads the pending draft, so a slice save could overwrite other pending draft edits of the same permission set. Different mechanism from this card's. carrier: none.
  • Observation, not filed: EmbeddedItemEditor reads the baseline alone and saves in publish mode (no mode: 'draft'), by design of the embedded flow; noted because the dispatch asked.
  • The triage's P1 checklist item (studio-authoring.draft-publish-lifecycle) owes an editor-driven step (clear a key over a published version, save, refresh, publish); that checklist lives in objectstack and is not touched here.

Generated by Claude Code

…ng it over the published layer

The metadata-admin editor rebuilt its draft as the served draft spread over
`layered.effective` on load, after each draft save and after publish; the
Studio design surface did the same when opening an app, a surface, an
object or a flow, and the package OWD overview when reading and re-saving
an object's draft. `effective` is the published layer and a spread cannot
express deletion, so a key the author cleared in the draft came back from
the published layer on the next refresh and the next save sent it again.

The server stores a `?mode=draft` body raw and serves the `?state=draft`
row raw, and every draft writer sends a whole document, so each site now
takes the served draft as-is and falls back to the baseline only when no
draft exists. `config.toDraft` still runs on whatever is taken, now on the
post-publish refresh too.

Pins: one server double that stores draft rows raw and serves `effective`
as the active row only; the report joined-clear case across save, refresh
and publish; a generic deleted key across load, save, refresh and publish
with the inherited `type` as the control; the four design-surface sites and
the two OWD-panel sites, one red-on-base assertion each.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
@github-actions

github-actions Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 1 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/6681-declared-lazy-marketplace-routes.md

  • names views/studio-design/StudioDesignSurface.tsx → packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx — edited by this change

    Both marketplace chunks were held by CHUNK CO-TENANCY, not by any import of the page: rolldown had parked components/SuggestedBindingsPanel.tsx (statically imported by the eager views/studio-design/StudioDesignSurface.tsx) in the first, and console/marketplace/InstalledListWidget.tsx (bare-imported by the package barrel for its SDUI registration) in the second. An app-shell-eager-leaves group in apps/console/vite.config.ts isolates those co-tenants so the three declared-lazy pages chunk by their own dynamic-only reachability. The console eager closure moves 3180.2 KB → 3171.5 KB gzipped (−8,888 bytes, 48 → 45 eager chunks) with the three per-chunk ceilings unmoved.

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Compared the checked-out tree with 17b323e5c (merge-base with origin/main): 6 file(s) changed outside .changeset/, read against 1580 pending declaration(s) that publish a body (2168 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3067.7 KB 3104.5 KB
Main entry chunk (gzip) 148.3 KB 350 KB
Entry file index-ChjD0-9f.js —
Status PASS —

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.57KB 6.15KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.17KB 10.58KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 554.94KB 132.67KB
core (index.js) 9.57KB 3.81KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.93KB 62.31KB
fields (index.js) 259.29KB 65.64KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.65KB 14.69KB
plugin-charts (index.js) 82.37KB 22.57KB
plugin-chatbot (index.js) 198.40KB 47.22KB
plugin-dashboard (index.js) 134.11KB 35.62KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 233.23KB 61.74KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 157.44KB 40.24KB
plugin-gantt (index.js) 169.75KB 41.96KB
plugin-grid (index.js) 218.12KB 59.72KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 114.70KB 28.42KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.91KB 9.05KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 87.83KB 22.01KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 116.21KB 38.14KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 5f3b9c73fd880ccfb70af682669813cf03b88231

① Derived judgments

H1, verified from objectstack origin/main 805af4f290, by symbol.

  • Write: protocol.ts saveMetaItem ends in repo.put(ref, request.item, { …, state: mode === 'draft' ? 'draft' : 'active' }); sys-metadata-repository.ts SysMetadataRepository.put stores metadata: JSON.stringify(body) with body = spec ?? {}, and reads the existing row only for the optimistic-lock hash, the identical-hash skipped short-circuit and (draft saves) package_id inheritance — never composes it into the body. The pre-put passes strip server-owned keys (stripReadDecorations, stripDerivedProvenance, stripServedObjectColumns), add identity or a declared default (normalizeViewMetadata, withDeclaredPageTypeDefault), rewrite spellings (flow canonicalisation, graftNormalizedOperators, graftFoldedFormSections) or carry redacted credential values forward from the stored draft-then-active row (carryForwardRedactedCredentials, redactor types only). None merges the prior draft or the active row into the body. "Stored raw" holds for authored content; the PR body's "only ADDS, never subtracts" is loose about the three strips (server-owned keys only).
  • Read: getMetaItem with state: 'draft' reads the state: 'draft' row → convertStoredItem → governServedObject → decorateMetadataItem, else throws NO_DRAFT 404 ("Draft reads stop here"). On this path decorateMetadataItem adds _diagnostics only; _draft: true is set on the previewDrafts arm — the body's "(_diagnostics, _draft)" is loose, harmless.
  • getMetaItemLayered: effective = governServedObject(overlay !== null ? foldObjectExtendersFromRegistry(overlay) : code), overlay the state: 'active' row only. promoteDraft: this.put(ref, draft.body, { state: 'active', opType: 'publish', packageId: draftPackageId }), then dropPromotedDraftRow.
  • Doors: rest-server.ts maps ?mode=draft to { mode: 'draft' } on PUT /meta/:type/:name; the only PATCH routes are on /data/:object/:id. The runtime dispatcher (runtime/src/domains/meta.ts) builds its saveMetaItem request with no mode — never a draft. client/src/index.ts saveItem forwards the caller's body and mode.
  • Writers, enumerated independently (git grep both trees for mode: 'draft', mode=draft, state: 'draft', saveDraft, putDraft, .save(, .saveItem(, saveMetaItem(): objectui — ResourceEditPage.doSave (itemToSave), StudioDesignSurface ten call sites (skeletons, draft, { ...appDraft, navigation }, objDraft, { ...objDraft, fields }, { ...draft, status }), ObjectHooksPanel two, PackageOwdOverviewPanel.doSave (next), PermissionMatrixEditor (mergePermissionSlice(fresh.effective ?? payload, …), draft only with packageId), persistRuntimeMetadata / createRuntimeMetadata (whole report from ReportView.saveSchema; buildViewConfigSaveBody emits a complete viewEnvelope), ObjectStackAdapter.updateView (mergeViewPatch(draft, partial), whole). objectstack — REST PUT ?mode=draft, migrateStoredMetadata --apply (mode: state === 'draft' ? 'draft' : 'publish', converted whole row). Every other saveMetaItem / saveItem caller (permission-set-projection.ts four, packages.ts app _unpublished flip, duplicatePackage mode: 'publish', cli/commands/meta/register.ts, console meta.saveItem sites, plugin-designer, EmbeddedItemEditor, DatasourceResourcePage) writes publish mode. No partial draft writer exists; no writer the dev missed. Premise: ALWAYS FULL. A stock boot is NOT MEASURED here either (objectstack node_modules empty) — source-cited.

H2. type is inside every draft these editors write; page type is filled on the read seam too (convertStoredItem → withDeclaredPageTypeDefault). toDraft / fromDraft are declared on the registry.ts interface and defined by no registered config (builtinComponents.tsx states none) — the new post-publish call is inert today. Fallback: all three ResourceEditPage sites read client.getDraft(…).catch(() => null); MetadataClient.readItemResponse returns null on 404 and throws otherwise, so the .catch folds 403 / 5xx / network into "no draft" and the editor shows the published body. Base did the same, so no regression; but the body's "only when no draft exists" is looser than the code ("when no draft body was read"), and an autosave from that state overwrites the pending draft — a pre-existing hazard, not this card's.

Nine sites. Base 63835d998f and head read: every site is served ?? baseline (baseline = effective ?? code ?? {}; effective ?? itemToSave and effective ?? draft at the two refreshes); config.toDraft still runs at sites 1 and 2 and now at 3; no per-type exception. No draft-over-effective spread remains in the three files; EmbeddedItemEditor.doSave reads layered.effective ?? layered.code alone and saves publish mode; doReset / doDiscardDraft read the baseline alone. Remaining spreads in the two directories are patches on the draft ({ ...draft, status }), the create seed (createBody.ts) and PermissionMatrixEditor's full over draftBody ?? lay.effective (draft wins wholesale) — none is the class; the runtime hosts' handleResumeDraft (Report / View config panels) replace wholesale. Two stale comments survive in touched files (nits): StudioDesignSurface.tsx Data pillar "objDraft below merges the draft on top", and PackageOwdOverviewPanel.tsx OwdRow docblock "(already merged over any pending draft)".

PackageOwdOverviewPanel widening. .claude/agents/os-dev.md rule 3 ("有界就地修豁免,四条全立"): ① same defect class, ② mechanical fix in the pinned shape, ③ no other claim on the file (10 open PRs' file lists read), ④ same gate family, no new verification surface — all hold; declared in the PR body and the report. What is SENT changes only when a draft exists: base sent { ...published, ...draft } plus the two OWD edits (every key the draft deleted resurrected); head sends the draft plus the edits. Correct. Pre-existing and not this card's: the panel's getDraft('object', name) is package-unscoped while its save is package-scoped.

Pins, re-run in a detached worktree at head, pnpm install --offline --frozen-lockfile, every run under os-verify-lock.sh. Head: 3 files, 10 passed. Base leg (three source files git restore --source=63835d998f, blobs 08e198fe76 / 469bdf769c / 72fa1eb906 confirmed, pin files at head): 3 files failed, 7 failed | 3 passed — "after the post-save refresh the editor draft has no dataset: expected { name: 'pipeline', …(8) } to not have property "dataset"", "expected { name: 'home', …(5) } to not have property "description"", "expected 'private' to be ''" twice, "expected { name: 'crm_contact', …(4) } to not have property "externalSharingModel"", "Unable to find an element with the text: acme_app · Navigation", "expected { name: 'notify_owner', …(4) } to not have property "description"". Restore proven: git status --porcelain empty, each blob equal to HEAD. The double stores draft rows raw, serves effective as the active row only, promotes and drains on publish — not tautological (its getDraft throws NO_DRAFT where the real client returns null; equivalent through the .catch). The triage pin is present as ruled: bound published matrix report, the REAL ReportDefaultInspector picker to joined, first PUT clean, post-save refresh without the six keys, second PUT without them, publish, published body and post-publish draft without them, type (with name, label) as the control. Ablations (anchor hit once, blob change proven, restored to a clean tree each): site 1 → 1 failed | 3 passed (GENERIC, description); site 2 → 2 failed | 2 passed (TRIAGE dataset, GENERIC); site 4 → 1 failed | 3 passed (acme_app · Navigation); site 8 → 2 failed (2) (LIST LOAD, SAVE) — all as reported, site 8's extra failure included.

Siblings. The 57 files the dev names (glob re-derived: ResourceEditPage.*, PackageOwdOverviewPanel*, ObjectSettingsPanel.owdAgreement, StudioDesignSurface.*, DataPillar.*, ObjectHooksPanel.*, ReportDefaultInspector.*), three batches: 19 + 19 + 19 files passed, 109 + 62 + 100 = 271 tests passed. (A first attempt on borrowed symlinked node_modules crashed a vitest worker on StudioDesignSurface.studioCanvasLeaf.test.tsx; the fresh-install run above is the reading.)

Type-check. Closure build turbo run build --filter='@object-ui/app-shell^...': 28 tasks, 28 cached; then tsc --noEmit exit 0 and tsc -p tsconfig.test.json exit 0, 0 errors.

② Semver level

'@object-ui/app-shell': patch, Clause-② no: right. The diff is statement-level inside three components plus three new test files and one changeset; packages/app-shell/src/index.ts, package.json, props and registrations untouched; nothing exported, typed or prop-shaped moves.

Changeset, sentence by sentence: true against head and the server source; "stores a ?mode=draft body raw and its ?state=draft read returns that row raw" is a fair shorthand (the additive normalisations are spelled out in the PR body and none composes the body with another row); "config.toDraft still normalises whatever is taken, now on the post-publish refresh as well" true.

PR body: the H1 table verified symbol by symbol (loose only at _draft and "only ADDS", above); the 12-writer enumeration matches mine; the per-site table matches the diff; the ablation table matches my four re-runs; the edges hold; Acceptance notes true — PermissionMatrixEditor's save re-bases on fresh.effective ?? payload and never reads the pending draft (draft mode only with packageId), EmbeddedItemEditor reads the baseline and saves publish mode, site 3 is inert because promoteDraft drains. Serial is stale by two merges: main is now 17b323e5ca (+4 over base: #10754, #10766, #10763, #10774), none touching the three source files or either changeset; git merge-tree --write-tree clean, tree d36314abd9, exit 0.

Pending changesets naming the touched files: check-changeset-claims (report-only) and the PR's changeset-claim-re-read comment flag only 6681-declared-lazy-marketplace-routes.md (a chunking claim, unaffected). NOT flagged, because it spells no file name in backticks: .changeset/10746-joined-report-clears-binding.md — pending, unreleased, publishing in the same release — ends "The metadata-admin editor rebuilds its draft as the served draft spread over layered.effective (on load, after each save and after publish), and effective is the published layer, so a report whose PUBLISHED version was bound gets those keys back in the draft after the first draft save until it is published, and a report already saved joined with stale keys is not repaired on load. Both are the host's draft-over-baseline merge, objectui#10765." At this head the first two claims are FALSE (the TRIAGE pin measures exactly that path) and the merge they describe no longer exists; only "not repaired on load" stays true. This is the WENT-FALSE class the gate's own header names ("If a claim did go false, CORRECT THE BODY … prose-only, with frontmatter untouched"). This is the FAIL below.

③ Boundary flags

Implemented-by: claude/issue-10765-draft-merge-deletion
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: FAIL

What stops it — one wording item, no code change owed:

  1. The pending changeset .changeset/10746-joined-report-clears-binding.md publishes a reach limit this head removes. Replace its last three sentences (from "The clear holds for the patch" to "objectui#10765.") with: "The clear holds for the patch and for the save that follows it, and — since the metadata-admin editor now takes a served draft as-is instead of spreading it over the published layer (objectui#10765, same release) — across the refresh after that save and through publish. A report already saved joined with stale keys is not repaired on load; the author deletes them from the JSON tab." Frontmatter untouched; declare the amendment in the PR body's Acceptance notes ("amends the pending objectui#10746 changeset's reach sentence, which this change made false; check-changeset-overwrite reports it as an intended correction"). If the seat would rather not touch another card's file, the minimum is one sentence appended to .changeset/10765-draft-merge-deletion.md: "This removes the reach limit the objectui#10746 entry states — a bound published report getting its cleared keys back after the first draft save; a report already saved joined with stale keys is still not repaired on load."

Non-blocking, for the seat's next push if any: the two stale merge comments in StudioDesignSurface.tsx (Data pillar, "merges the draft on top") and PackageOwdOverviewPanel.tsx (OwdRow, "already merged over any pending draft"); PR body H1 "_diagnostics, _draft" (strict draft read adds _diagnostics only) and "only ADDS … never subtracts" (three server-owned-key strips); H2 "only when no draft exists" reads "when no draft body was read" in code; Serial figures now two merges stale. Everything else in ①–③ passes: premise verified always-full with no missed writer, the same rule at all nine sites, widening inside the exemption, 10 pins and 4 ablations reproduce exactly, 57 sibling files and 271 tests green, type-check clean, CI green, tree clean, not governed.

…say what the draft reads do now

The pending objectui#10746 changeset stated a reach limit this branch
removes: a report whose PUBLISHED version was bound got its cleared keys
back in the draft after the first draft save, through the host's
draft-over-baseline merge. Its sentences are now scoped to that change and
a dated note (objectui#10156 / objectui#10392 form) states what holds now:
the clear survives the refresh after the save and publish; a report already
saved joined with stale keys is still not repaired on load. Frontmatter
unchanged. The objectui#10765 changeset names the reach limit it removes.

Comment-only: the Data pillar's projection-baseline comment and three
PackageOwdOverviewPanel comments (the OwdRow docblock, the list-load and
the save) said the body was merged over the pending draft; they now say
the draft is read as-is, else the layered baseline.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3068.1 KB 3104.5 KB
Main entry chunk (gzip) 148.3 KB 350 KB
Entry file index-ByWzAab2.js —
Status PASS —

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.57KB 6.15KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.17KB 10.58KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 554.94KB 132.67KB
core (index.js) 9.57KB 3.81KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.93KB 62.31KB
fields (index.js) 259.29KB 65.64KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.65KB 14.69KB
plugin-charts (index.js) 82.37KB 22.57KB
plugin-chatbot (index.js) 198.40KB 47.22KB
plugin-dashboard (index.js) 134.11KB 35.62KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 233.23KB 61.74KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 157.44KB 40.24KB
plugin-gantt (index.js) 169.75KB 41.96KB
plugin-grid (index.js) 218.12KB 59.72KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 114.70KB 28.42KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.91KB 9.05KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 87.83KB 22.01KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 116.21KB 38.14KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 85f1b9e91a6708ebca7ef99cd486ac77517c8ea5

Narrow round-2 review, run in-seat by the dispatching seat, of the delta 5f3b9c73fd..85f1b9e91a (one commit) against the round-1 FAIL record 5856077631. Every other judgment in that record stands.

① Derived judgments

  • The delta is prose and comments only. 4 files, +28 / −13. The two .tsx files change comment lines only: every changed line not beginning //, * or /* was filtered out, and none remained. No statement that the round-1 pins and ablations read has moved.
  • The blocking item is resolved. .changeset/10746-joined-report-clears-binding.md now scopes its reach sentences "At this change", then appends ⚠️ **Dated note, 2026-09-27 — that merge has since been removed — objectui#10765.** in the form of the objectui#10156 / objectui#10392 dated notes. The note says what is true at this head: the clear holds across the refresh after the save and through publish, as the TRIAGE pin measures; a report already saved joined with stale keys is still not repaired on load. Frontmatter lines 1–3 have md5 4e8ce55021fe3b14329c8b4cb3f9f354 at base 63835d998f and at this head.
  • The objectui#10765 changeset's added sentence names the removed reach limit and restates the one that remains. It is true at head.
  • The four comment rewrites say what the code does.
    • Three PackageOwdOverviewPanel.tsx comments now read "pending draft as-is, else layered baseline", which matches draftBody ?? baseline.
    • The StudioDesignSurface.tsx Data pillar comment now reads "pending draft as-is", with the gridColumns reason.
  • The sweep claim ("nothing else to reconcile") was spot-checked by grepping pending changesets for layered.effective and draft-over. The only hits are the two entries this PR edits and the past-tense 7603 / 8181 entries. It holds.

② Semver level

'@object-ui/app-shell': patch, unchanged, and Clause-②: no stands. The amended objectui#10746 entry keeps its declared level (check-changeset-overwrite: patch at base and now).

Observation, not blocking: the objectui#10765 changeset still says the editor falls back "only when no draft exists". The code falls back when no draft body was READ, which includes a failed read. The replacement PR body now states that precisely (H2 and the Acceptance note on the pre-existing autosave hazard), and round 1 passed the changeset sentence by sentence.

③ Boundary flags

  • CI on 85f1b9e91a: 43 check runs; 40 success, 3 skipped (Test (coverage), the coverage-shard matrix entry, dependabot), 0 failure, 0 pending.
  • Merge-tree against main 17b323e5ca is clean, per the dev's reading, tree fcd7226887. main's four commits past base touch none of the eight paths.
  • Governed: none of the eight paths is governed.
  • PR state: draft; assignee os-elon-musk.
  • The dev served at the default judgment tier after the fable-quota takeover, which is recorded in the report's takeover field.

Implemented-by: claude/issue-10765-draft-merge-deletion
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 27, 2026 13:26
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 27, 2026
Merged via the queue into main with commit ab0e0fd Sep 27, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10765-draft-merge-deletion branch September 27, 2026 13:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants