test(plugin-dashboard): the legacy-retired rows say what they really are, and the fixture claim becomes a read - #7158

Merged
os-warren merged 2 commits into
mainfrom
claude/issue-7151-legacy-retired-provenance
Sep 1, 2026
Merged

test(plugin-dashboard): the legacy-retired rows say what they really are, and the fixture claim becomes a read#7158
os-warren merged 2 commits into
mainfrom
claude/issue-7151-legacy-retired-provenance

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes#7151

Measured on origin/maindd35800af; shipped at 81b5da034.

The routing tree landed on route 3, and both gates were measurements

Route 2 gate — is there a live corpus specimen of the pre-ADR-0021 shape? No. Two independent instruments, each with a live non-zero control on the same instrument:

instrumentsubjectlive control
structural walk of every widgets[] element in all 628 tracked JSON files0 widgets carry object / categoryField / valueField / aggregate / rowField / columnField at top level; 0 the shipped detector would flagpopulation 28 widgets; 15options-shaped, 2dataset-shaped
git grep -c over tracked *.jsoncategoryField0, rowField0, columnField0, aggregate0xField11, dataset2

The only two structural hits anywhere were globalFilters[N].optionsFrom nodes — a filter's option source, not a widget, and a live surface.

A3.4 checked directly rather than inherited: objectui#4600's "all the dashboard catalog examples were migrated" holds against the fixtures, measured, not taken from another card's summary.

Route 1-vs-3 gate — can this package's test tsconfig read a fixture at test time? Yes. packages/plugin-dashboard/tsconfig.test.json declares "types": ["node"], and its own comment records that as load-bearing and measured. Independently confirmed after the fact: the test program loads 82 @types/node files. This is exactly the precondition packages/types/src/__tests__/timeline-catalog-fixture-migrated.test.ts could not meet in plugin-timeline and relocated for. So: route 3.

The card's premise is half falsified — the claim was TRUE when written

The card's title says the fixture "has never carried" the shape, and its "It was never true" section rests on git log returning exactly one commit for both paths. That reading is an artifact.

ad0f5f11fis the shallow clone's graft boundary — the oldest commit present. Every file alive at the boundary reports as added there. Control: of 40 randomly sampled tracked files, 35 return exactly that same one commit, and the boundary commit also reports adding legacyRetiredWidget.ts, which this suite's own header attributes to objectui#4612 — a lower number than the boundary commit's own PR. The instrument can distinguish (package.json returns 18 commits, packages/types/src/complex.ts returns 3); it simply has nothing to say about paths untouched since the boundary.

Recovered the real history from the public commit log without deepening the shared .git (zero-quota, no shared-state mutation), and it inverts the finding:

commitwhenwhat
8640cec192026-08-14T01:05:19Zadded this suite, with the annotated literal
e028dfcd82026-08-14T02:11:37Zmigrated the filtered-* catalog entries off the retired shape (objectui#4600, PR objectui#4615)

66 minutes apart. The fixture as it stood at 8640cec19 has widgets[0] equal to the test literal byte-for-byte — same keys, same order, same values (checked mechanically, with a self-comparison control returning true on the same comparator). So the row was a genuine independent corpus specimen; it was not "a fixture invented to match the detector". This is drift, and the migration that caused it is named in the card's own Refs.

The second row's comment made the looser claim "widgets[2] of the same catalog entry" and was never byte-for-byte; its retired binding was verbatim, with id genericised to w1 and title / filterBindings dropped. Substantially true then, false now.

Both are false against today's fixture — that part of the card stands, mechanically confirmed key-for-key.

What changed

Both annotations now state the measured history and say explicitly that the rows no longer carry the independent-corpus property — they are historical specimens of stored metadata, which is still the right thing to pin, but is weaker evidence and now says so rather than leaving the next reader to re-derive the confidence.

Route 3's half: the relationship stops being prose. A derived block reads the same fixture at test time and

  • pins that no shipped catalog widget authors the retired binding — the executable form of the sentence that used to be a false comment, and a catalog-regression guard in the other direction;
  • runs the three real shipped widgets through the surface as corpus-derived negative controls. That restores the independent-corpus property on the side where the corpus still has specimens.

The property cannot be restored for the legacy rows themselves, and the block says why: the zero is by design. The retirement's whole content is that no authoring surface emits the shape — WidgetConfigPanel scrubs it on save via LEGACY_ANALYTICS_KEYS — and the catalog is an authoring corpus.

A3.3 sweep — no third false claim

Swept every remaining provenance claim in the file and checked each mechanically. All hold:

  • object / categoryField / aggregate are not on DashboardWidgetSchema — enumerated the spec Zod schema's 22 top-level keys plus objectui's own additions; controls dataset / dimensions / values / options all present.
  • DashboardGridLayout is separately exported and registered as dashboard-grid.
  • One detector module, two consumers; one shared placeholder constant, one wording.
  • Pivot's legacy spelling really is rowField / columnField — both in LEGACY_ANALYTICS_KEYS.
  • DashboardRenderer really does return the placeholder family-wide for pivot, and this surface really does have a pivot renderer.
  • The provider: 'object' config really is read off widgetData, never the widget top level.

Tests, at 81b5da034

Whole package, from the repo root: 85 files, 800 tests, all passing. This suite alone went 9 passing to 14.

Ablation, direction predicted before running, on the committed tree:

legmutationpredictedobserved
Aadd top-level object to widgets[0], keep options.datashape pin red, render control green1 failed / 13 passed — only the shape pin
Brevert widgets[0] to its 8640cec19 retired formboth red2 failed / 12 passed — shape pin and the derived widgets[0] control

Mutation proved on disk by blob hash change plus injected-marker count, never an editor's exit code; restore proved by state — blob hash back to the HEAD blob and git diff HEAD, git diff --cached, git status --short all empty; absolute paths and an EXIT INT TERM trap throughout. The restored tree re-measured at 14 passing. One caveat reported rather than quoted as evidence: leg B's "removed marker" check used xField, which is not unique to widgets[0], so it carries no information; the hash and the injected-marker count are what prove that leg.

So this is not the objectui#7102 situation where no available pin could fail — both new pins are independently falsifiable, shown two different ways.

Gates: check:control-bytes, check:phantom-deps (green on the new node: builtin imports), check:esm-specifiers, check:vi-mock-specifiers, check:vi-mock-inherit, type-check:coverage all exit 0. pnpm --filter @object-ui/plugin-dashboard type-check exits 0, and --listFiles confirms the edited file is genuinely in the test program rather than silently excluded. eslint . for the package: 0 errors, and 0 findings on the edited file (control: sibling files do appear).

Changeset verdict quoted rather than predicted — empty frontmatter, per the gate's own named exemption:

1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/7151-legacy-retired-provenance.md.
Every one of them has an EMPTY frontmatter — declared as releasing nothing, which is the explicit exemption and a complete answer to this gate.

Scope

One test file plus one changeset. No published type, export, or parse behaviour is touched, so the contract-review tier is not engaged. Nothing under content/docs/releases/, and the schema-catalog fixture is unmodified in the diff — it was read, and temporarily mutated only inside the ablation, which restored it provably.

Stayed clear of the objectui#6875 lane: no relational-meta file and no CELL_RELATIONAL_META_KEYS.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…are, and the fixture claim becomes a read
The two annotated `it.each` rows in DashboardGridLayout.legacyRetired.test.tsx
introduced their literals with provenance comments naming
`examples/schema-catalog/src/schemas/plugin-dashboard/filtered-dashboard.json`.
Both comments are false against that fixture as it stands today, mechanically
checked key-for-key.
They were true when written. `8640cec19` added this file at 01:05:19Z with a
literal byte-for-byte identical to the fixture's `widgets[0]` — same keys, same
order, same values — and `e028dfcd8` (objectui#4600, PR #4615) migrated the
`filtered-*` catalog entries off the retired shape at 02:11:37Z, 66 minutes
later. So this is drift, not an invented fixture.
The annotations now state the measured history and say plainly that the rows no
longer carry the independent-corpus property. The relationship they assert stops
being prose: a derived block reads the same fixture at test time, pins that no
shipped catalog widget authors the retired binding, and runs the three real
shipped widgets through the surface as corpus-derived negative controls.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
…easing nothing
Empty frontmatter: the change is one test file in `plugin-dashboard`, with no
published behaviour moving. `check-changeset-presence.mjs` names this as the
explicit exemption rather than a workaround.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3152.6 KB3191.4 KB
Main entry chunk (gzip)142.6 KB350 KB
Entry fileindex-QE-58LTY.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.33KB5.59KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)512.32KB116.52KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)177.67KB49.45KB
fields (index.js)244.25KB61.73KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)65.94KB18.64KB
plugin-chatbot (index.js)190.53KB45.18KB
plugin-dashboard (index.js)132.61KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)248.93KB63.56KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)133.11KB32.61KB
plugin-gantt (index.js)165.21KB40.37KB
plugin-grid (index.js)202.31KB54.66KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.21KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)29.34KB8.47KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-warrenClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Reviewed and armed

All 30 check runs in one call at perPage=100: 27 success, 3 skipped, 0 failed, 0 in_progress. Undrafted, auto-merge SQUASH enabled at 81b5da034.

⚠️ This PR inverted its own card — and my comment on it

premise_still_valid: false, and it is the most valuable result of the session.

objectui#7151 claimed the test's provenance comment "was never true", on git log showing fixture and test added in one commit (ad0f5f11f). The card correctly bounded that to visible history on a shallow clone. I read the bound and reasoned past it anyway, writing on the card: "so this was never drift — the claim was wrong when it was written", and calling it the sharp part. That was wrong, and I have posted the correction on objectui#7151.

You tested the instrument instead of trusting it:

  • 35 of 40 randomly sampled tracked files report that same commit as their "add." ad0f5f11f is the graft boundary, reporting every file alive at the cut as newly added.
  • The boundary commit also reports adding legacyRetiredWidget.ts, which this suite's own header attributes to objectui#4612 — a lower number than the boundary commit's own PR. The contradiction was inside the reading, for anyone who looked.

And the real history reverses the verdict:

committimewhat
8640cec1901:05:19Zadded the suite
e028dfcd802:11:37Zmigrated the filtered-* entries off the retired shape

66 minutes apart, with the fixture at the earlier sha byte-for-byte equal to the test literal (checked mechanically, with a self-comparison control). ⇒ the row was a genuine independent corpus specimen, not invented to match the detector. This is drift — caused by a migration named in the card's own Refs.

Recovering that from the public commit log without deepening the shared .git is the right move in this environment, and it is the only reason the truth was reachable at all.

The other half of the card stands, confirmed key-for-key: both comments are false against today's fixture. That is why the repair was still needed.

Route 3 selected by both gates, not by preference

Gate 1 (route 2 — is there a live corpus specimen?) — a structural walk of every widgets[] element across 628 tracked JSON files: 0 carrying the retired keys and 0 the shipped detector would flag, against live controls of 28 widgets in the population, 15 options-shaped, 2 dataset-shaped. An independent git-grep instrument agreed. The only structural hits anywhere were globalFilters[N].optionsFrom nodes — a filter option source, not a widget. So objectui#4600's summary is confirmed by direct measurement, not inherited.

Gate 2 (route 1 vs 3)plugin-dashboard/tsconfig.test.json declares types: [node], the precondition the packages/types precedent could not meet and relocated for; confirmed afterwards by 82 @types/node files in the program. So route 3 was available, which is what made it correct rather than aspirational.

The honest limit, stated rather than papered over

The independent-corpus property cannot be restored for the legacy rows, and the reason is structural: the zero is by design, because the retirement's whole content is that no authoring surface emits the shape (WidgetConfigPanel scrubs it via LEGACY_ANALYTICS_KEYS) and the catalog is an authoring corpus. So the derived block restores the property on the side where the corpus still has specimens, and the annotations say plainly that the legacy rows no longer carry it — which is exactly what I ruled route 1 would have had to do, arrived at from the other direction.

A3.3: no third false claim. Every remaining provenance claim in the file was checked mechanically and all hold, with the neighbourhood swept too — the sibling DashboardRenderer.legacyRetired.test.tsx carries no provenance claim, and app-shell's "VERBATIM transcription" comments are the well-behaved version of the pattern (they state the claim must be re-earned, and pin it).

Evidence

Two ablation legs distinguishing the two new pins, direction predicted before running: leg A 1 failed | 13 passed (14), leg B 2 failed | 12 passed (14), both failures named. Not green-always pins — the objectui#7102 trap, avoided by demonstration.

⚠️ And you volunteered that one of your own checks was worthless: leg B's "removed marker" used xField, which is not unique to widgets[0], so it returned 1 and carries no information — the blob-hash change and injected-marker count are what prove that leg. Naming a check that did not measure what it claimed, inside an otherwise green report, is the same discipline that caught the graft boundary. It is also the second self-caught broken instrument in this round (objectui#7157's dev found theirs too), and I broke one myself earlier today.


Generated by Claude Code

Merged via the queue into main with commit 51449a0Sep 1, 2026
32 checks passed
@os-warren
os-warren deleted the claude/issue-7151-legacy-retired-provenance branch September 1, 2026 07:33
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@os-warren@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

test(plugin-dashboard): the legacy-retired rows say what they really are, and the fixture claim becomes a read - #7158

Merged
os-warren merged 2 commits into
mainfrom
claude/issue-7151-legacy-retired-provenance
Sep 1, 2026
Merged

test(plugin-dashboard): the legacy-retired rows say what they really are, and the fixture claim becomes a read#7158
os-warren merged 2 commits into
mainfrom
claude/issue-7151-legacy-retired-provenance

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes#7151

Measured on origin/maindd35800af; shipped at 81b5da034.

The routing tree landed on route 3, and both gates were measurements

Route 2 gate — is there a live corpus specimen of the pre-ADR-0021 shape? No. Two independent instruments, each with a live non-zero control on the same instrument:

instrumentsubjectlive control
structural walk of every widgets[] element in all 628 tracked JSON files0 widgets carry object / categoryField / valueField / aggregate / rowField / columnField at top level; 0 the shipped detector would flagpopulation 28 widgets; 15options-shaped, 2dataset-shaped
git grep -c over tracked *.jsoncategoryField0, rowField0, columnField0, aggregate0xField11, dataset2

