Commit 993f27e
perf(console): the first screen stops loading the spec entries only the metadata designers validate with; the eager-closure ceiling comes down by what that recovered (objectui#11101) (#11342)
Fixes #11101
Clause-②: no. Load timing only; no contract, export or accept set moves,
and validation is unchanged once loaded.
The console-side payback of the objectui#11088 decision 1 = A loan (the
`@objectstack/*` 17.5.0 ceiling raise of PR #11086). Implemented by
`os-dev` for the `domain:devx` seat 2 claim `5924908810`, session
`https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh`.
## Measured first
Every `@objectstack/spec` entry the console bundles sits in one chunk,
`vendor-objectstack`, and that chunk is a static import of the entry.
Read on a console build of `main` at `5262f7d` with a throwaway
module-graph dump (not committed). "gzip alone" is each module's
rendered code gzipped by itself: an attribution, not additive, and
larger than the module's share of the chunk.
| spec entry | rendered | gzip alone | static importers in eager chunks
| verdict |
|---|---:|---:|---|---|
| root `@objectstack/spec` | 2,370,467 | 692,269 | `@object-ui/core`,
`@object-ui/auth`, `@objectstack/formula`, metadata-admin
`view-item-normalize.ts` | runtime; objectstack#20646's |
| `/system` | 1,039,623 | 281,625 | `views/FlowRunner.tsx` (runtime),
metadata-admin `metadata-form-i18n.ts`, `previews/doc-draft.ts` |
runtime |
| `/kernel` | 886,545 | 247,041 | `@object-ui/data-objectstack`
(runtime), preview and designer modules | runtime |
| `/data` | 614,780 | 172,275 | core, fields, grid, detail, form,
client, formula | runtime |
| `/ui` | 584,593 | 156,024 | shell, layout, components, grid, list,
view | runtime |
| `/shared` | 368,108 | 81,754 | `preview/DraftChangesPanel.tsx`
(through `ConsoleLayout`'s draft preview bar),
`inspectors/ObjectFieldInspector.tsx`,
`previews/simulator/flow-sim-validate.ts` | admin/designer code, eager
through the layout and the registration leaf |
| `/api` | 322,818 | 90,817 | core, data-objectstack, client, formula |
runtime |
| `/automation` | 127,672 | 37,283 | `utils/approverIdentity.ts`
(runtime, `DeclaredActionsBar`) plus metadata-admin inspectors | runtime
|
| `/ai` | 108,241 | 32,100 | **none**: only `clientValidation.ts`,
through `await import()` | lazy in source, eager by the chunk group |
| `/security` | 58,779 | 18,500 | formula, plugin-grid | runtime |
| `/identity` | 26,836 | 9,348 | `inspectors/FlowReferenceField.tsx`,
eager through `register-builtins.ts` | admin-only, eager through the
registration leaf |
| `/integration` | 23,392 | 6,981 | **none**: only
`clientValidation.ts`, through `await import()` | lazy in source, eager
by the chunk group |
| `/contracts` | 1,467 | 639 | none (lazy console pages, the linter) |
lazy in source, eager by the chunk group |
| `@objectstack/sdui-parser` (not spec) | 19,801 | 5,694 | none (the
linter only) | lazy in source, eager by the chunk group |
⇒ The hypothesis held only in part. `clientValidation.ts` already loads
every spec entry through `await import()`. What kept `/ai` and
`/integration` on the first screen was the `vendor-objectstack` chunk
group: a group claims by module id, not by reachability. That is the
objectui#5266 mechanism, one package over; the lookaheads in
`VENDOR_OBJECTSTACK_TEST` only ever excluded `@objectstack/lint`. Every
other entry the validator loads is also imported statically by something
on the first screen, so no lazy import in the validator can move it.
## The change
1. `apps/console/vite.config.ts`: the `vendor-objectstack` group gets
`tags: ['$initial']`. That is rolldown's built-in tag for "statically
reachable from an entry", so the group now claims only the entry's
static closure. A vendor module that sits behind an `import()` follows
its importer into a lazy chunk. Nothing the first screen executes moves,
because `$initial` IS that closure. The lint lookaheads stay. A
group-table pin in the budget's unit test records the tag.
2. `scripts/check-eager-closure-budget.mjs`: the ceilings come down by
exactly what was recovered (below). The owed `squashMerge` handle for
`048e7f6` is back-filled, and the near-ceiling `framework` fixture pair
holds the aggregate at `BASELINE` by construction.
3. A patch changeset for `@object-ui/console`, whose published `dist`
changes chunk layout. `check-changeset-presence` owes none, because no
published source moved.
## Eager closure, before and after
Two builds of one tree, one container, one instrument (`CI=true pnpm
exec vite build` in `apps/console`, reading
`apps/console/dist/eager-closure.json`), in one `os-verify-lock.sh`
hold. The trees differ only in the vite.config line; the before leg put
`5262f7d`'s blob on disk and the after leg restored `HEAD`'s, each
proven by blob hash.
| | `5262f7d` (`main`) | `4acbea07` (this PR) | delta |
|---|---:|---:|---:|
| aggregate eager closure, gzip | 3,671,724 | 3,638,104 | **−33,620** |
| `vendor-objectstack`, gzip | 1,704,699 | 1,670,964 | −33,735 |
| the other 329 eager chunks, gzip | 1,967,025 | 1,967,140 | +115
(import bookkeeping, 31 chunks, none over 20) |
| aggregate eager closure, raw | 12,366,134 | 12,257,433 | −108,701 |
| eager / total chunks | 330 / 2447 | 330 / 2450 | three new lazy
chunks: `ai`, `integration`, `contracts` |
Noise: the instrument is deterministic per tree. The untagged config
read 3,671,724 on three builds (the graph-dump build, the canonical
before leg, the ablation's mutated leg), and the tagged config read
3,638,104 on three (the graph-dump build, the canonical after leg, the
ablation's restored leg). The recovered 33,620 is 0.37x the 89 KiB
regression the gate exists to catch.
## The ceiling: lowered by the recovered amount
| constant | before | after |
|---|---:|---:|
| `MAX_EAGER_CLOSURE_GZIP_BYTES` | 3,694,000 | **3,660,380** (−33,620) |
| `BASELINE` | 3,648,737 on `048e7f6` | 3,638,104 on `4acbea07` |
| `PER_CHUNK_GZIP_CEILINGS['vendor-objectstack']` | 1,723,000 |
**1,689,265** (−33,735) |
| `PER_CHUNK_BASELINE['vendor-objectstack']` | 1,703,690 on `048e7f6` |
1,670,964 on `4acbea07` |
The ceilings are not re-derived as "reading + half a regression". That
would absorb `main`'s own drift since `048e7f6` (+22,987 on `5262f7d`)
into the ceiling, which is a raise under another name. The live headroom
`main` had before the payback is kept to the byte: 22,276 (0.24x) on the
aggregate and 18,301 (0.20x) on `vendor-objectstack`. No build that
passed before and measures under 3,660,380 fails after.
`REGRESSION_THIS_GATE_MUST_CATCH_BYTES` and the other three per-chunk
rows did not move.
**`squashMerge`:** `048e7f6` landed on `main` as `81f8498` (#11086,
single parent, an ancestor of `origin/main`). That handle is back-filled
in the header prose that still cites the `048e7f6` reading. This change
re-pins `BASELINE` onto its own reading, so the field is `null` again by
its own rule ("the change that re-pins the field above cannot write its
own here"). The `4acbea07` → squash back-fill is owed once this merges.
## Ablation: re-eager, and the lowered ceiling goes red
`node ../objectstack/scripts/ablation-replace.mjs` dropped `tags:
['$initial']` (anchor 1 → 0, blob `40f6d42c` → `32c403ab`), rebuilt, and
ran the gate:
- **mutated:** 3,671,724 bytes, `node
scripts/check-eager-closure-budget.mjs` **exit 1**. The aggregate line
read "3585.7 KB … 11.1 KB over the 3574.6 KB budget", and
`vendor-objectstack` read "1664.7 KB / 1649.7 KB ceiling (OVER by 15.1
KB)".
- **restored:** blob back to `HEAD` (`40f6d42c`), `git diff HEAD` empty.
The rebuild read 3,638,104 and the gate **exit 0**.
## Validation unchanged once a designer opens
- **Probe on the BUILT console** (`vite preview`, proxied to a `--fresh`
objectstack 17.5.0 showcase backend started by
`e2e/live/ci/start-backend.sh`, Playwright +
`/opt/pw-browsers/chromium`). The probe ran two builds of the same tree,
with and without the tag.
- First screen (`/apps/showcase_app`, 331 JS requests): no `ai-*` or
`integration-*` chunk is fetched.
- `/apps/showcase_app/metadata/agent/new`: opening the designer fetches
`ai-rfegJgG7.js`. Typing `Bad Name!` into Name raises "This metadata
does not match the spec — 4 validation error(s). Name: Invalid string:
must match pattern …".
- `/apps/showcase_app/metadata/connector/new`: this designer fetches
`integration-DM--okeM.js`, and the 3-error banner shows.
- The validator's text is **byte-identical** between the untagged and
the tagged build, for both designers.
- The only non-2xx was `GET /api/v1/usage/storage` 404 from the backend,
present on both builds. No `pageerror`.
- **Existing tests, all of
`packages/app-shell/src/views/metadata-admin/**` (373 files):** 36
clientValidation / ResourceEditPage / createConformance files plus 1
console file, 398 passed. The other 337 files ran in three chunks: 1066
passed; 1455 passed and 1 skipped; 1462 passed.
## Verification (head `7d24740c`)
- Console build + budget, before and after, under the lock: the table
above. Checker exit 0 on both. The after build gets exit 0 at the new
constants: "3552.8 KB … budget: 3574.6 KB, headroom: 21.8 KB".
- `scripts/__tests__`: the 18 files that read
`apps/console/vite.config.ts` or the budget script, 1020 passed. That
includes `check-eager-closure-budget.test.ts` at 179, plus
`vite-objectstack-spec-dist`, `vite-objectstack-client-dist`,
`render-budget-comment` and `check-node-esm-load`.
- `apps/console` vitest project: 139 files, 1593 passed.
- `pnpm --filter @object-ui/console run type-check` exit 0. Reverse
check: `tags: ['$bogus']` gives `tsc -b tsconfig.node.json` exit 1 with
TS2769 at the group, then restored.
- `pnpm --filter @object-ui/app-shell run type-check` exit 0.
- Both type-checks ran after `turbo run build
--filter='@object-ui/console^...'`.
- `pnpm check:control-bytes` OK.
- `node scripts/check-changeset-presence.mjs` exit 0.
- `pnpm changeset:check` exit 0.
- `pnpm check:changeset-claims` exit 0, report-only. It shows two
pending bodies naming files touched here,
`6681-declared-lazy-marketplace-routes.md` and
`7173-ai-pending-actions-inbox-i18n.md`. I read both, and this diff
falsifies neither.
- `pnpm check:new-line-citations` 0 new.
- Narrowed lint: `eslint --no-inline-config --format json` on the 3
changed code files gives 3 files, 0 errors, 0 warnings.
- Population: all three are inside `eslint.config.js`'s population; none
was reported ignored.
- Invariance: the config enables no type-aware linting, so this diff
cannot move a verdict on an untouched file.
- **NOT MEASURED:**
- The full `@object-ui/app-shell` suite outside `metadata-admin/**`,
because its ~600 remaining files exceed the foreground window on a box
shared by three devs. `packages/app-shell/src` is untouched by this
diff, and CI runs the suite.
- The repo-wide `pnpm lint`, which CI owns.
## Acceptance notes (observations, not filed)
- `/shared` (81,754 gzip alone) and `/identity` (9,348) are reached on
the first screen only by designer code. Their routes in are
`register-builtins.ts` → `inspectors/index.ts` / `previews/index.ts`,
which the package entry bare-imports, and `ConsoleLayout` →
`DraftPreviewBar` → `DraftChangesPanel`. Recovering them means
lazy-registering the inspector and preview tables, which is an
architecture change, not this card. Carrier: none.
- `DraftChangesPanel.tsx`'s comment says `@objectstack/core` (inside
`@objectstack/client`) has a runtime import of
`@objectstack/spec/shared` that keeps that subpath eager. In this 17.5.0
bundle, `/shared`'s importers are the three objectui modules above plus
the linter. The comment's premise no longer holds. Carrier: none.
- `e2e/live/studio-editor.spec.ts` waits on
`getByTestId('metadata-edit-page')`. `git grep` finds that string in no
other tracked file, and the agent and connector edit pages rendered
without it (this probe's first wait on it timed out). Whether the live
suite still runs that spec, and how it passes, is NOT MEASURED. Carrier:
none.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: 黄怡 <huangyi@hotoa.com>1 parent db0beb2 commit 993f27e
4 files changed
Lines changed: 227 additions & 59 deletions
File tree
- .changeset
- apps/console
- scripts
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
925 | 925 | | |
926 | 926 | | |
927 | 927 | | |
928 | | - | |
| 928 | + | |
| 929 | + | |
| 930 | + | |
| 931 | + | |
| 932 | + | |
| 933 | + | |
| 934 | + | |
| 935 | + | |
| 936 | + | |
| 937 | + | |
| 938 | + | |
| 939 | + | |
| 940 | + | |
| 941 | + | |
| 942 | + | |
| 943 | + | |
| 944 | + | |
| 945 | + | |
| 946 | + | |
| 947 | + | |
| 948 | + | |
| 949 | + | |
| 950 | + | |
| 951 | + | |
| 952 | + | |
| 953 | + | |
| 954 | + | |
| 955 | + | |
| 956 | + | |
| 957 | + | |
| 958 | + | |
929 | 959 | | |
930 | 960 | | |
931 | 961 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
614 | 614 | | |
615 | 615 | | |
616 | 616 | | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
| 628 | + | |
| 629 | + | |
| 630 | + | |
| 631 | + | |
| 632 | + | |
| 633 | + | |
| 634 | + | |
| 635 | + | |
| 636 | + | |
| 637 | + | |
| 638 | + | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
617 | 643 | | |
618 | 644 | | |
619 | 645 | | |
| |||
780 | 806 | | |
781 | 807 | | |
782 | 808 | | |
783 | | - | |
784 | | - | |
785 | | - | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
786 | 813 | | |
787 | 814 | | |
788 | 815 | | |
| |||
1612 | 1639 | | |
1613 | 1640 | | |
1614 | 1641 | | |
1615 | | - | |
| 1642 | + | |
1616 | 1643 | | |
1617 | 1644 | | |
1618 | 1645 | | |
| |||
1759 | 1786 | | |
1760 | 1787 | | |
1761 | 1788 | | |
| 1789 | + | |
| 1790 | + | |
| 1791 | + | |
| 1792 | + | |
| 1793 | + | |
| 1794 | + | |
| 1795 | + | |
| 1796 | + | |
| 1797 | + | |
| 1798 | + | |
1762 | 1799 | | |
| 1800 | + | |
| 1801 | + | |
| 1802 | + | |
1763 | 1803 | | |
1764 | | - | |
| 1804 | + | |
1765 | 1805 | | |
1766 | 1806 | | |
1767 | 1807 | | |
| |||
1781 | 1821 | | |
1782 | 1822 | | |
1783 | 1823 | | |
1784 | | - | |
| 1824 | + | |
1785 | 1825 | | |
1786 | 1826 | | |
1787 | 1827 | | |
| |||
2576 | 2616 | | |
2577 | 2617 | | |
2578 | 2618 | | |
2579 | | - | |
2580 | | - | |
2581 | | - | |
| 2619 | + | |
| 2620 | + | |
| 2621 | + | |
| 2622 | + | |
2582 | 2623 | | |
2583 | 2624 | | |
2584 | 2625 | | |
| |||
0 commit comments