docs(scripts): carry the squash-merge sha beside BASELINE.commit so the provenance resolves with git alone - #9429
Conversation
…rovenance resolves with git alone `BASELINE.commit` names a branch tip and this repository squash-merges, so the object is absent from every `main` checkout. The docblock already rules that correct — naming the tree the reading was taken on is the point — and that ruling is untouched. This adds the other half: `squashMerge`, the squash that carried the same branch onto `main`, which resolves. Carrying it as DATA rather than only as prose is the substance of the change: the existing positive pin holds every commit the constant carries to its own attached prose, so a carried sha cannot go stale in silence, while a prose one is guarded by nothing. No ceiling, baseline, threshold or ratchet moves. The ledger case recording what each baseline carries is re-pinned to the exact new pair, not widened. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
|
| Step | Outcome |
|---|---|
| Build packages | success |
| Check console performance budget | failure |
Which half objected:
| Eager-closure half | Verdict |
|---|---|
| Aggregate closure ceiling | ✅ pass |
| Per-chunk ceilings | ✅ pass |
| Ceiling sensitivity (headroom) | |
| Ceiling freshness (checkout vs. base branch) | ✅ pass |
⚠️ A broken gauge half is a verdict about the ceiling, not about the bundle: that line has drifted out of range of the regression it exists to catch, or the report behind it cannot be trusted. It does not say anything grew. TheCheck console performance budgetstep log carries the ceiling and the number it was compared against.
Reason: The entry chunk measured 144.5 KB, but the eager-closure half of this gate returned no trustworthy VERDICT: the report could not be read, a ceiling has drifted out of range of the regression it must catch, or (objectui#6245) a ceiling was replaced on the base branch after this checkout was made. The step log says which. This is not a passing budget — and it is not a size regression either.
See the workflow run for details.
📦 Bundle Size Report
| Package | Size | Gzipped |
|---|---|---|
| app-shell (consoleActionDispatch.js) | 0.20KB | 0.19KB |
| app-shell (index.js) | 16.69KB | 6.21KB |
| 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) | 25.05KB | 9.16KB |
| auth (AuthContext.js) | 0.31KB | 0.24KB |
| auth (AuthGuard.js) | 2.07KB | 1.00KB |
| auth (AuthProvider.js) | 40.18KB | 10.59KB |
| 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.41KB | 1.23KB |
| 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.46KB | 3.43KB |
| auth (index.js) | 3.19KB | 1.44KB |
| 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) | 26.08KB | 7.56KB |
| 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) | 502.03KB | 115.16KB |
| core (index.js) | 8.52KB | 3.41KB |
| create-plugin (index.js) | 27.94KB | 9.51KB |
| data-objectstack (index.js) | 213.54KB | 59.33KB |
| fields (index.js) | 247.89KB | 62.50KB |
| i18n (LocalizationContext.js) | 1.76KB | 0.96KB |
| i18n (builtinAggregateLabels.js) | 0.86KB | 0.49KB |
| i18n (currency.js) | 1.22KB | 0.64KB |
| i18n (fallbackInterpolation.js) | 6.25KB | 2.77KB |
| i18n (i18n.js) | 8.87KB | 3.64KB |
| i18n (index.js) | 5.22KB | 2.26KB |
| i18n (pickLocalized.js) | 9.86KB | 3.95KB |
| i18n (provider.js) | 32.15KB | 10.49KB |
| i18n (useDisplayLocale.js) | 2.85KB | 1.45KB |
| i18n (useObjectLabel.js) | 34.34KB | 9.17KB |
| i18n (useSafeTranslation.js) | 5.60KB | 2.33KB |
| layout (index.js) | 38.83KB | 10.95KB |
| 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) | 2.53KB | 0.85KB |
| mobile (useResponsive.js) | 0.72KB | 0.42KB |
| mobile (useSpecGesture.js) | 4.39KB | 1.66KB |
| 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.39KB | 3.10KB |
| 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) | 14.81KB | 3.63KB |
| plugin-calendar (index.js) | 49.25KB | 13.99KB |
| plugin-charts (index.js) | 71.34KB | 19.90KB |
| plugin-chatbot (index.js) | 195.34KB | 46.51KB |
| plugin-dashboard (index.js) | 131.44KB | 34.65KB |
| plugin-designer (index.js) | 215.94KB | 44.33KB |
| plugin-detail (index.js) | 253.46KB | 65.85KB |
| plugin-editor (index.js) | 2.23KB | 1.05KB |
| plugin-form (index.js) | 136.71KB | 34.16KB |
| plugin-gantt (index.js) | 166.95KB | 41.04KB |
| plugin-grid (index.js) | 211.68KB | 57.52KB |
| plugin-kanban (index.js) | 46.00KB | 14.30KB |
| plugin-list (index.js) | 112.53KB | 27.63KB |
| plugin-map (index.js) | 21.48KB | 6.99KB |
| plugin-markdown (index.js) | 13.88KB | 4.80KB |
| plugin-report (index.js) | 43.41KB | 11.93KB |
| plugin-timeline (index.js) | 30.07KB | 8.74KB |
| plugin-tree (index.js) | 9.55KB | 3.32KB |
| plugin-view (index.js) | 84.36KB | 20.78KB |
| 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) | 96.00KB | 31.71KB |
| react (data-invalidation.js) | 5.05KB | 2.08KB |
| react (index.js) | 4.63KB | 2.18KB |
| react (schema-input.js) | 4.25KB | 2.04KB |
| react (spec-input.js) | 0.20KB | 0.18KB |
| sdui-parser (codegen.js) | 6.58KB | 2.74KB |
| sdui-parser (dashboard-widget-options.js) | 3.08KB | 1.30KB |
| sdui-parser (index.js) | 5.66KB | 2.50KB |
| 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) | 14.82KB | 4.99KB |
| types (ai.js) | 0.20KB | 0.17KB |
| 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) | 4.73KB | 2.28KB |
| types (navigation.js) | 0.20KB | 0.18KB |
| types (objectql.js) | 0.20KB | 0.18KB |
| types (overlay.js) | 0.20KB | 0.18KB |
| types (permissions.js) | 0.20KB | 0.18KB |
| 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) | 14.04KB | 5.36KB |
| 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
One conflict, in `scripts/check-eager-closure-budget.mjs`, and it is the
re-baseline this branch's own docblock was written to survive: objectui#9251
(squashed onto `main` as `67485872ed`) moved `BASELINE` to a new reading while
this branch was adding `squashMerge` beside the old one.
Resolution, hunk by hunk — all inside the one conflicted region, the `BASELINE`
object literal:
- `gzipBytes`, `chunks`, `totalChunks`, `commit`: `origin/main` wins,
verbatim. This branch moves no number, and the re-baseline is not its
business.
- the `squashMerge` field and its docblock: this branch wins — the side
`origin/main` has no counterpart for.
- the field's VALUE is back-filled, ⛔ not carried forward. Its own docblock
forbids exactly that: "⛔ do not carry this one forward onto a reading it
was not taken with". `BASELINE.commit` now names `bbf6b02d9`, so the value
is the squash that carried THAT tree onto `main`, re-derived here by the
same method the field was first derived with.
- the two `git cat-file` legs are re-pointed at the pair the constant now
names.
- the paragraph citing objectui#9209's 191-byte reading is REMOVED rather
than refreshed. It subtracted from a figure this constant no longer
carries, and the paragraph itself said it was "a fact about THIS pair" that
"says nothing about the next one". Replaced by what is actually established
about the current pair, which is that the distance is not measured.
The changeset loses one clause that the re-baseline falsified: the squash sha
no longer "already appears in the prose". `origin/main`'s rewrite of the
PROVENANCE paragraph deleted the only prose copy, so on `main` today the squash
of the tree the constant names appears nowhere in the repository — which is the
argument for carrying it as data, demonstrated rather than asserted.
The three ruled deliverables are unchanged in substance: the field, the exact
positional ledger re-pin `toEqual([BASELINE.commit, BASELINE.squashMerge])`,
and the back-fill rule. objectui#9355.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013VGeMu3p6qEFWR6K6GGLaW
✅ 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
|
Fixes #9355
Records the squash-merge sha alongside
BASELINE.commitinscripts/check-eager-closure-budget.mjs, as a second clearly-labelled fieldsquashMerge.The fence, and that it held
numbers_touched: NONE. No ceiling, baseline, threshold, ratchet or non-vacuity floor moves, and whatBASELINE.commitnames is unchanged. The diff is three files, and the whole-diff removed-line list is three lines — two of prose and one assertion, all in the ledger case described below.The docblock's standing ruling — that
commitmust name the tree the reading was taken on, and that no commit onmainhas that tree — is quoted back in the new field's own prose and left exactly as written. The new field is explicitly never a substitute for it.The squash sha, re-derived here rather than copied
77b2a18a16is not taken on trust from the card. It is the commit onmainthat introducedcommit: '755d34a5f'into this file, found by content search, and it is a single-parent (squash) commit of PR #9122 — the same pull request whose branch tip the constant names.What actually justifies the field, which is NOT what the card says
⭐ This section INVERTED when⚠️ The card's original stated cost (that the handle "had to be recovered through the compare API") remains falsified as written, and objectui#9209's deliverable was a byte delta needing a console build however the sha is obtained. The justification is the one below, which the re-baseline strengthened rather than weakened.
mainwas merged in, and the inversion is the point. When this branch was cut, the squash handle was already in the file in prose twice, so the card's motivating cost did not hold and round 1 recorded it as falsified. objectui#9251's re-baseline then rewrote the PROVENANCE paragraph and deleted both prose copies. Measured onmaintoday:67485872ed— the squash of the treeBASELINEnow names — appears 0 times in this file, while the retired pair still does (755d34a5f9 hits,77b2a18a162 hits,bbf6b02d94 hits; nonsense control 0). ⇒ the field is no longer a second copy of something prose already carried — it is now the only copy, and this pull request's two added lines are the sole occurrences of that handle in the repository.What the field does buy, which prose cannot: this suite already pins every commit the constant carries as data into its own attached prose (the objectui#7046 pins, written after objectui#6778 let a three-re-baselines-stale hash sit in a comment and draw the reverse of the verdict the same script printed). A hash that lives only in prose is guarded by nothing. Carrying it as data drags it under that existing pin, so it cannot go stale in silence at the next re-baseline. That is the justification this change is made on.
Counted on this base, so the scope question is answered rather than assumed: of 37 sha-shaped tokens in the file, 9 do not resolve in a full non-shallow clone, and exactly 1 of those 9 is carried as data — the row this card names. The other 8 are prose citations, mostly superseded baselines the blocks legitimately cite as history. There is no second row of this shape, in this file or repo-wide (
commit: '...'as a carried provenance field returns exactly one hit outside the test that pins it).The validator the shape change touches, and why re-pinning it is not loosening it
The shape change is seen by exactly one pin,
records what each baseline carries as data, so the pin cannot go vacuous. It is re-pinned to the exact new pair. It is still positional and still exact —toEqual([BASELINE.commit, BASELINE.squashMerge]), not a predicate widened to tolerate either shape.Ablation, run from the committed tree: restore only the test file to the base blob, keeping the new field, and the pin reds on exactly the right line and on nothing else.
144 of 145 still passing under the ablation is the other half of the reading: the positive prose pin accepts the new sha (it is in the attached prose), the
BASELINE's HASHclaim pin is untouched, and no other case sees the field. Restore verified bygit diff HEADexiting 0 with an emptygit status --porcelain, not by a checkout's exit code.The new field's docblock also writes down the one thing a future re-baseline must know: a squash sha does not exist until the pull request merges, so the change that re-pins the constant cannot write its own — the honest value at that moment is
null,nullreds this ledger, and that red is the intended signal to re-pin deliberately rather than to widen.Tests
Tests 145 passed (145),PRE_EXIT=0Tests 145 passed (145),VERDICT command-exit 0unitproject (thescripts/suite)Test Files 1130 passed, 2 skipped;Tests 20266 passed, 4 skipped,VERDICT command-exit 0ABLATION_EXIT=1, one named failurecheck:control-bytescheck:changeset-claimscheck:new-line-citationstype-check:scriptslint:root(full population, not narrowed)Every exit code captured by redirect-then-capture, never across a pipe; heavy runs serialised through the shared verify lock.
Two gate readings that are not measurements of this diff, reported rather than suppressed:
check:eager-closureexits 2 on the pre-existing exhaustedui-componentsrow. The gate's own verdict says this is not an accusation against the diff that happened to be weighed and that the two edits which would turn it green are forbidden; neither was made. This diff cannot reach it: the checker's runtime never readsBASELINEat all — a code-only grep forBASELINE.in the checker returns 0 hits, against 27 in the test file with the same reader.check:node-esm-loadexits 1 onrefused on provenance, naming@object-ui/authand@object-ui/react-runtimereplayed from a sibling agent's worktree through the shared turbo cache. This branch changes no file underpackages/(0 of its 3 changed files; 2 underscripts/), so it cannot be the cause.Bundle Analysisis red across this lane and is not required.Changeset
.changeset/baseline-squash-merge-sha-9355.md, empty frontmatter — this publishes nothing.scripts/**is not one of this repo'sGOVERNED_SURFACES(docs/adr/**,.claude/**,skills/**,AGENTS.md,CLAUDE.md), so no governed-surface reader section is owed; the constant is change-controlled by convention, which is why the fence above is stated explicitly.Draft, deliberately. Not flipped ready, no auto-merge, not enqueued.
🤖 Generated with Claude Code
https://claude.ai/code/session_01FhBNJcLRZLe8M87VcUgpKr
Generated by Claude Code