The only two structural hits anywhere were globalFilters[N].optionsFrom nodes — a filter's option source, not a widget, and a live surface.

A3.4 checked directly rather than inherited: objectui#4600's "all the dashboard catalog examples were migrated" holds against the fixtures, measured, not taken from another card's summary.

Route 1-vs-3 gate — can this package's test tsconfig read a fixture at test time? Yes. packages/plugin-dashboard/tsconfig.test.json declares "types": ["node"], and its own comment records that as load-bearing and measured. Independently confirmed after the fact: the test program loads 82 @types/node files. This is exactly the precondition packages/types/src/__tests__/timeline-catalog-fixture-migrated.test.ts could not meet in plugin-timeline and relocated for. So: route 3.

The card's premise is half falsified — the claim was TRUE when written

The card's title says the fixture "has never carried" the shape, and its "It was never true" section rests on git log returning exactly one commit for both paths. That reading is an artifact.

ad0f5f11fis the shallow clone's graft boundary — the oldest commit present. Every file alive at the boundary reports as added there. Control: of 40 randomly sampled tracked files, 35 return exactly that same one commit, and the boundary commit also reports adding legacyRetiredWidget.ts, which this suite's own header attributes to objectui#4612 — a lower number than the boundary commit's own PR. The instrument can distinguish (package.json returns 18 commits, packages/types/src/complex.ts returns 3); it simply has nothing to say about paths untouched since the boundary.

Recovered the real history from the public commit log without deepening the shared .git (zero-quota, no shared-state mutation), and it inverts the finding:

commitwhenwhat
8640cec192026-08-14T01:05:19Zadded this suite, with the annotated literal
e028dfcd82026-08-14T02:11:37Zmigrated the filtered-* catalog entries off the retired shape (objectui#4600, PR objectui#4615)

66 minutes apart. The fixture as it stood at 8640cec19 has widgets[0] equal to the test literal byte-for-byte — same keys, same order, same values (checked mechanically, with a self-comparison control returning true on the same comparator). So the row was a genuine independent corpus specimen; it was not "a fixture invented to match the detector". This is drift, and the migration that caused it is named in the card's own Refs.

The second row's comment made the looser claim "widgets[2] of the same catalog entry" and was never byte-for-byte; its retired binding was verbatim, with id genericised to w1 and title / filterBindings dropped. Substantially true then, false now.

Both are false against today's fixture — that part of the card stands, mechanically confirmed key-for-key.

What changed

Both annotations now state the measured history and say explicitly that the rows no longer carry the independent-corpus property — they are historical specimens of stored metadata, which is still the right thing to pin, but is weaker evidence and now says so rather than leaving the next reader to re-derive the confidence.

Route 3's half: the relationship stops being prose. A derived block reads the same fixture at test time and

  • pins that no shipped catalog widget authors the retired binding — the executable form of the sentence that used to be a false comment, and a catalog-regression guard in the other direction;
  • runs the three real shipped widgets through the surface as corpus-derived negative controls. That restores the independent-corpus property on the side where the corpus still has specimens.

The property cannot be restored for the legacy rows themselves, and the block says why: the zero is by design. The retirement's whole content is that no authoring surface emits the shape — WidgetConfigPanel scrubs it on save via LEGACY_ANALYTICS_KEYS — and the catalog is an authoring corpus.

A3.3 sweep — no third false claim

Swept every remaining provenance claim in the file and checked each mechanically. All hold:

  • object / categoryField / aggregate are not on DashboardWidgetSchema — enumerated the spec Zod schema's 22 top-level keys plus objectui's own additions; controls dataset / dimensions / values / options all present.
  • DashboardGridLayout is separately exported and registered as dashboard-grid.
  • One detector module, two consumers; one shared placeholder constant, one wording.
  • Pivot's legacy spelling really is rowField / columnField — both in LEGACY_ANALYTICS_KEYS.
  • DashboardRenderer really does return the placeholder family-wide for pivot, and this surface really does have a pivot renderer.
  • The provider: 'object' config really is read off widgetData, never the widget top level.

Tests, at 81b5da034

Whole package, from the repo root: 85 files, 800 tests, all passing. This suite alone went 9 passing to 14.

Ablation, direction predicted before running, on the committed tree:

legmutationpredictedobserved
Aadd top-level object to widgets[0], keep options.datashape pin red, render control green1 failed / 13 passed — only the shape pin
Brevert widgets[0] to its 8640cec19 retired formboth red2 failed / 12 passed — shape pin and the derived widgets[0] control

Mutation proved on disk by blob hash change plus injected-marker count, never an editor's exit code; restore proved by state — blob hash back to the HEAD blob and git diff HEAD, git diff --cached, git status --short all empty; absolute paths and an EXIT INT TERM trap throughout. The restored tree re-measured at 14 passing. One caveat reported rather than quoted as evidence: leg B's "removed marker" check used xField, which is not unique to widgets[0], so it carries no information; the hash and the injected-marker count are what prove that leg.

So this is not the objectui#7102 situation where no available pin could fail — both new pins are independently falsifiable, shown two different ways.

Gates: check:control-bytes, check:phantom-deps (green on the new node: builtin imports), check:esm-specifiers, check:vi-mock-specifiers, check:vi-mock-inherit, type-check:coverage all exit 0. pnpm --filter @object-ui/plugin-dashboard type-check exits 0, and --listFiles confirms the edited file is genuinely in the test program rather than silently excluded. eslint . for the package: 0 errors, and 0 findings on the edited file (control: sibling files do appear).

Changeset verdict quoted rather than predicted — empty frontmatter, per the gate's own named exemption:

1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/7151-legacy-retired-provenance.md.
Every one of them has an EMPTY frontmatter — declared as releasing nothing, which is the explicit exemption and a complete answer to this gate.

Scope

One test file plus one changeset. No published type, export, or parse behaviour is touched, so the contract-review tier is not engaged. Nothing under content/docs/releases/, and the schema-catalog fixture is unmodified in the diff — it was read, and temporarily mutated only inside the ablation, which restored it provably.

Stayed clear of the objectui#6875 lane: no relational-meta file and no CELL_RELATIONAL_META_KEYS.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…are, and the fixture claim becomes a read
The two annotated `it.each` rows in DashboardGridLayout.legacyRetired.test.tsx
introduced their literals with provenance comments naming
`examples/schema-catalog/src/schemas/plugin-dashboard/filtered-dashboard.json`.
Both comments are false against that fixture as it stands today, mechanically
checked key-for-key.
They were true when written. `8640cec19` added this file at 01:05:19Z with a
literal byte-for-byte identical to the fixture's `widgets[0]` — same keys, same
order, same values — and `e028dfcd8` (objectui#4600, PR #4615) migrated the
`filtered-*` catalog entries off the retired shape at 02:11:37Z, 66 minutes
later. So this is drift, not an invented fixture.
The annotations now state the measured history and say plainly that the rows no
longer carry the independent-corpus property. The relationship they assert stops
being prose: a derived block reads the same fixture at test time, pins that no
shipped catalog widget authors the retired binding, and runs the three real
shipped widgets through the surface as corpus-derived negative controls.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
…easing nothing
Empty frontmatter: the change is one test file in `plugin-dashboard`, with no
published behaviour moving. `check-changeset-presence.mjs` names this as the
explicit exemption rather than a workaround.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3152.6 KB3191.4 KB
Main entry chunk (gzip)142.6 KB350 KB
Entry fileindex-QE-58LTY.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.33KB5.59KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)512.32KB116.52KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)177.67KB49.45KB
fields (index.js)244.25KB61.73KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)65.94KB18.64KB
plugin-chatbot (index.js)190.53KB45.18KB
plugin-dashboard (index.js)132.61KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)248.93KB63.56KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)133.11KB32.61KB
plugin-gantt (index.js)165.21KB40.37KB
plugin-grid (index.js)202.31KB54.66KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.21KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)29.34KB8.47KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-warrenClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Reviewed and armed

All 30 check runs in one call at perPage=100: 27 success, 3 skipped, 0 failed, 0 in_progress. Undrafted, auto-merge SQUASH enabled at 81b5da034.

⚠️ This PR inverted its own card — and my comment on it

premise_still_valid: false, and it is the most valuable result of the session.

objectui#7151 claimed the test's provenance comment "was never true", on git log showing fixture and test added in one commit (ad0f5f11f). The card correctly bounded that to visible history on a shallow clone. I read the bound and reasoned past it anyway, writing on the card: "so this was never drift — the claim was wrong when it was written", and calling it the sharp part. That was wrong, and I have posted the correction on objectui#7151.

You tested the instrument instead of trusting it:

  • 35 of 40 randomly sampled tracked files report that same commit as their "add." ad0f5f11f is the graft boundary, reporting every file alive at the cut as newly added.
  • The boundary commit also reports adding legacyRetiredWidget.ts, which this suite's own header attributes to objectui#4612 — a lower number than the boundary commit's own PR. The contradiction was inside the reading, for anyone who looked.

And the real history reverses the verdict:

committimewhat
8640cec1901:05:19Zadded the suite
e028dfcd802:11:37Zmigrated the filtered-* entries off the retired shape

66 minutes apart, with the fixture at the earlier sha byte-for-byte equal to the test literal (checked mechanically, with a self-comparison control). ⇒ the row was a genuine independent corpus specimen, not invented to match the detector. This is drift — caused by a migration named in the card's own Refs.

Recovering that from the public commit log without deepening the shared .git is the right move in this environment, and it is the only reason the truth was reachable at all.

The other half of the card stands, confirmed key-for-key: both comments are false against today's fixture. That is why the repair was still needed.

Route 3 selected by both gates, not by preference

Gate 1 (route 2 — is there a live corpus specimen?) — a structural walk of every widgets[] element across 628 tracked JSON files: 0 carrying the retired keys and 0 the shipped detector would flag, against live controls of 28 widgets in the population, 15 options-shaped, 2 dataset-shaped. An independent git-grep instrument agreed. The only structural hits anywhere were globalFilters[N].optionsFrom nodes — a filter option source, not a widget. So objectui#4600's summary is confirmed by direct measurement, not inherited.

Gate 2 (route 1 vs 3)plugin-dashboard/tsconfig.test.json declares types: [node], the precondition the packages/types precedent could not meet and relocated for; confirmed afterwards by 82 @types/node files in the program. So route 3 was available, which is what made it correct rather than aspirational.

The honest limit, stated rather than papered over

The independent-corpus property cannot be restored for the legacy rows, and the reason is structural: the zero is by design, because the retirement's whole content is that no authoring surface emits the shape (WidgetConfigPanel scrubs it via LEGACY_ANALYTICS_KEYS) and the catalog is an authoring corpus. So the derived block restores the property on the side where the corpus still has specimens, and the annotations say plainly that the legacy rows no longer carry it — which is exactly what I ruled route 1 would have had to do, arrived at from the other direction.

A3.3: no third false claim. Every remaining provenance claim in the file was checked mechanically and all hold, with the neighbourhood swept too — the sibling DashboardRenderer.legacyRetired.test.tsx carries no provenance claim, and app-shell's "VERBATIM transcription" comments are the well-behaved version of the pattern (they state the claim must be re-earned, and pin it).

Evidence

Two ablation legs distinguishing the two new pins, direction predicted before running: leg A 1 failed | 13 passed (14), leg B 2 failed | 12 passed (14), both failures named. Not green-always pins — the objectui#7102 trap, avoided by demonstration.

⚠️ And you volunteered that one of your own checks was worthless: leg B's "removed marker" used xField, which is not unique to widgets[0], so it returned 1 and carries no information — the blob-hash change and injected-marker count are what prove that leg. Naming a check that did not measure what it claimed, inside an otherwise green report, is the same discipline that caught the graft boundary. It is also the second self-caught broken instrument in this round (objectui#7157's dev found theirs too), and I broke one myself earlier today.


Generated by Claude Code

Merged via the queue into main with commit 51449a0Sep 1, 2026
32 checks passed
@os-warren
os-warren deleted the claude/issue-7151-legacy-retired-provenance branch September 1, 2026 07:33
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@os-warren@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(plugin-dashboard): the legacy-retired rows say what they really are, and the fixture claim becomes a read - #7158

Merged
os-warren merged 2 commits into
mainfrom
claude/issue-7151-legacy-retired-provenance
Sep 1, 2026
Merged

test(plugin-dashboard): the legacy-retired rows say what they really are, and the fixture claim becomes a read#7158
os-warren merged 2 commits into
mainfrom
claude/issue-7151-legacy-retired-provenance

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes#7151

Measured on origin/maindd35800af; shipped at 81b5da034.

The routing tree landed on route 3, and both gates were measurements

Route 2 gate — is there a live corpus specimen of the pre-ADR-0021 shape? No. Two independent instruments, each with a live non-zero control on the same instrument:

instrumentsubjectlive control
structural walk of every widgets[] element in all 628 tracked JSON files0 widgets carry object / categoryField / valueField / aggregate / rowField / columnField at top level; 0 the shipped detector would flagpopulation 28 widgets; 15options-shaped, 2dataset-shaped
git grep -c over tracked *.jsoncategoryField0, rowField0, columnField0, aggregate0xField11, dataset2

The only two structural hits anywhere were globalFilters[N].optionsFrom nodes — a filter's option source, not a widget, and a live surface.

A3.4 checked directly rather than inherited: objectui#4600's "all the dashboard catalog examples were migrated" holds against the fixtures, measured, not taken from another card's summary.

Route 1-vs-3 gate — can this package's test tsconfig read a fixture at test time? Yes. packages/plugin-dashboard/tsconfig.test.json declares "types": ["node"], and its own comment records that as load-bearing and measured. Independently confirmed after the fact: the test program loads 82 @types/node files. This is exactly the precondition packages/types/src/__tests__/timeline-catalog-fixture-migrated.test.ts could not meet in plugin-timeline and relocated for. So: route 3.

The card's premise is half falsified — the claim was TRUE when written

The card's title says the fixture "has never carried" the shape, and its "It was never true" section rests on git log returning exactly one commit for both paths. That reading is an artifact.

ad0f5f11fis the shallow clone's graft boundary — the oldest commit present. Every file alive at the boundary reports as added there. Control: of 40 randomly sampled tracked files, 35 return exactly that same one commit, and the boundary commit also reports adding legacyRetiredWidget.ts, which this suite's own header attributes to objectui#4612 — a lower number than the boundary commit's own PR. The instrument can distinguish (package.json returns 18 commits, packages/types/src/complex.ts returns 3); it simply has nothing to say about paths untouched since the boundary.

Recovered the real history from the public commit log without deepening the shared .git (zero-quota, no shared-state mutation), and it inverts the finding:

commitwhenwhat
8640cec192026-08-14T01:05:19Zadded this suite, with the annotated literal
e028dfcd82026-08-14T02:11:37Zmigrated the filtered-* catalog entries off the retired shape (objectui#4600, PR objectui#4615)

66 minutes apart. The fixture as it stood at 8640cec19 has widgets[0] equal to the test literal byte-for-byte — same keys, same order, same values (checked mechanically, with a self-comparison control returning true on the same comparator). So the row was a genuine independent corpus specimen; it was not "a fixture invented to match the detector". This is drift, and the migration that caused it is named in the card's own Refs.

The second row's comment made the looser claim "widgets[2] of the same catalog entry" and was never byte-for-byte; its retired binding was verbatim, with id genericised to w1 and title / filterBindings dropped. Substantially true then, false now.

Both are false against today's fixture — that part of the card stands, mechanically confirmed key-for-key.

What changed

Both annotations now state the measured history and say explicitly that the rows no longer carry the independent-corpus property — they are historical specimens of stored metadata, which is still the right thing to pin, but is weaker evidence and now says so rather than leaving the next reader to re-derive the confidence.

Route 3's half: the relationship stops being prose. A derived block reads the same fixture at test time and

  • pins that no shipped catalog widget authors the retired binding — the executable form of the sentence that used to be a false comment, and a catalog-regression guard in the other direction;
  • runs the three real shipped widgets through the surface as corpus-derived negative controls. That restores the independent-corpus property on the side where the corpus still has specimens.

The property cannot be restored for the legacy rows themselves, and the block says why: the zero is by design. The retirement's whole content is that no authoring surface emits the shape — WidgetConfigPanel scrubs it on save via LEGACY_ANALYTICS_KEYS — and the catalog is an authoring corpus.

A3.3 sweep — no third false claim

Swept every remaining provenance claim in the file and checked each mechanically. All hold:

  • object / categoryField / aggregate are not on DashboardWidgetSchema — enumerated the spec Zod schema's 22 top-level keys plus objectui's own additions; controls dataset / dimensions / values / options all present.
  • DashboardGridLayout is separately exported and registered as dashboard-grid.
  • One detector module, two consumers; one shared placeholder constant, one wording.
  • Pivot's legacy spelling really is rowField / columnField — both in LEGACY_ANALYTICS_KEYS.
  • DashboardRenderer really does return the placeholder family-wide for pivot, and this surface really does have a pivot renderer.
  • The provider: 'object' config really is read off widgetData, never the widget top level.

Tests, at 81b5da034

Whole package, from the repo root: 85 files, 800 tests, all passing. This suite alone went 9 passing to 14.

Ablation, direction predicted before running, on the committed tree:

legmutationpredictedobserved
Aadd top-level object to widgets[0], keep options.datashape pin red, render control green1 failed / 13 passed — only the shape pin
Brevert widgets[0] to its 8640cec19 retired formboth red2 failed / 12 passed — shape pin and the derived widgets[0] control

Mutation proved on disk by blob hash change plus injected-marker count, never an editor's exit code; restore proved by state — blob hash back to the HEAD blob and git diff HEAD, git diff --cached, git status --short all empty; absolute paths and an EXIT INT TERM trap throughout. The restored tree re-measured at 14 passing. One caveat reported rather than quoted as evidence: leg B's "removed marker" check used xField, which is not unique to widgets[0], so it carries no information; the hash and the injected-marker count are what prove that leg.

So this is not the objectui#7102 situation where no available pin could fail — both new pins are independently falsifiable, shown two different ways.

Gates: check:control-bytes, check:phantom-deps (green on the new node: builtin imports), check:esm-specifiers, check:vi-mock-specifiers, check:vi-mock-inherit, type-check:coverage all exit 0. pnpm --filter @object-ui/plugin-dashboard type-check exits 0, and --listFiles confirms the edited file is genuinely in the test program rather than silently excluded. eslint . for the package: 0 errors, and 0 findings on the edited file (control: sibling files do appear).

Changeset verdict quoted rather than predicted — empty frontmatter, per the gate's own named exemption:

1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/7151-legacy-retired-provenance.md.
Every one of them has an EMPTY frontmatter — declared as releasing nothing, which is the explicit exemption and a complete answer to this gate.

Scope

One test file plus one changeset. No published type, export, or parse behaviour is touched, so the contract-review tier is not engaged. Nothing under content/docs/releases/, and the schema-catalog fixture is unmodified in the diff — it was read, and temporarily mutated only inside the ablation, which restored it provably.

Stayed clear of the objectui#6875 lane: no relational-meta file and no CELL_RELATIONAL_META_KEYS.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…are, and the fixture claim becomes a read
The two annotated `it.each` rows in DashboardGridLayout.legacyRetired.test.tsx
introduced their literals with provenance comments naming
`examples/schema-catalog/src/schemas/plugin-dashboard/filtered-dashboard.json`.
Both comments are false against that fixture as it stands today, mechanically
checked key-for-key.
They were true when written. `8640cec19` added this file at 01:05:19Z with a
literal byte-for-byte identical to the fixture's `widgets[0]` — same keys, same
order, same values — and `e028dfcd8` (objectui#4600, PR #4615) migrated the
`filtered-*` catalog entries off the retired shape at 02:11:37Z, 66 minutes
later. So this is drift, not an invented fixture.
The annotations now state the measured history and say plainly that the rows no
longer carry the independent-corpus property. The relationship they assert stops
being prose: a derived block reads the same fixture at test time, pins that no
shipped catalog widget authors the retired binding, and runs the three real
shipped widgets through the surface as corpus-derived negative controls.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
…easing nothing
Empty frontmatter: the change is one test file in `plugin-dashboard`, with no
published behaviour moving. `check-changeset-presence.mjs` names this as the
explicit exemption rather than a workaround.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3152.6 KB3191.4 KB
Main entry chunk (gzip)142.6 KB350 KB
Entry fileindex-QE-58LTY.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.33KB5.59KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)512.32KB116.52KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)177.67KB49.45KB
fields (index.js)244.25KB61.73KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)65.94KB18.64KB
plugin-chatbot (index.js)190.53KB45.18KB
plugin-dashboard (index.js)132.61KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)248.93KB63.56KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)133.11KB32.61KB
plugin-gantt (index.js)165.21KB40.37KB
plugin-grid (index.js)202.31KB54.66KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.21KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)29.34KB8.47KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-warrenClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Reviewed and armed

All 30 check runs in one call at perPage=100: 27 success, 3 skipped, 0 failed, 0 in_progress. Undrafted, auto-merge SQUASH enabled at 81b5da034.

⚠️ This PR inverted its own card — and my comment on it

premise_still_valid: false, and it is the most valuable result of the session.

objectui#7151 claimed the test's provenance comment "was never true", on git log showing fixture and test added in one commit (ad0f5f11f). The card correctly bounded that to visible history on a shallow clone. I read the bound and reasoned past it anyway, writing on the card: "so this was never drift — the claim was wrong when it was written", and calling it the sharp part. That was wrong, and I have posted the correction on objectui#7151.

You tested the instrument instead of trusting it:

  • 35 of 40 randomly sampled tracked files report that same commit as their "add." ad0f5f11f is the graft boundary, reporting every file alive at the cut as newly added.
  • The boundary commit also reports adding legacyRetiredWidget.ts, which this suite's own header attributes to objectui#4612 — a lower number than the boundary commit's own PR. The contradiction was inside the reading, for anyone who looked.

And the real history reverses the verdict:

committimewhat
8640cec1901:05:19Zadded the suite
e028dfcd802:11:37Zmigrated the filtered-* entries off the retired shape

66 minutes apart, with the fixture at the earlier sha byte-for-byte equal to the test literal (checked mechanically, with a self-comparison control). ⇒ the row was a genuine independent corpus specimen, not invented to match the detector. This is drift — caused by a migration named in the card's own Refs.

Recovering that from the public commit log without deepening the shared .git is the right move in this environment, and it is the only reason the truth was reachable at all.

The other half of the card stands, confirmed key-for-key: both comments are false against today's fixture. That is why the repair was still needed.

Route 3 selected by both gates, not by preference

Gate 1 (route 2 — is there a live corpus specimen?) — a structural walk of every widgets[] element across 628 tracked JSON files: 0 carrying the retired keys and 0 the shipped detector would flag, against live controls of 28 widgets in the population, 15 options-shaped, 2 dataset-shaped. An independent git-grep instrument agreed. The only structural hits anywhere were globalFilters[N].optionsFrom nodes — a filter option source, not a widget. So objectui#4600's summary is confirmed by direct measurement, not inherited.

Gate 2 (route 1 vs 3)plugin-dashboard/tsconfig.test.json declares types: [node], the precondition the packages/types precedent could not meet and relocated for; confirmed afterwards by 82 @types/node files in the program. So route 3 was available, which is what made it correct rather than aspirational.

The honest limit, stated rather than papered over

The independent-corpus property cannot be restored for the legacy rows, and the reason is structural: the zero is by design, because the retirement's whole content is that no authoring surface emits the shape (WidgetConfigPanel scrubs it via LEGACY_ANALYTICS_KEYS) and the catalog is an authoring corpus. So the derived block restores the property on the side where the corpus still has specimens, and the annotations say plainly that the legacy rows no longer carry it — which is exactly what I ruled route 1 would have had to do, arrived at from the other direction.

A3.3: no third false claim. Every remaining provenance claim in the file was checked mechanically and all hold, with the neighbourhood swept too — the sibling DashboardRenderer.legacyRetired.test.tsx carries no provenance claim, and app-shell's "VERBATIM transcription" comments are the well-behaved version of the pattern (they state the claim must be re-earned, and pin it).

Evidence

Two ablation legs distinguishing the two new pins, direction predicted before running: leg A 1 failed | 13 passed (14), leg B 2 failed | 12 passed (14), both failures named. Not green-always pins — the objectui#7102 trap, avoided by demonstration.

⚠️ And you volunteered that one of your own checks was worthless: leg B's "removed marker" used xField, which is not unique to widgets[0], so it returned 1 and carries no information — the blob-hash change and injected-marker count are what prove that leg. Naming a check that did not measure what it claimed, inside an otherwise green report, is the same discipline that caught the graft boundary. It is also the second self-caught broken instrument in this round (objectui#7157's dev found theirs too), and I broke one myself earlier today.


Generated by Claude Code

Merged via the queue into main with commit 51449a0Sep 1, 2026
32 checks passed
@os-warren
os-warren deleted the claude/issue-7151-legacy-retired-provenance branch September 1, 2026 07:33
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@os-warren@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(plugin-dashboard): the legacy-retired rows say what they really are, and the fixture claim becomes a read - #7158

Merged
os-warren merged 2 commits into
mainfrom
claude/issue-7151-legacy-retired-provenance
Sep 1, 2026
Merged

test(plugin-dashboard): the legacy-retired rows say what they really are, and the fixture claim becomes a read#7158
os-warren merged 2 commits into
mainfrom
claude/issue-7151-legacy-retired-provenance

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes#7151

Measured on origin/maindd35800af; shipped at 81b5da034.

The routing tree landed on route 3, and both gates were measurements

Route 2 gate — is there a live corpus specimen of the pre-ADR-0021 shape? No. Two independent instruments, each with a live non-zero control on the same instrument:

instrumentsubjectlive control
structural walk of every widgets[] element in all 628 tracked JSON files0 widgets carry object / categoryField / valueField / aggregate / rowField / columnField at top level; 0 the shipped detector would flagpopulation 28 widgets; 15options-shaped, 2dataset-shaped
git grep -c over tracked *.jsoncategoryField0, rowField0, columnField0, aggregate0xField11, dataset2

The only two structural hits anywhere were globalFilters[N].optionsFrom nodes — a filter's option source, not a widget, and a live surface.

A3.4 checked directly rather than inherited: objectui#4600's "all the dashboard catalog examples were migrated" holds against the fixtures, measured, not taken from another card's summary.

Route 1-vs-3 gate — can this package's test tsconfig read a fixture at test time? Yes. packages/plugin-dashboard/tsconfig.test.json declares "types": ["node"], and its own comment records that as load-bearing and measured. Independently confirmed after the fact: the test program loads 82 @types/node files. This is exactly the precondition packages/types/src/__tests__/timeline-catalog-fixture-migrated.test.ts could not meet in plugin-timeline and relocated for. So: route 3.

The card's premise is half falsified — the claim was TRUE when written

The card's title says the fixture "has never carried" the shape, and its "It was never true" section rests on git log returning exactly one commit for both paths. That reading is an artifact.

ad0f5f11fis the shallow clone's graft boundary — the oldest commit present. Every file alive at the boundary reports as added there. Control: of 40 randomly sampled tracked files, 35 return exactly that same one commit, and the boundary commit also reports adding legacyRetiredWidget.ts, which this suite's own header attributes to objectui#4612 — a lower number than the boundary commit's own PR. The instrument can distinguish (package.json returns 18 commits, packages/types/src/complex.ts returns 3); it simply has nothing to say about paths untouched since the boundary.

Recovered the real history from the public commit log without deepening the shared .git (zero-quota, no shared-state mutation), and it inverts the finding:

commitwhenwhat
8640cec192026-08-14T01:05:19Zadded this suite, with the annotated literal
e028dfcd82026-08-14T02:11:37Zmigrated the filtered-* catalog entries off the retired shape (objectui#4600, PR objectui#4615)

66 minutes apart. The fixture as it stood at 8640cec19 has widgets[0] equal to the test literal byte-for-byte — same keys, same order, same values (checked mechanically, with a self-comparison control returning true on the same comparator). So the row was a genuine independent corpus specimen; it was not "a fixture invented to match the detector". This is drift, and the migration that caused it is named in the card's own Refs.

The second row's comment made the looser claim "widgets[2] of the same catalog entry" and was never byte-for-byte; its retired binding was verbatim, with id genericised to w1 and title / filterBindings dropped. Substantially true then, false now.

Both are false against today's fixture — that part of the card stands, mechanically confirmed key-for-key.

What changed

Both annotations now state the measured history and say explicitly that the rows no longer carry the independent-corpus property — they are historical specimens of stored metadata, which is still the right thing to pin, but is weaker evidence and now says so rather than leaving the next reader to re-derive the confidence.

Route 3's half: the relationship stops being prose. A derived block reads the same fixture at test time and

  • pins that no shipped catalog widget authors the retired binding — the executable form of the sentence that used to be a false comment, and a catalog-regression guard in the other direction;
  • runs the three real shipped widgets through the surface as corpus-derived negative controls. That restores the independent-corpus property on the side where the corpus still has specimens.

The property cannot be restored for the legacy rows themselves, and the block says why: the zero is by design. The retirement's whole content is that no authoring surface emits the shape — WidgetConfigPanel scrubs it on save via LEGACY_ANALYTICS_KEYS — and the catalog is an authoring corpus.

A3.3 sweep — no third false claim

Swept every remaining provenance claim in the file and checked each mechanically. All hold:

  • object / categoryField / aggregate are not on DashboardWidgetSchema — enumerated the spec Zod schema's 22 top-level keys plus objectui's own additions; controls dataset / dimensions / values / options all present.
  • DashboardGridLayout is separately exported and registered as dashboard-grid.
  • One detector module, two consumers; one shared placeholder constant, one wording.
  • Pivot's legacy spelling really is rowField / columnField — both in LEGACY_ANALYTICS_KEYS.
  • DashboardRenderer really does return the placeholder family-wide for pivot, and this surface really does have a pivot renderer.
  • The provider: 'object' config really is read off widgetData, never the widget top level.

Tests, at 81b5da034

Whole package, from the repo root: 85 files, 800 tests, all passing. This suite alone went 9 passing to 14.

Ablation, direction predicted before running, on the committed tree:

legmutationpredictedobserved
Aadd top-level object to widgets[0], keep options.datashape pin red, render control green1 failed / 13 passed — only the shape pin
Brevert widgets[0] to its 8640cec19 retired formboth red2 failed / 12 passed — shape pin and the derived widgets[0] control

Mutation proved on disk by blob hash change plus injected-marker count, never an editor's exit code; restore proved by state — blob hash back to the HEAD blob and git diff HEAD, git diff --cached, git status --short all empty; absolute paths and an EXIT INT TERM trap throughout. The restored tree re-measured at 14 passing. One caveat reported rather than quoted as evidence: leg B's "removed marker" check used xField, which is not unique to widgets[0], so it carries no information; the hash and the injected-marker count are what prove that leg.

So this is not the objectui#7102 situation where no available pin could fail — both new pins are independently falsifiable, shown two different ways.

Gates: check:control-bytes, check:phantom-deps (green on the new node: builtin imports), check:esm-specifiers, check:vi-mock-specifiers, check:vi-mock-inherit, type-check:coverage all exit 0. pnpm --filter @object-ui/plugin-dashboard type-check exits 0, and --listFiles confirms the edited file is genuinely in the test program rather than silently excluded. eslint . for the package: 0 errors, and 0 findings on the edited file (control: sibling files do appear).

Changeset verdict quoted rather than predicted — empty frontmatter, per the gate's own named exemption:

1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/7151-legacy-retired-provenance.md.
Every one of them has an EMPTY frontmatter — declared as releasing nothing, which is the explicit exemption and a complete answer to this gate.

Scope

One test file plus one changeset. No published type, export, or parse behaviour is touched, so the contract-review tier is not engaged. Nothing under content/docs/releases/, and the schema-catalog fixture is unmodified in the diff — it was read, and temporarily mutated only inside the ablation, which restored it provably.

Stayed clear of the objectui#6875 lane: no relational-meta file and no CELL_RELATIONAL_META_KEYS.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…are, and the fixture claim becomes a read
The two annotated `it.each` rows in DashboardGridLayout.legacyRetired.test.tsx
introduced their literals with provenance comments naming
`examples/schema-catalog/src/schemas/plugin-dashboard/filtered-dashboard.json`.
Both comments are false against that fixture as it stands today, mechanically
checked key-for-key.
They were true when written. `8640cec19` added this file at 01:05:19Z with a
literal byte-for-byte identical to the fixture's `widgets[0]` — same keys, same
order, same values — and `e028dfcd8` (objectui#4600, PR #4615) migrated the
`filtered-*` catalog entries off the retired shape at 02:11:37Z, 66 minutes
later. So this is drift, not an invented fixture.
The annotations now state the measured history and say plainly that the rows no
longer carry the independent-corpus property. The relationship they assert stops
being prose: a derived block reads the same fixture at test time, pins that no
shipped catalog widget authors the retired binding, and runs the three real
shipped widgets through the surface as corpus-derived negative controls.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
…easing nothing
Empty frontmatter: the change is one test file in `plugin-dashboard`, with no
published behaviour moving. `check-changeset-presence.mjs` names this as the
explicit exemption rather than a workaround.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3152.6 KB3191.4 KB
Main entry chunk (gzip)142.6 KB350 KB
Entry fileindex-QE-58LTY.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.33KB5.59KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)512.32KB116.52KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)177.67KB49.45KB
fields (index.js)244.25KB61.73KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)65.94KB18.64KB
plugin-chatbot (index.js)190.53KB45.18KB
plugin-dashboard (index.js)132.61KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)248.93KB63.56KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)133.11KB32.61KB
plugin-gantt (index.js)165.21KB40.37KB
plugin-grid (index.js)202.31KB54.66KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.21KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)29.34KB8.47KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-warrenClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Reviewed and armed

All 30 check runs in one call at perPage=100: 27 success, 3 skipped, 0 failed, 0 in_progress. Undrafted, auto-merge SQUASH enabled at 81b5da034.

⚠️ This PR inverted its own card — and my comment on it

premise_still_valid: false, and it is the most valuable result of the session.

objectui#7151 claimed the test's provenance comment "was never true", on git log showing fixture and test added in one commit (ad0f5f11f). The card correctly bounded that to visible history on a shallow clone. I read the bound and reasoned past it anyway, writing on the card: "so this was never drift — the claim was wrong when it was written", and calling it the sharp part. That was wrong, and I have posted the correction on objectui#7151.

You tested the instrument instead of trusting it:

  • 35 of 40 randomly sampled tracked files report that same commit as their "add." ad0f5f11f is the graft boundary, reporting every file alive at the cut as newly added.
  • The boundary commit also reports adding legacyRetiredWidget.ts, which this suite's own header attributes to objectui#4612 — a lower number than the boundary commit's own PR. The contradiction was inside the reading, for anyone who looked.

And the real history reverses the verdict:

committimewhat
8640cec1901:05:19Zadded the suite
e028dfcd802:11:37Zmigrated the filtered-* entries off the retired shape

66 minutes apart, with the fixture at the earlier sha byte-for-byte equal to the test literal (checked mechanically, with a self-comparison control). ⇒ the row was a genuine independent corpus specimen, not invented to match the detector. This is drift — caused by a migration named in the card's own Refs.

Recovering that from the public commit log without deepening the shared .git is the right move in this environment, and it is the only reason the truth was reachable at all.

The other half of the card stands, confirmed key-for-key: both comments are false against today's fixture. That is why the repair was still needed.

Route 3 selected by both gates, not by preference

Gate 1 (route 2 — is there a live corpus specimen?) — a structural walk of every widgets[] element across 628 tracked JSON files: 0 carrying the retired keys and 0 the shipped detector would flag, against live controls of 28 widgets in the population, 15 options-shaped, 2 dataset-shaped. An independent git-grep instrument agreed. The only structural hits anywhere were globalFilters[N].optionsFrom nodes — a filter option source, not a widget. So objectui#4600's summary is confirmed by direct measurement, not inherited.

Gate 2 (route 1 vs 3)plugin-dashboard/tsconfig.test.json declares types: [node], the precondition the packages/types precedent could not meet and relocated for; confirmed afterwards by 82 @types/node files in the program. So route 3 was available, which is what made it correct rather than aspirational.

The honest limit, stated rather than papered over

The independent-corpus property cannot be restored for the legacy rows, and the reason is structural: the zero is by design, because the retirement's whole content is that no authoring surface emits the shape (WidgetConfigPanel scrubs it via LEGACY_ANALYTICS_KEYS) and the catalog is an authoring corpus. So the derived block restores the property on the side where the corpus still has specimens, and the annotations say plainly that the legacy rows no longer carry it — which is exactly what I ruled route 1 would have had to do, arrived at from the other direction.

A3.3: no third false claim. Every remaining provenance claim in the file was checked mechanically and all hold, with the neighbourhood swept too — the sibling DashboardRenderer.legacyRetired.test.tsx carries no provenance claim, and app-shell's "VERBATIM transcription" comments are the well-behaved version of the pattern (they state the claim must be re-earned, and pin it).

Evidence

Two ablation legs distinguishing the two new pins, direction predicted before running: leg A 1 failed | 13 passed (14), leg B 2 failed | 12 passed (14), both failures named. Not green-always pins — the objectui#7102 trap, avoided by demonstration.

⚠️ And you volunteered that one of your own checks was worthless: leg B's "removed marker" used xField, which is not unique to widgets[0], so it returned 1 and carries no information — the blob-hash change and injected-marker count are what prove that leg. Naming a check that did not measure what it claimed, inside an otherwise green report, is the same discipline that caught the graft boundary. It is also the second self-caught broken instrument in this round (objectui#7157's dev found theirs too), and I broke one myself earlier today.


Generated by Claude Code

Merged via the queue into main with commit 51449a0Sep 1, 2026
32 checks passed
@os-warren
os-warren deleted the claude/issue-7151-legacy-retired-provenance branch September 1, 2026 07:33
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@os-warren@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

test(plugin-dashboard): the legacy-retired rows say what they really are, and the fixture claim becomes a read - #7158

Merged
os-warren merged 2 commits into
mainfrom
claude/issue-7151-legacy-retired-provenance
Sep 1, 2026
Merged

test(plugin-dashboard): the legacy-retired rows say what they really are, and the fixture claim becomes a read#7158
os-warren merged 2 commits into
mainfrom
claude/issue-7151-legacy-retired-provenance

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes#7151

Measured on origin/maindd35800af; shipped at 81b5da034.

The routing tree landed on route 3, and both gates were measurements

Route 2 gate — is there a live corpus specimen of the pre-ADR-0021 shape? No. Two independent instruments, each with a live non-zero control on the same instrument:

instrumentsubjectlive control
structural walk of every widgets[] element in all 628 tracked JSON files0 widgets carry object / categoryField / valueField / aggregate / rowField / columnField at top level; 0 the shipped detector would flagpopulation 28 widgets; 15options-shaped, 2dataset-shaped
git grep -c over tracked *.jsoncategoryField0, rowField0, columnField0, aggregate0xField11, dataset2

The only two structural hits anywhere were globalFilters[N].optionsFrom nodes — a filter's option source, not a widget, and a live surface.

A3.4 checked directly rather than inherited: objectui#4600's "all the dashboard catalog examples were migrated" holds against the fixtures, measured, not taken from another card's summary.

Route 1-vs-3 gate — can this package's test tsconfig read a fixture at test time? Yes. packages/plugin-dashboard/tsconfig.test.json declares "types": ["node"], and its own comment records that as load-bearing and measured. Independently confirmed after the fact: the test program loads 82 @types/node files. This is exactly the precondition packages/types/src/__tests__/timeline-catalog-fixture-migrated.test.ts could not meet in plugin-timeline and relocated for. So: route 3.

The card's premise is half falsified — the claim was TRUE when written

The card's title says the fixture "has never carried" the shape, and its "It was never true" section rests on git log returning exactly one commit for both paths. That reading is an artifact.

ad0f5f11fis the shallow clone's graft boundary — the oldest commit present. Every file alive at the boundary reports as added there. Control: of 40 randomly sampled tracked files, 35 return exactly that same one commit, and the boundary commit also reports adding legacyRetiredWidget.ts, which this suite's own header attributes to objectui#4612 — a lower number than the boundary commit's own PR. The instrument can distinguish (package.json returns 18 commits, packages/types/src/complex.ts returns 3); it simply has nothing to say about paths untouched since the boundary.

Recovered the real history from the public commit log without deepening the shared .git (zero-quota, no shared-state mutation), and it inverts the finding:

commitwhenwhat
8640cec192026-08-14T01:05:19Zadded this suite, with the annotated literal
e028dfcd82026-08-14T02:11:37Zmigrated the filtered-* catalog entries off the retired shape (objectui#4600, PR objectui#4615)

66 minutes apart. The fixture as it stood at 8640cec19 has widgets[0] equal to the test literal byte-for-byte — same keys, same order, same values (checked mechanically, with a self-comparison control returning true on the same comparator). So the row was a genuine independent corpus specimen; it was not "a fixture invented to match the detector". This is drift, and the migration that caused it is named in the card's own Refs.

The second row's comment made the looser claim "widgets[2] of the same catalog entry" and was never byte-for-byte; its retired binding was verbatim, with id genericised to w1 and title / filterBindings dropped. Substantially true then, false now.

Both are false against today's fixture — that part of the card stands, mechanically confirmed key-for-key.

What changed

Both annotations now state the measured history and say explicitly that the rows no longer carry the independent-corpus property — they are historical specimens of stored metadata, which is still the right thing to pin, but is weaker evidence and now says so rather than leaving the next reader to re-derive the confidence.

Route 3's half: the relationship stops being prose. A derived block reads the same fixture at test time and

  • pins that no shipped catalog widget authors the retired binding — the executable form of the sentence that used to be a false comment, and a catalog-regression guard in the other direction;
  • runs the three real shipped widgets through the surface as corpus-derived negative controls. That restores the independent-corpus property on the side where the corpus still has specimens.

The property cannot be restored for the legacy rows themselves, and the block says why: the zero is by design. The retirement's whole content is that no authoring surface emits the shape — WidgetConfigPanel scrubs it on save via LEGACY_ANALYTICS_KEYS — and the catalog is an authoring corpus.

A3.3 sweep — no third false claim

Swept every remaining provenance claim in the file and checked each mechanically. All hold:

  • object / categoryField / aggregate are not on DashboardWidgetSchema — enumerated the spec Zod schema's 22 top-level keys plus objectui's own additions; controls dataset / dimensions / values / options all present.
  • DashboardGridLayout is separately exported and registered as dashboard-grid.
  • One detector module, two consumers; one shared placeholder constant, one wording.
  • Pivot's legacy spelling really is rowField / columnField — both in LEGACY_ANALYTICS_KEYS.
  • DashboardRenderer really does return the placeholder family-wide for pivot, and this surface really does have a pivot renderer.
  • The provider: 'object' config really is read off widgetData, never the widget top level.

Tests, at 81b5da034

Whole package, from the repo root: 85 files, 800 tests, all passing. This suite alone went 9 passing to 14.

Ablation, direction predicted before running, on the committed tree:

legmutationpredictedobserved
Aadd top-level object to widgets[0], keep options.datashape pin red, render control green1 failed / 13 passed — only the shape pin
Brevert widgets[0] to its 8640cec19 retired formboth red2 failed / 12 passed — shape pin and the derived widgets[0] control

Mutation proved on disk by blob hash change plus injected-marker count, never an editor's exit code; restore proved by state — blob hash back to the HEAD blob and git diff HEAD, git diff --cached, git status --short all empty; absolute paths and an EXIT INT TERM trap throughout. The restored tree re-measured at 14 passing. One caveat reported rather than quoted as evidence: leg B's "removed marker" check used xField, which is not unique to widgets[0], so it carries no information; the hash and the injected-marker count are what prove that leg.

So this is not the objectui#7102 situation where no available pin could fail — both new pins are independently falsifiable, shown two different ways.

Gates: check:control-bytes, check:phantom-deps (green on the new node: builtin imports), check:esm-specifiers, check:vi-mock-specifiers, check:vi-mock-inherit, type-check:coverage all exit 0. pnpm --filter @object-ui/plugin-dashboard type-check exits 0, and --listFiles confirms the edited file is genuinely in the test program rather than silently excluded. eslint . for the package: 0 errors, and 0 findings on the edited file (control: sibling files do appear).

Changeset verdict quoted rather than predicted — empty frontmatter, per the gate's own named exemption:

1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/7151-legacy-retired-provenance.md.
Every one of them has an EMPTY frontmatter — declared as releasing nothing, which is the explicit exemption and a complete answer to this gate.

Scope

One test file plus one changeset. No published type, export, or parse behaviour is touched, so the contract-review tier is not engaged. Nothing under content/docs/releases/, and the schema-catalog fixture is unmodified in the diff — it was read, and temporarily mutated only inside the ablation, which restored it provably.

Stayed clear of the objectui#6875 lane: no relational-meta file and no CELL_RELATIONAL_META_KEYS.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…are, and the fixture claim becomes a read
The two annotated `it.each` rows in DashboardGridLayout.legacyRetired.test.tsx
introduced their literals with provenance comments naming
`examples/schema-catalog/src/schemas/plugin-dashboard/filtered-dashboard.json`.
Both comments are false against that fixture as it stands today, mechanically
checked key-for-key.
They were true when written. `8640cec19` added this file at 01:05:19Z with a
literal byte-for-byte identical to the fixture's `widgets[0]` — same keys, same
order, same values — and `e028dfcd8` (objectui#4600, PR #4615) migrated the
`filtered-*` catalog entries off the retired shape at 02:11:37Z, 66 minutes
later. So this is drift, not an invented fixture.
The annotations now state the measured history and say plainly that the rows no
longer carry the independent-corpus property. The relationship they assert stops
being prose: a derived block reads the same fixture at test time, pins that no
shipped catalog widget authors the retired binding, and runs the three real
shipped widgets through the surface as corpus-derived negative controls.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
…easing nothing
Empty frontmatter: the change is one test file in `plugin-dashboard`, with no
published behaviour moving. `check-changeset-presence.mjs` names this as the
explicit exemption rather than a workaround.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3152.6 KB3191.4 KB
Main entry chunk (gzip)142.6 KB350 KB
Entry fileindex-QE-58LTY.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.33KB5.59KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)512.32KB116.52KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)177.67KB49.45KB
fields (index.js)244.25KB61.73KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)65.94KB18.64KB
plugin-chatbot (index.js)190.53KB45.18KB
plugin-dashboard (index.js)132.61KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)248.93KB63.56KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)133.11KB32.61KB
plugin-gantt (index.js)165.21KB40.37KB
plugin-grid (index.js)202.31KB54.66KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.21KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)29.34KB8.47KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-warrenClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Reviewed and armed

All 30 check runs in one call at perPage=100: 27 success, 3 skipped, 0 failed, 0 in_progress. Undrafted, auto-merge SQUASH enabled at 81b5da034.

⚠️ This PR inverted its own card — and my comment on it

premise_still_valid: false, and it is the most valuable result of the session.

objectui#7151 claimed the test's provenance comment "was never true", on git log showing fixture and test added in one commit (ad0f5f11f). The card correctly bounded that to visible history on a shallow clone. I read the bound and reasoned past it anyway, writing on the card: "so this was never drift — the claim was wrong when it was written", and calling it the sharp part. That was wrong, and I have posted the correction on objectui#7151.

You tested the instrument instead of trusting it:

  • 35 of 40 randomly sampled tracked files report that same commit as their "add." ad0f5f11f is the graft boundary, reporting every file alive at the cut as newly added.
  • The boundary commit also reports adding legacyRetiredWidget.ts, which this suite's own header attributes to objectui#4612 — a lower number than the boundary commit's own PR. The contradiction was inside the reading, for anyone who looked.

And the real history reverses the verdict:

committimewhat
8640cec1901:05:19Zadded the suite
e028dfcd802:11:37Zmigrated the filtered-* entries off the retired shape

66 minutes apart, with the fixture at the earlier sha byte-for-byte equal to the test literal (checked mechanically, with a self-comparison control). ⇒ the row was a genuine independent corpus specimen, not invented to match the detector. This is drift — caused by a migration named in the card's own Refs.

Recovering that from the public commit log without deepening the shared .git is the right move in this environment, and it is the only reason the truth was reachable at all.

The other half of the card stands, confirmed key-for-key: both comments are false against today's fixture. That is why the repair was still needed.

Route 3 selected by both gates, not by preference

Gate 1 (route 2 — is there a live corpus specimen?) — a structural walk of every widgets[] element across 628 tracked JSON files: 0 carrying the retired keys and 0 the shipped detector would flag, against live controls of 28 widgets in the population, 15 options-shaped, 2 dataset-shaped. An independent git-grep instrument agreed. The only structural hits anywhere were globalFilters[N].optionsFrom nodes — a filter option source, not a widget. So objectui#4600's summary is confirmed by direct measurement, not inherited.

Gate 2 (route 1 vs 3)plugin-dashboard/tsconfig.test.json declares types: [node], the precondition the packages/types precedent could not meet and relocated for; confirmed afterwards by 82 @types/node files in the program. So route 3 was available, which is what made it correct rather than aspirational.

The honest limit, stated rather than papered over

The independent-corpus property cannot be restored for the legacy rows, and the reason is structural: the zero is by design, because the retirement's whole content is that no authoring surface emits the shape (WidgetConfigPanel scrubs it via LEGACY_ANALYTICS_KEYS) and the catalog is an authoring corpus. So the derived block restores the property on the side where the corpus still has specimens, and the annotations say plainly that the legacy rows no longer carry it — which is exactly what I ruled route 1 would have had to do, arrived at from the other direction.

A3.3: no third false claim. Every remaining provenance claim in the file was checked mechanically and all hold, with the neighbourhood swept too — the sibling DashboardRenderer.legacyRetired.test.tsx carries no provenance claim, and app-shell's "VERBATIM transcription" comments are the well-behaved version of the pattern (they state the claim must be re-earned, and pin it).

Evidence

Two ablation legs distinguishing the two new pins, direction predicted before running: leg A 1 failed | 13 passed (14), leg B 2 failed | 12 passed (14), both failures named. Not green-always pins — the objectui#7102 trap, avoided by demonstration.

⚠️ And you volunteered that one of your own checks was worthless: leg B's "removed marker" used xField, which is not unique to widgets[0], so it returned 1 and carries no information — the blob-hash change and injected-marker count are what prove that leg. Naming a check that did not measure what it claimed, inside an otherwise green report, is the same discipline that caught the graft boundary. It is also the second self-caught broken instrument in this round (objectui#7157's dev found theirs too), and I broke one myself earlier today.


Generated by Claude Code

Merged via the queue into main with commit 51449a0Sep 1, 2026
32 checks passed
@os-warren
os-warren deleted the claude/issue-7151-legacy-retired-provenance branch September 1, 2026 07:33
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@os-warren@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(plugin-dashboard): the legacy-retired rows say what they really are, and the fixture claim becomes a read - #7158

Merged
os-warren merged 2 commits into
mainfrom
claude/issue-7151-legacy-retired-provenance
Sep 1, 2026
Merged

test(plugin-dashboard): the legacy-retired rows say what they really are, and the fixture claim becomes a read#7158
os-warren merged 2 commits into
mainfrom
claude/issue-7151-legacy-retired-provenance

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes#7151

Measured on origin/maindd35800af; shipped at 81b5da034.

The routing tree landed on route 3, and both gates were measurements

Route 2 gate — is there a live corpus specimen of the pre-ADR-0021 shape? No. Two independent instruments, each with a live non-zero control on the same instrument:

instrumentsubjectlive control
structural walk of every widgets[] element in all 628 tracked JSON files0 widgets carry object / categoryField / valueField / aggregate / rowField / columnField at top level; 0 the shipped detector would flagpopulation 28 widgets; 15options-shaped, 2dataset-shaped
git grep -c over tracked *.jsoncategoryField0, rowField0, columnField0, aggregate0xField11, dataset2

The only two structural hits anywhere were globalFilters[N].optionsFrom nodes — a filter's option source, not a widget, and a live surface.

A3.4 checked directly rather than inherited: objectui#4600's "all the dashboard catalog examples were migrated" holds against the fixtures, measured, not taken from another card's summary.

Route 1-vs-3 gate — can this package's test tsconfig read a fixture at test time? Yes. packages/plugin-dashboard/tsconfig.test.json declares "types": ["node"], and its own comment records that as load-bearing and measured. Independently confirmed after the fact: the test program loads 82 @types/node files. This is exactly the precondition packages/types/src/__tests__/timeline-catalog-fixture-migrated.test.ts could not meet in plugin-timeline and relocated for. So: route 3.

The card's premise is half falsified — the claim was TRUE when written

The card's title says the fixture "has never carried" the shape, and its "It was never true" section rests on git log returning exactly one commit for both paths. That reading is an artifact.

ad0f5f11fis the shallow clone's graft boundary — the oldest commit present. Every file alive at the boundary reports as added there. Control: of 40 randomly sampled tracked files, 35 return exactly that same one commit, and the boundary commit also reports adding legacyRetiredWidget.ts, which this suite's own header attributes to objectui#4612 — a lower number than the boundary commit's own PR. The instrument can distinguish (package.json returns 18 commits, packages/types/src/complex.ts returns 3); it simply has nothing to say about paths untouched since the boundary.

Recovered the real history from the public commit log without deepening the shared .git (zero-quota, no shared-state mutation), and it inverts the finding:

commitwhenwhat
8640cec192026-08-14T01:05:19Zadded this suite, with the annotated literal
e028dfcd82026-08-14T02:11:37Zmigrated the filtered-* catalog entries off the retired shape (objectui#4600, PR objectui#4615)

66 minutes apart. The fixture as it stood at 8640cec19 has widgets[0] equal to the test literal byte-for-byte — same keys, same order, same values (checked mechanically, with a self-comparison control returning true on the same comparator). So the row was a genuine independent corpus specimen; it was not "a fixture invented to match the detector". This is drift, and the migration that caused it is named in the card's own Refs.

The second row's comment made the looser claim "widgets[2] of the same catalog entry" and was never byte-for-byte; its retired binding was verbatim, with id genericised to w1 and title / filterBindings dropped. Substantially true then, false now.

Both are false against today's fixture — that part of the card stands, mechanically confirmed key-for-key.

What changed

Both annotations now state the measured history and say explicitly that the rows no longer carry the independent-corpus property — they are historical specimens of stored metadata, which is still the right thing to pin, but is weaker evidence and now says so rather than leaving the next reader to re-derive the confidence.

Route 3's half: the relationship stops being prose. A derived block reads the same fixture at test time and

  • pins that no shipped catalog widget authors the retired binding — the executable form of the sentence that used to be a false comment, and a catalog-regression guard in the other direction;
  • runs the three real shipped widgets through the surface as corpus-derived negative controls. That restores the independent-corpus property on the side where the corpus still has specimens.

The property cannot be restored for the legacy rows themselves, and the block says why: the zero is by design. The retirement's whole content is that no authoring surface emits the shape — WidgetConfigPanel scrubs it on save via LEGACY_ANALYTICS_KEYS — and the catalog is an authoring corpus.

A3.3 sweep — no third false claim

Swept every remaining provenance claim in the file and checked each mechanically. All hold:

  • object / categoryField / aggregate are not on DashboardWidgetSchema — enumerated the spec Zod schema's 22 top-level keys plus objectui's own additions; controls dataset / dimensions / values / options all present.
  • DashboardGridLayout is separately exported and registered as dashboard-grid.
  • One detector module, two consumers; one shared placeholder constant, one wording.
  • Pivot's legacy spelling really is rowField / columnField — both in LEGACY_ANALYTICS_KEYS.
  • DashboardRenderer really does return the placeholder family-wide for pivot, and this surface really does have a pivot renderer.
  • The provider: 'object' config really is read off widgetData, never the widget top level.

Tests, at 81b5da034

Whole package, from the repo root: 85 files, 800 tests, all passing. This suite alone went 9 passing to 14.

Ablation, direction predicted before running, on the committed tree:

legmutationpredictedobserved
Aadd top-level object to widgets[0], keep options.datashape pin red, render control green1 failed / 13 passed — only the shape pin
Brevert widgets[0] to its 8640cec19 retired formboth red2 failed / 12 passed — shape pin and the derived widgets[0] control

Mutation proved on disk by blob hash change plus injected-marker count, never an editor's exit code; restore proved by state — blob hash back to the HEAD blob and git diff HEAD, git diff --cached, git status --short all empty; absolute paths and an EXIT INT TERM trap throughout. The restored tree re-measured at 14 passing. One caveat reported rather than quoted as evidence: leg B's "removed marker" check used xField, which is not unique to widgets[0], so it carries no information; the hash and the injected-marker count are what prove that leg.

So this is not the objectui#7102 situation where no available pin could fail — both new pins are independently falsifiable, shown two different ways.

Gates: check:control-bytes, check:phantom-deps (green on the new node: builtin imports), check:esm-specifiers, check:vi-mock-specifiers, check:vi-mock-inherit, type-check:coverage all exit 0. pnpm --filter @object-ui/plugin-dashboard type-check exits 0, and --listFiles confirms the edited file is genuinely in the test program rather than silently excluded. eslint . for the package: 0 errors, and 0 findings on the edited file (control: sibling files do appear).

Changeset verdict quoted rather than predicted — empty frontmatter, per the gate's own named exemption:

1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/7151-legacy-retired-provenance.md.
Every one of them has an EMPTY frontmatter — declared as releasing nothing, which is the explicit exemption and a complete answer to this gate.

Scope

One test file plus one changeset. No published type, export, or parse behaviour is touched, so the contract-review tier is not engaged. Nothing under content/docs/releases/, and the schema-catalog fixture is unmodified in the diff — it was read, and temporarily mutated only inside the ablation, which restored it provably.

Stayed clear of the objectui#6875 lane: no relational-meta file and no CELL_RELATIONAL_META_KEYS.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…are, and the fixture claim becomes a read
The two annotated `it.each` rows in DashboardGridLayout.legacyRetired.test.tsx
introduced their literals with provenance comments naming
`examples/schema-catalog/src/schemas/plugin-dashboard/filtered-dashboard.json`.
Both comments are false against that fixture as it stands today, mechanically
checked key-for-key.
They were true when written. `8640cec19` added this file at 01:05:19Z with a
literal byte-for-byte identical to the fixture's `widgets[0]` — same keys, same
order, same values — and `e028dfcd8` (objectui#4600, PR #4615) migrated the
`filtered-*` catalog entries off the retired shape at 02:11:37Z, 66 minutes
later. So this is drift, not an invented fixture.
The annotations now state the measured history and say plainly that the rows no
longer carry the independent-corpus property. The relationship they assert stops
being prose: a derived block reads the same fixture at test time, pins that no
shipped catalog widget authors the retired binding, and runs the three real
shipped widgets through the surface as corpus-derived negative controls.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
…easing nothing
Empty frontmatter: the change is one test file in `plugin-dashboard`, with no
published behaviour moving. `check-changeset-presence.mjs` names this as the
explicit exemption rather than a workaround.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3152.6 KB3191.4 KB
Main entry chunk (gzip)142.6 KB350 KB
Entry fileindex-QE-58LTY.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.33KB5.59KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)512.32KB116.52KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)177.67KB49.45KB
fields (index.js)244.25KB61.73KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)65.94KB18.64KB
plugin-chatbot (index.js)190.53KB45.18KB
plugin-dashboard (index.js)132.61KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)248.93KB63.56KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)133.11KB32.61KB
plugin-gantt (index.js)165.21KB40.37KB
plugin-grid (index.js)202.31KB54.66KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.21KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)29.34KB8.47KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-warrenClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Reviewed and armed

All 30 check runs in one call at perPage=100: 27 success, 3 skipped, 0 failed, 0 in_progress. Undrafted, auto-merge SQUASH enabled at 81b5da034.

⚠️ This PR inverted its own card — and my comment on it

premise_still_valid: false, and it is the most valuable result of the session.

objectui#7151 claimed the test's provenance comment "was never true", on git log showing fixture and test added in one commit (ad0f5f11f). The card correctly bounded that to visible history on a shallow clone. I read the bound and reasoned past it anyway, writing on the card: "so this was never drift — the claim was wrong when it was written", and calling it the sharp part. That was wrong, and I have posted the correction on objectui#7151.

You tested the instrument instead of trusting it:

  • 35 of 40 randomly sampled tracked files report that same commit as their "add." ad0f5f11f is the graft boundary, reporting every file alive at the cut as newly added.
  • The boundary commit also reports adding legacyRetiredWidget.ts, which this suite's own header attributes to objectui#4612 — a lower number than the boundary commit's own PR. The contradiction was inside the reading, for anyone who looked.

And the real history reverses the verdict:

committimewhat
8640cec1901:05:19Zadded the suite
e028dfcd802:11:37Zmigrated the filtered-* entries off the retired shape

66 minutes apart, with the fixture at the earlier sha byte-for-byte equal to the test literal (checked mechanically, with a self-comparison control). ⇒ the row was a genuine independent corpus specimen, not invented to match the detector. This is drift — caused by a migration named in the card's own Refs.

Recovering that from the public commit log without deepening the shared .git is the right move in this environment, and it is the only reason the truth was reachable at all.

The other half of the card stands, confirmed key-for-key: both comments are false against today's fixture. That is why the repair was still needed.

Route 3 selected by both gates, not by preference

Gate 1 (route 2 — is there a live corpus specimen?) — a structural walk of every widgets[] element across 628 tracked JSON files: 0 carrying the retired keys and 0 the shipped detector would flag, against live controls of 28 widgets in the population, 15 options-shaped, 2 dataset-shaped. An independent git-grep instrument agreed. The only structural hits anywhere were globalFilters[N].optionsFrom nodes — a filter option source, not a widget. So objectui#4600's summary is confirmed by direct measurement, not inherited.

Gate 2 (route 1 vs 3)plugin-dashboard/tsconfig.test.json declares types: [node], the precondition the packages/types precedent could not meet and relocated for; confirmed afterwards by 82 @types/node files in the program. So route 3 was available, which is what made it correct rather than aspirational.

The honest limit, stated rather than papered over

The independent-corpus property cannot be restored for the legacy rows, and the reason is structural: the zero is by design, because the retirement's whole content is that no authoring surface emits the shape (WidgetConfigPanel scrubs it via LEGACY_ANALYTICS_KEYS) and the catalog is an authoring corpus. So the derived block restores the property on the side where the corpus still has specimens, and the annotations say plainly that the legacy rows no longer carry it — which is exactly what I ruled route 1 would have had to do, arrived at from the other direction.

A3.3: no third false claim. Every remaining provenance claim in the file was checked mechanically and all hold, with the neighbourhood swept too — the sibling DashboardRenderer.legacyRetired.test.tsx carries no provenance claim, and app-shell's "VERBATIM transcription" comments are the well-behaved version of the pattern (they state the claim must be re-earned, and pin it).

Evidence

Two ablation legs distinguishing the two new pins, direction predicted before running: leg A 1 failed | 13 passed (14), leg B 2 failed | 12 passed (14), both failures named. Not green-always pins — the objectui#7102 trap, avoided by demonstration.

⚠️ And you volunteered that one of your own checks was worthless: leg B's "removed marker" used xField, which is not unique to widgets[0], so it returned 1 and carries no information — the blob-hash change and injected-marker count are what prove that leg. Naming a check that did not measure what it claimed, inside an otherwise green report, is the same discipline that caught the graft boundary. It is also the second self-caught broken instrument in this round (objectui#7157's dev found theirs too), and I broke one myself earlier today.


Generated by Claude Code

Merged via the queue into main with commit 51449a0Sep 1, 2026
32 checks passed
@os-warren
os-warren deleted the claude/issue-7151-legacy-retired-provenance branch September 1, 2026 07:33
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@os-warren@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(plugin-dashboard): the legacy-retired rows say what they really are, and the fixture claim becomes a read - #7158

Merged
os-warren merged 2 commits into
mainfrom
claude/issue-7151-legacy-retired-provenance
Sep 1, 2026
Merged

test(plugin-dashboard): the legacy-retired rows say what they really are, and the fixture claim becomes a read#7158
os-warren merged 2 commits into
mainfrom
claude/issue-7151-legacy-retired-provenance

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes#7151

Measured on origin/maindd35800af; shipped at 81b5da034.

The routing tree landed on route 3, and both gates were measurements

Route 2 gate — is there a live corpus specimen of the pre-ADR-0021 shape? No. Two independent instruments, each with a live non-zero control on the same instrument:

instrumentsubjectlive control
structural walk of every widgets[] element in all 628 tracked JSON files0 widgets carry object / categoryField / valueField / aggregate / rowField / columnField at top level; 0 the shipped detector would flagpopulation 28 widgets; 15options-shaped, 2dataset-shaped
git grep -c over tracked *.jsoncategoryField0, rowField0, columnField0, aggregate0xField11, dataset2

The only two structural hits anywhere were globalFilters[N].optionsFrom nodes — a filter's option source, not a widget, and a live surface.

A3.4 checked directly rather than inherited: objectui#4600's "all the dashboard catalog examples were migrated" holds against the fixtures, measured, not taken from another card's summary.

Route 1-vs-3 gate — can this package's test tsconfig read a fixture at test time? Yes. packages/plugin-dashboard/tsconfig.test.json declares "types": ["node"], and its own comment records that as load-bearing and measured. Independently confirmed after the fact: the test program loads 82 @types/node files. This is exactly the precondition packages/types/src/__tests__/timeline-catalog-fixture-migrated.test.ts could not meet in plugin-timeline and relocated for. So: route 3.

The card's premise is half falsified — the claim was TRUE when written

The card's title says the fixture "has never carried" the shape, and its "It was never true" section rests on git log returning exactly one commit for both paths. That reading is an artifact.

ad0f5f11fis the shallow clone's graft boundary — the oldest commit present. Every file alive at the boundary reports as added there. Control: of 40 randomly sampled tracked files, 35 return exactly that same one commit, and the boundary commit also reports adding legacyRetiredWidget.ts, which this suite's own header attributes to objectui#4612 — a lower number than the boundary commit's own PR. The instrument can distinguish (package.json returns 18 commits, packages/types/src/complex.ts returns 3); it simply has nothing to say about paths untouched since the boundary.

Recovered the real history from the public commit log without deepening the shared .git (zero-quota, no shared-state mutation), and it inverts the finding:

commitwhenwhat
8640cec192026-08-14T01:05:19Zadded this suite, with the annotated literal
e028dfcd82026-08-14T02:11:37Zmigrated the filtered-* catalog entries off the retired shape (objectui#4600, PR objectui#4615)

66 minutes apart. The fixture as it stood at 8640cec19 has widgets[0] equal to the test literal byte-for-byte — same keys, same order, same values (checked mechanically, with a self-comparison control returning true on the same comparator). So the row was a genuine independent corpus specimen; it was not "a fixture invented to match the detector". This is drift, and the migration that caused it is named in the card's own Refs.

The second row's comment made the looser claim "widgets[2] of the same catalog entry" and was never byte-for-byte; its retired binding was verbatim, with id genericised to w1 and title / filterBindings dropped. Substantially true then, false now.

Both are false against today's fixture — that part of the card stands, mechanically confirmed key-for-key.

What changed

Both annotations now state the measured history and say explicitly that the rows no longer carry the independent-corpus property — they are historical specimens of stored metadata, which is still the right thing to pin, but is weaker evidence and now says so rather than leaving the next reader to re-derive the confidence.

Route 3's half: the relationship stops being prose. A derived block reads the same fixture at test time and

  • pins that no shipped catalog widget authors the retired binding — the executable form of the sentence that used to be a false comment, and a catalog-regression guard in the other direction;
  • runs the three real shipped widgets through the surface as corpus-derived negative controls. That restores the independent-corpus property on the side where the corpus still has specimens.

The property cannot be restored for the legacy rows themselves, and the block says why: the zero is by design. The retirement's whole content is that no authoring surface emits the shape — WidgetConfigPanel scrubs it on save via LEGACY_ANALYTICS_KEYS — and the catalog is an authoring corpus.

A3.3 sweep — no third false claim

Swept every remaining provenance claim in the file and checked each mechanically. All hold:

  • object / categoryField / aggregate are not on DashboardWidgetSchema — enumerated the spec Zod schema's 22 top-level keys plus objectui's own additions; controls dataset / dimensions / values / options all present.
  • DashboardGridLayout is separately exported and registered as dashboard-grid.
  • One detector module, two consumers; one shared placeholder constant, one wording.
  • Pivot's legacy spelling really is rowField / columnField — both in LEGACY_ANALYTICS_KEYS.
  • DashboardRenderer really does return the placeholder family-wide for pivot, and this surface really does have a pivot renderer.
  • The provider: 'object' config really is read off widgetData, never the widget top level.

Tests, at 81b5da034

Whole package, from the repo root: 85 files, 800 tests, all passing. This suite alone went 9 passing to 14.

Ablation, direction predicted before running, on the committed tree:

legmutationpredictedobserved
Aadd top-level object to widgets[0], keep options.datashape pin red, render control green1 failed / 13 passed — only the shape pin
Brevert widgets[0] to its 8640cec19 retired formboth red2 failed / 12 passed — shape pin and the derived widgets[0] control

Mutation proved on disk by blob hash change plus injected-marker count, never an editor's exit code; restore proved by state — blob hash back to the HEAD blob and git diff HEAD, git diff --cached, git status --short all empty; absolute paths and an EXIT INT TERM trap throughout. The restored tree re-measured at 14 passing. One caveat reported rather than quoted as evidence: leg B's "removed marker" check used xField, which is not unique to widgets[0], so it carries no information; the hash and the injected-marker count are what prove that leg.

So this is not the objectui#7102 situation where no available pin could fail — both new pins are independently falsifiable, shown two different ways.

Gates: check:control-bytes, check:phantom-deps (green on the new node: builtin imports), check:esm-specifiers, check:vi-mock-specifiers, check:vi-mock-inherit, type-check:coverage all exit 0. pnpm --filter @object-ui/plugin-dashboard type-check exits 0, and --listFiles confirms the edited file is genuinely in the test program rather than silently excluded. eslint . for the package: 0 errors, and 0 findings on the edited file (control: sibling files do appear).

Changeset verdict quoted rather than predicted — empty frontmatter, per the gate's own named exemption:

1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/7151-legacy-retired-provenance.md.
Every one of them has an EMPTY frontmatter — declared as releasing nothing, which is the explicit exemption and a complete answer to this gate.

Scope

One test file plus one changeset. No published type, export, or parse behaviour is touched, so the contract-review tier is not engaged. Nothing under content/docs/releases/, and the schema-catalog fixture is unmodified in the diff — it was read, and temporarily mutated only inside the ablation, which restored it provably.

Stayed clear of the objectui#6875 lane: no relational-meta file and no CELL_RELATIONAL_META_KEYS.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…are, and the fixture claim becomes a read
The two annotated `it.each` rows in DashboardGridLayout.legacyRetired.test.tsx
introduced their literals with provenance comments naming
`examples/schema-catalog/src/schemas/plugin-dashboard/filtered-dashboard.json`.
Both comments are false against that fixture as it stands today, mechanically
checked key-for-key.
They were true when written. `8640cec19` added this file at 01:05:19Z with a
literal byte-for-byte identical to the fixture's `widgets[0]` — same keys, same
order, same values — and `e028dfcd8` (objectui#4600, PR #4615) migrated the
`filtered-*` catalog entries off the retired shape at 02:11:37Z, 66 minutes
later. So this is drift, not an invented fixture.
The annotations now state the measured history and say plainly that the rows no
longer carry the independent-corpus property. The relationship they assert stops
being prose: a derived block reads the same fixture at test time, pins that no
shipped catalog widget authors the retired binding, and runs the three real
shipped widgets through the surface as corpus-derived negative controls.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
…easing nothing
Empty frontmatter: the change is one test file in `plugin-dashboard`, with no
published behaviour moving. `check-changeset-presence.mjs` names this as the
explicit exemption rather than a workaround.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3152.6 KB3191.4 KB
Main entry chunk (gzip)142.6 KB350 KB
Entry fileindex-QE-58LTY.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.33KB5.59KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)512.32KB116.52KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)177.67KB49.45KB
fields (index.js)244.25KB61.73KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)65.94KB18.64KB
plugin-chatbot (index.js)190.53KB45.18KB
plugin-dashboard (index.js)132.61KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)248.93KB63.56KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)133.11KB32.61KB
plugin-gantt (index.js)165.21KB40.37KB
plugin-grid (index.js)202.31KB54.66KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.21KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)29.34KB8.47KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-warrenClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Reviewed and armed

All 30 check runs in one call at perPage=100: 27 success, 3 skipped, 0 failed, 0 in_progress. Undrafted, auto-merge SQUASH enabled at 81b5da034.

⚠️ This PR inverted its own card — and my comment on it

premise_still_valid: false, and it is the most valuable result of the session.

objectui#7151 claimed the test's provenance comment "was never true", on git log showing fixture and test added in one commit (ad0f5f11f). The card correctly bounded that to visible history on a shallow clone. I read the bound and reasoned past it anyway, writing on the card: "so this was never drift — the claim was wrong when it was written", and calling it the sharp part. That was wrong, and I have posted the correction on objectui#7151.

You tested the instrument instead of trusting it:

  • 35 of 40 randomly sampled tracked files report that same commit as their "add." ad0f5f11f is the graft boundary, reporting every file alive at the cut as newly added.
  • The boundary commit also reports adding legacyRetiredWidget.ts, which this suite's own header attributes to objectui#4612 — a lower number than the boundary commit's own PR. The contradiction was inside the reading, for anyone who looked.

And the real history reverses the verdict:

committimewhat
8640cec1901:05:19Zadded the suite
e028dfcd802:11:37Zmigrated the filtered-* entries off the retired shape

66 minutes apart, with the fixture at the earlier sha byte-for-byte equal to the test literal (checked mechanically, with a self-comparison control). ⇒ the row was a genuine independent corpus specimen, not invented to match the detector. This is drift — caused by a migration named in the card's own Refs.

Recovering that from the public commit log without deepening the shared .git is the right move in this environment, and it is the only reason the truth was reachable at all.

The other half of the card stands, confirmed key-for-key: both comments are false against today's fixture. That is why the repair was still needed.

Route 3 selected by both gates, not by preference

Gate 1 (route 2 — is there a live corpus specimen?) — a structural walk of every widgets[] element across 628 tracked JSON files: 0 carrying the retired keys and 0 the shipped detector would flag, against live controls of 28 widgets in the population, 15 options-shaped, 2 dataset-shaped. An independent git-grep instrument agreed. The only structural hits anywhere were globalFilters[N].optionsFrom nodes — a filter option source, not a widget. So objectui#4600's summary is confirmed by direct measurement, not inherited.

Gate 2 (route 1 vs 3)plugin-dashboard/tsconfig.test.json declares types: [node], the precondition the packages/types precedent could not meet and relocated for; confirmed afterwards by 82 @types/node files in the program. So route 3 was available, which is what made it correct rather than aspirational.

The honest limit, stated rather than papered over

The independent-corpus property cannot be restored for the legacy rows, and the reason is structural: the zero is by design, because the retirement's whole content is that no authoring surface emits the shape (WidgetConfigPanel scrubs it via LEGACY_ANALYTICS_KEYS) and the catalog is an authoring corpus. So the derived block restores the property on the side where the corpus still has specimens, and the annotations say plainly that the legacy rows no longer carry it — which is exactly what I ruled route 1 would have had to do, arrived at from the other direction.

A3.3: no third false claim. Every remaining provenance claim in the file was checked mechanically and all hold, with the neighbourhood swept too — the sibling DashboardRenderer.legacyRetired.test.tsx carries no provenance claim, and app-shell's "VERBATIM transcription" comments are the well-behaved version of the pattern (they state the claim must be re-earned, and pin it).

Evidence

Two ablation legs distinguishing the two new pins, direction predicted before running: leg A 1 failed | 13 passed (14), leg B 2 failed | 12 passed (14), both failures named. Not green-always pins — the objectui#7102 trap, avoided by demonstration.

⚠️ And you volunteered that one of your own checks was worthless: leg B's "removed marker" used xField, which is not unique to widgets[0], so it returned 1 and carries no information — the blob-hash change and injected-marker count are what prove that leg. Naming a check that did not measure what it claimed, inside an otherwise green report, is the same discipline that caught the graft boundary. It is also the second self-caught broken instrument in this round (objectui#7157's dev found theirs too), and I broke one myself earlier today.


Generated by Claude Code

Merged via the queue into main with commit 51449a0Sep 1, 2026
32 checks passed
@os-warren
os-warren deleted the claude/issue-7151-legacy-retired-provenance branch September 1, 2026 07:33
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@os-warren@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

test(plugin-dashboard): the legacy-retired rows say what they really are, and the fixture claim becomes a read - #7158

Merged
os-warren merged 2 commits into
mainfrom
claude/issue-7151-legacy-retired-provenance
Sep 1, 2026
Merged

test(plugin-dashboard): the legacy-retired rows say what they really are, and the fixture claim becomes a read#7158
os-warren merged 2 commits into
mainfrom
claude/issue-7151-legacy-retired-provenance

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes#7151

Measured on origin/maindd35800af; shipped at 81b5da034.

The routing tree landed on route 3, and both gates were measurements

Route 2 gate — is there a live corpus specimen of the pre-ADR-0021 shape? No. Two independent instruments, each with a live non-zero control on the same instrument:

instrumentsubjectlive control
structural walk of every widgets[] element in all 628 tracked JSON files0 widgets carry object / categoryField / valueField / aggregate / rowField / columnField at top level; 0 the shipped detector would flagpopulation 28 widgets; 15options-shaped, 2dataset-shaped
git grep -c over tracked *.jsoncategoryField0, rowField0, columnField0, aggregate0xField11, dataset2

The only two structural hits anywhere were globalFilters[N].optionsFrom nodes — a filter's option source, not a widget, and a live surface.

A3.4 checked directly rather than inherited: objectui#4600's "all the dashboard catalog examples were migrated" holds against the fixtures, measured, not taken from another card's summary.

Route 1-vs-3 gate — can this package's test tsconfig read a fixture at test time? Yes. packages/plugin-dashboard/tsconfig.test.json declares "types": ["node"], and its own comment records that as load-bearing and measured. Independently confirmed after the fact: the test program loads 82 @types/node files. This is exactly the precondition packages/types/src/__tests__/timeline-catalog-fixture-migrated.test.ts could not meet in plugin-timeline and relocated for. So: route 3.

The card's premise is half falsified — the claim was TRUE when written

The card's title says the fixture "has never carried" the shape, and its "It was never true" section rests on git log returning exactly one commit for both paths. That reading is an artifact.

ad0f5f11fis the shallow clone's graft boundary — the oldest commit present. Every file alive at the boundary reports as added there. Control: of 40 randomly sampled tracked files, 35 return exactly that same one commit, and the boundary commit also reports adding legacyRetiredWidget.ts, which this suite's own header attributes to objectui#4612 — a lower number than the boundary commit's own PR. The instrument can distinguish (package.json returns 18 commits, packages/types/src/complex.ts returns 3); it simply has nothing to say about paths untouched since the boundary.

Recovered the real history from the public commit log without deepening the shared .git (zero-quota, no shared-state mutation), and it inverts the finding:

commitwhenwhat
8640cec192026-08-14T01:05:19Zadded this suite, with the annotated literal
e028dfcd82026-08-14T02:11:37Zmigrated the filtered-* catalog entries off the retired shape (objectui#4600, PR objectui#4615)

66 minutes apart. The fixture as it stood at 8640cec19 has widgets[0] equal to the test literal byte-for-byte — same keys, same order, same values (checked mechanically, with a self-comparison control returning true on the same comparator). So the row was a genuine independent corpus specimen; it was not "a fixture invented to match the detector". This is drift, and the migration that caused it is named in the card's own Refs.

The second row's comment made the looser claim "widgets[2] of the same catalog entry" and was never byte-for-byte; its retired binding was verbatim, with id genericised to w1 and title / filterBindings dropped. Substantially true then, false now.

Both are false against today's fixture — that part of the card stands, mechanically confirmed key-for-key.

What changed

Both annotations now state the measured history and say explicitly that the rows no longer carry the independent-corpus property — they are historical specimens of stored metadata, which is still the right thing to pin, but is weaker evidence and now says so rather than leaving the next reader to re-derive the confidence.

Route 3's half: the relationship stops being prose. A derived block reads the same fixture at test time and

  • pins that no shipped catalog widget authors the retired binding — the executable form of the sentence that used to be a false comment, and a catalog-regression guard in the other direction;
  • runs the three real shipped widgets through the surface as corpus-derived negative controls. That restores the independent-corpus property on the side where the corpus still has specimens.

The property cannot be restored for the legacy rows themselves, and the block says why: the zero is by design. The retirement's whole content is that no authoring surface emits the shape — WidgetConfigPanel scrubs it on save via LEGACY_ANALYTICS_KEYS — and the catalog is an authoring corpus.

A3.3 sweep — no third false claim

Swept every remaining provenance claim in the file and checked each mechanically. All hold:

  • object / categoryField / aggregate are not on DashboardWidgetSchema — enumerated the spec Zod schema's 22 top-level keys plus objectui's own additions; controls dataset / dimensions / values / options all present.
  • DashboardGridLayout is separately exported and registered as dashboard-grid.
  • One detector module, two consumers; one shared placeholder constant, one wording.
  • Pivot's legacy spelling really is rowField / columnField — both in LEGACY_ANALYTICS_KEYS.
  • DashboardRenderer really does return the placeholder family-wide for pivot, and this surface really does have a pivot renderer.
  • The provider: 'object' config really is read off widgetData, never the widget top level.

Tests, at 81b5da034

Whole package, from the repo root: 85 files, 800 tests, all passing. This suite alone went 9 passing to 14.

Ablation, direction predicted before running, on the committed tree:

legmutationpredictedobserved
Aadd top-level object to widgets[0], keep options.datashape pin red, render control green1 failed / 13 passed — only the shape pin
Brevert widgets[0] to its 8640cec19 retired formboth red2 failed / 12 passed — shape pin and the derived widgets[0] control

Mutation proved on disk by blob hash change plus injected-marker count, never an editor's exit code; restore proved by state — blob hash back to the HEAD blob and git diff HEAD, git diff --cached, git status --short all empty; absolute paths and an EXIT INT TERM trap throughout. The restored tree re-measured at 14 passing. One caveat reported rather than quoted as evidence: leg B's "removed marker" check used xField, which is not unique to widgets[0], so it carries no information; the hash and the injected-marker count are what prove that leg.

So this is not the objectui#7102 situation where no available pin could fail — both new pins are independently falsifiable, shown two different ways.

Gates: check:control-bytes, check:phantom-deps (green on the new node: builtin imports), check:esm-specifiers, check:vi-mock-specifiers, check:vi-mock-inherit, type-check:coverage all exit 0. pnpm --filter @object-ui/plugin-dashboard type-check exits 0, and --listFiles confirms the edited file is genuinely in the test program rather than silently excluded. eslint . for the package: 0 errors, and 0 findings on the edited file (control: sibling files do appear).

Changeset verdict quoted rather than predicted — empty frontmatter, per the gate's own named exemption:

1 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s): .changeset/7151-legacy-retired-provenance.md.
Every one of them has an EMPTY frontmatter — declared as releasing nothing, which is the explicit exemption and a complete answer to this gate.

Scope

One test file plus one changeset. No published type, export, or parse behaviour is touched, so the contract-review tier is not engaged. Nothing under content/docs/releases/, and the schema-catalog fixture is unmodified in the diff — it was read, and temporarily mutated only inside the ablation, which restored it provably.

Stayed clear of the objectui#6875 lane: no relational-meta file and no CELL_RELATIONAL_META_KEYS.

🤖 Generated with Claude Code

Generated by Claude Code


Generated by Claude Code

…are, and the fixture claim becomes a read
The two annotated `it.each` rows in DashboardGridLayout.legacyRetired.test.tsx
introduced their literals with provenance comments naming
`examples/schema-catalog/src/schemas/plugin-dashboard/filtered-dashboard.json`.
Both comments are false against that fixture as it stands today, mechanically
checked key-for-key.
They were true when written. `8640cec19` added this file at 01:05:19Z with a
literal byte-for-byte identical to the fixture's `widgets[0]` — same keys, same
order, same values — and `e028dfcd8` (objectui#4600, PR #4615) migrated the
`filtered-*` catalog entries off the retired shape at 02:11:37Z, 66 minutes
later. So this is drift, not an invented fixture.
The annotations now state the measured history and say plainly that the rows no
longer carry the independent-corpus property. The relationship they assert stops
being prose: a derived block reads the same fixture at test time, pins that no
shipped catalog widget authors the retired binding, and runs the three real
shipped widgets through the surface as corpus-derived negative controls.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
…easing nothing
Empty frontmatter: the change is one test file in `plugin-dashboard`, with no
published behaviour moving. `check-changeset-presence.mjs` names this as the
explicit exemption rather than a workaround.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012wwHa4aaFybxXrfmfHioDM
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 48 chunks)3152.6 KB3191.4 KB
Main entry chunk (gzip)142.6 KB350 KB
Entry fileindex-QE-58LTY.js
StatusPASS

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

PackageSizeGzipped
app-shell (consoleActionDispatch.js)0.20KB0.19KB
app-shell (index.js)15.33KB5.59KB
app-shell (runtime-config.js)20.68KB7.36KB
app-shell (types.js)0.01KB0.04KB
app-shell (urlParams.js)10.06KB3.86KB
auth (ActiveOrganizationStorage.js)25.05KB9.16KB
auth (AuthContext.js)0.31KB0.24KB
auth (AuthGuard.js)2.07KB1.00KB
auth (AuthProvider.js)40.18KB10.59KB
auth (AuthShell.js)3.49KB1.40KB
auth (ForgotPasswordForm.js)12.21KB3.45KB
auth (LoginForm.js)18.15KB5.39KB
auth (PreviewBanner.js)0.90KB0.50KB
auth (RegisterForm.js)6.65KB2.22KB
auth (SocialSignInButtons.js)9.61KB3.89KB
auth (UserMenu.js)3.41KB1.23KB
auth (auth-gate-events.js)1.29KB0.66KB
auth (authStyles.js)5.04KB1.72KB
auth (createAuthClient.js)40.21KB10.80KB
auth (createAuthenticatedFetch.js)8.46KB3.43KB
auth (index.js)3.19KB1.44KB
auth (invitation-status.js)1.22KB0.70KB
auth (org-roles.js)6.66KB2.78KB
auth (phone-identifier.js)1.11KB0.66KB
auth (types.js)0.59KB0.35KB
auth (useAuth.js)5.30KB1.02KB
auth (useWorkspaceAdminStatus.js)5.13KB2.35KB
collaboration (CommentThread.js)26.08KB7.56KB
collaboration (LiveCursors.js)3.17KB1.27KB
collaboration (PresenceAvatars.js)6.49KB2.64KB
collaboration (PresenceProvider.js)2.79KB1.13KB
collaboration (index.js)1.68KB0.73KB
collaboration (useCollaborationTranslation.js)6.05KB2.52KB
collaboration (useCommentSearch.js)1.98KB0.88KB
collaboration (useConflictResolution.js)7.75KB1.86KB
collaboration (useMentionNotifications.js)1.81KB0.68KB
collaboration (usePresence.js)6.33KB1.84KB
collaboration (useRealtimeSubscription.js)7.91KB2.01KB
components (index.js)512.32KB116.52KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)177.67KB49.45KB
fields (index.js)244.25KB61.73KB
i18n (LocalizationContext.js)1.76KB0.96KB
i18n (currency.js)1.22KB0.64KB
i18n (fallbackInterpolation.js)6.25KB2.77KB
i18n (i18n.js)4.28KB1.75KB
i18n (index.js)3.44KB1.39KB
i18n (pickLocalized.js)7.62KB3.26KB
i18n (provider.js)26.89KB9.04KB
i18n (useDisplayLocale.js)2.85KB1.45KB
i18n (useObjectLabel.js)33.40KB8.71KB
i18n (useSafeTranslation.js)5.60KB2.33KB
layout (index.js)38.98KB10.98KB
mobile (MobileProvider.js)0.92KB0.49KB
mobile (ResponsiveContainer.js)0.94KB0.38KB
mobile (breakpoints.js)1.51KB0.70KB
mobile (createOfflineDataSource.js)5.61KB1.75KB
mobile (index.js)1.55KB0.62KB
mobile (offlineQueue.js)3.91KB1.35KB
mobile (pwa.js)0.97KB0.49KB
mobile (serviceWorker.js)1.48KB0.62KB
mobile (serviceWorkerSource.js)3.41KB1.48KB
mobile (useBreakpoint.js)1.54KB0.65KB
mobile (useGesture.js)6.96KB1.98KB
mobile (useOfflineSync.js)1.99KB0.72KB
mobile (usePullToRefresh.js)2.53KB0.85KB
mobile (useResponsive.js)0.72KB0.42KB
mobile (useResponsiveConfig.js)1.37KB0.63KB
mobile (useSpecGesture.js)4.32KB1.64KB
mobile (useTouchTarget.js)1.01KB0.54KB
permissions (MePermissionsProvider.js)11.71KB4.29KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)6.24KB2.16KB
permissions (discardProofCache.js)1.04KB0.55KB
permissions (evaluator.js)5.12KB1.74KB
permissions (index.js)0.93KB0.41KB
permissions (store.js)0.91KB0.42KB
permissions (useFieldPermissions.js)1.28KB0.53KB
permissions (usePermissions.js)4.83KB2.27KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.92KB12.93KB
plugin-charts (index.js)65.94KB18.64KB
plugin-chatbot (index.js)190.53KB45.18KB
plugin-dashboard (index.js)132.61KB34.56KB
plugin-designer (index.js)212.87KB43.19KB
plugin-detail (index.js)248.93KB63.56KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)133.11KB32.61KB
plugin-gantt (index.js)165.21KB40.37KB
plugin-grid (index.js)202.31KB54.66KB
plugin-kanban (index.js)53.21KB14.66KB
plugin-list (index.js)113.21KB27.60KB
plugin-map (index.js)20.20KB6.66KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)29.34KB8.47KB
plugin-tree (index.js)8.98KB3.08KB
plugin-view (index.js)85.90KB21.12KB
providers (DataSourceProvider.js)0.75KB0.39KB
providers (MetadataProvider.js)1.37KB0.59KB
providers (ThemeProvider.js)1.90KB0.85KB
providers (UploadProvider.js)11.66KB3.50KB
providers (index.js)0.45KB0.23KB
providers (types.js)0.01KB0.04KB
react-runtime (index.js)5.62KB2.34KB
react (LazyPluginLoader.js)4.47KB1.63KB
react (SchemaRenderer.js)81.07KB26.86KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)3.11KB1.48KB
react (schema-input.js)2.32KB1.24KB
react (spec-input.js)0.20KB0.18KB
sdui-parser (codegen.js)5.41KB2.34KB
sdui-parser (dashboard-widget-options.js)3.08KB1.30KB
sdui-parser (index.js)4.93KB2.24KB
sdui-parser (input-type.js)2.84KB1.40KB
sdui-parser (parse.js)20.57KB5.88KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)10.35KB3.60KB
types (ai.js)0.20KB0.17KB
types (api-types.js)0.20KB0.18KB
types (app.js)2.87KB0.99KB
types (base.js)0.20KB0.18KB
types (blocks.js)0.20KB0.18KB
types (complex.js)2.74KB1.41KB
types (crud.js)0.20KB0.18KB
types (dashboard-filter-alias.js)6.23KB2.74KB
types (data-display.js)3.75KB1.85KB
types (data-protocol.js)0.20KB0.19KB
types (data.js)0.20KB0.18KB
types (designer.js)1.85KB0.85KB
types (disclosure.js)0.20KB0.18KB
types (error-code.js)1.54KB0.88KB
types (feedback.js)0.20KB0.18KB
types (field-types.js)0.20KB0.18KB
types (form.js)0.20KB0.18KB
types (http-inflight.js)8.87KB3.73KB
types (http-retry.js)4.32KB2.02KB
types (icon-key-migration.js)4.26KB1.63KB
types (index.js)4.72KB2.24KB
types (layout.js)0.20KB0.18KB
types (managed-by.js)0.19KB0.18KB
types (mobile.js)2.59KB1.31KB
types (navigation.js)0.20KB0.18KB
types (objectql.js)0.20KB0.18KB
types (overlay.js)0.20KB0.18KB
types (permissions.js)0.20KB0.18KB
types (plugin-scope.js)0.20KB0.18KB
types (record-components.js)0.20KB0.19KB
types (record-semantics.js)1.28KB0.67KB
types (registry.js)0.20KB0.18KB
types (reports.js)0.20KB0.18KB
types (spec-report.js)5.05KB1.93KB
types (spec-ui-namespace.js)0.20KB0.19KB
types (system-fields.js)3.33KB1.54KB
types (theme.js)6.28KB2.87KB
types (ui-action.js)3.40KB1.71KB
types (views.js)0.20KB0.18KB
types (widget.js)0.20KB0.18KB

Size Limits

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

@os-warrenClaude

Copy link
Copy Markdown
CollaboratorAuthor

✅ Reviewed and armed

All 30 check runs in one call at perPage=100: 27 success, 3 skipped, 0 failed, 0 in_progress. Undrafted, auto-merge SQUASH enabled at 81b5da034.

⚠️ This PR inverted its own card — and my comment on it

premise_still_valid: false, and it is the most valuable result of the session.

objectui#7151 claimed the test's provenance comment "was never true", on git log showing fixture and test added in one commit (ad0f5f11f). The card correctly bounded that to visible history on a shallow clone. I read the bound and reasoned past it anyway, writing on the card: "so this was never drift — the claim was wrong when it was written", and calling it the sharp part. That was wrong, and I have posted the correction on objectui#7151.

You tested the instrument instead of trusting it:

  • 35 of 40 randomly sampled tracked files report that same commit as their "add." ad0f5f11f is the graft boundary, reporting every file alive at the cut as newly added.
  • The boundary commit also reports adding legacyRetiredWidget.ts, which this suite's own header attributes to objectui#4612 — a lower number than the boundary commit's own PR. The contradiction was inside the reading, for anyone who looked.

And the real history reverses the verdict:

committimewhat
8640cec1901:05:19Zadded the suite
e028dfcd802:11:37Zmigrated the filtered-* entries off the retired shape

66 minutes apart, with the fixture at the earlier sha byte-for-byte equal to the test literal (checked mechanically, with a self-comparison control). ⇒ the row was a genuine independent corpus specimen, not invented to match the detector. This is drift — caused by a migration named in the card's own Refs.

Recovering that from the public commit log without deepening the shared .git is the right move in this environment, and it is the only reason the truth was reachable at all.

The other half of the card stands, confirmed key-for-key: both comments are false against today's fixture. That is why the repair was still needed.

Route 3 selected by both gates, not by preference

Gate 1 (route 2 — is there a live corpus specimen?) — a structural walk of every widgets[] element across 628 tracked JSON files: 0 carrying the retired keys and 0 the shipped detector would flag, against live controls of 28 widgets in the population, 15 options-shaped, 2 dataset-shaped. An independent git-grep instrument agreed. The only structural hits anywhere were globalFilters[N].optionsFrom nodes — a filter option source, not a widget. So objectui#4600's summary is confirmed by direct measurement, not inherited.

Gate 2 (route 1 vs 3)plugin-dashboard/tsconfig.test.json declares types: [node], the precondition the packages/types precedent could not meet and relocated for; confirmed afterwards by 82 @types/node files in the program. So route 3 was available, which is what made it correct rather than aspirational.

The honest limit, stated rather than papered over

The independent-corpus property cannot be restored for the legacy rows, and the reason is structural: the zero is by design, because the retirement's whole content is that no authoring surface emits the shape (WidgetConfigPanel scrubs it via LEGACY_ANALYTICS_KEYS) and the catalog is an authoring corpus. So the derived block restores the property on the side where the corpus still has specimens, and the annotations say plainly that the legacy rows no longer carry it — which is exactly what I ruled route 1 would have had to do, arrived at from the other direction.

A3.3: no third false claim. Every remaining provenance claim in the file was checked mechanically and all hold, with the neighbourhood swept too — the sibling DashboardRenderer.legacyRetired.test.tsx carries no provenance claim, and app-shell's "VERBATIM transcription" comments are the well-behaved version of the pattern (they state the claim must be re-earned, and pin it).

Evidence

Two ablation legs distinguishing the two new pins, direction predicted before running: leg A 1 failed | 13 passed (14), leg B 2 failed | 12 passed (14), both failures named. Not green-always pins — the objectui#7102 trap, avoided by demonstration.

⚠️ And you volunteered that one of your own checks was worthless: leg B's "removed marker" used xField, which is not unique to widgets[0], so it returned 1 and carries no information — the blob-hash change and injected-marker count are what prove that leg. Naming a check that did not measure what it claimed, inside an otherwise green report, is the same discipline that caught the graft boundary. It is also the second self-caught broken instrument in this round (objectui#7157's dev found theirs too), and I broke one myself earlier today.


Generated by Claude Code

Merged via the queue into main with commit 51449a0Sep 1, 2026
32 checks passed
@os-warren
os-warren deleted the claude/issue-7151-legacy-retired-provenance branch September 1, 2026 07:33
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@os-warren@claude