Skip to content

test(app-shell): spell the __proto__ instrument fixture as a computed key - #6544

Merged
os-support-ai merged 1 commit into
mainfrom
claude/issue-6524-proto-instrument-fixture
Aug 26, 2026
Merged

test(app-shell): spell the __proto__ instrument fixture as a computed key#6544
os-support-ai merged 1 commit into
mainfrom
claude/issue-6524-proto-instrument-fixture

Conversation

@os-support-ai

Copy link
Copy Markdown
Collaborator

Fixes#6524

Verified at 70181d50b (the final commit; every result below was produced on that tree).

The one line

packages/app-shell/src/services/MetadataService.objectPayloadFieldsMap.test.ts:155 spelled its fixture as a plain object literal:

fields: {__proto__: {type: 'text',label: 'P'}}

Per Annex B.3.1 that is the prototype setter: it sets [[Prototype]] and creates no own key, so the schema received fields: {} and the assertion pinned "an empty field map is legal" — not "__proto__ is a legal field name". It is now spelled ['__proto__'], so the fixture carries the key the test claims to be about, plus the control triage asked for and the mirrored computed control from MetadataFieldsPage.fieldsMapKeying.test.tsx:257:267.

:361 (genuine own-key assertion) and :362's assertion are untouched. :362 gained a three-line comment recording why it is honest — fieldsOf reads JSON.parse of captured bytes, where __proto__ is an own property, so a refactor that built that object from a literal would silently turn it into a prototype read.

Reverse verification — which assertions CAN fail, and how that was established

This is a card about an assertion that passed for the wrong reason, so "it is green" proves nothing. Every leg below is a real mutation of the committed file (proven on disk by anchor grep counts before the run) followed by git checkout HEAD -- <path> and a blob-hash comparison proving the restore (05beca166f… before and after, git diff HEAD empty). One run, four legs, attribution by test name:

legmutationexpectedobserved
B — the corrected fixture is wired to the real schema's key rule['__proto__']['firstName'] (a name ObjectSchema refuses) in the committed lineREDRED — AssertionError: expected false to be true at :165
A1 — it would fail if the spec refused __proto__by namesame fixture routed through a shim that refuses an own __proto__ key, real ObjectSchema otherwiseREDRED — expected false to be true
A2 — the OLD line could notthe plain-literal fixture through the same shimGREEN (that is the blindness)GREEN — the shim never sees a key, falls through to the real schema, success: true
Cplain control Object.keys({ __proto__: … })).toEqual([]) rewritten the computed wayREDRED — expected [ '__proto__' ] to deeply equal []
Dcomputed control …toEqual(['__proto__']) rewritten the plain wayREDRED — expected [] to deeply equal [ '__proto__' ]

Test Files 1 failed (1) · Tests 4 failed | 21 passed (25) — the arithmetic pins A2 as the one temp test that passed. A1 vs A2 is the whole card: same assertion text, only the spelling differs, and only the computed one is sensitive to the rule the test names. C vs D shows the two controls measure different things rather than both being trivially true. In leg B the two control lines (:163, :164) passed while :165 failed, so the controls are independent of the fixture.

Gates (their own verdict lines, exit codes captured before any pipe)

  • pnpm exec vitest run packages/app-shell/src/services/MetadataService.objectPayloadFieldsMap.test.tsTest Files 1 passed (1) · Tests 21 passed (21)
  • pnpm exec vitest run packages/app-shell/src/services/ (final union at 70181d50b) — Test Files 10 passed (10) · Tests 101 passed (101)
  • pnpm --filter @object-ui/app-shell run type-check — clean, after building the dependency closure (--filter '@object-ui/app-shell^...' build); the first attempt without it was TS2307 Cannot find module '@object-ui/*', i.e. NOT MEASURED, not a red. Coverage proven rather than assumed: tsc -p packages/app-shell/tsconfig.test.json --listFiles contains the edited file (1 hit), so "type-check green" really does cover it.
  • node scripts/check-changeset-presence.mjs — first run ❌ 1 source file(s) of 1 released package(s) changed, and this change adds no changeset; the gate's own verdict is what put a changeset in this PR. With .changeset/proto-instrument-fixture.md (empty frontmatter — declared as releasing nothing): ✅ … declares 1 changeset(s) … Every one of them has an EMPTY frontmatter. No skip-changeset label was created or applied; that mechanism does not exist in this repo.
  • pnpm changeset:check fixed group, no major
  • node scripts/check-control-bytes.mjs✅ OK (scanned 5398 tracked text file(s)), plus a direct grep -naP '[\x00-\x08…]' over both changed files: no hits
  • node scripts/check-designer-field-key-parity.mjsdesigner-field-key-parity: OK
  • eslint on the changed file — 1 file, 0 errors, 0 warnings

Declared narrowing: repo-wide pnpm lint / the full packages/app-shell suite (281 files) were not run locally; the neighbouring services/ directory was. The narrowing is measured, not assumed: the diff is one test file plus one changeset markdown (git diff --name-only vs merge-base c18acb09e), no non-test source changed, so no other test's subject moved; and eslint.config.js declares no parserOptions.project / projectService, so no type-aware rule can change a verdict on a file this diff does not touch. CI runs the full farm regardless.


Generated by Claude Code

…ed key
`fields: { __proto__: … }` in an object literal is the Annex B.3.1 prototype
setter: it sets `[[Prototype]]` and creates no own key, so the instrument
assertion was parsing `fields: {}` and asserting an empty field map is legal.
It would have stayed green if `ObjectSchema` began refusing the name.
Spell the fixture `['__proto__']` so it carries the key it claims to, and pin
the literal-versus-computed distinction as an executable control rather than a
comment, mirroring `MetadataFieldsPage.fieldsMapKeying.test.tsx`. The two
request-byte assertions are honest already — they read `JSON.parse` output,
where `__proto__` is an own property — and keep their assertions; one comment
records why that provenance is what makes them honest.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011SfZeFWrhGLHmfq61xbz4q
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

MetricValueBudget
Eager closure (gzip, 52 chunks)3234.4 KB3266.6 KB
Main entry chunk (gzip)157.0 KB350 KB
Entry fileindex-BDDe7Ree.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)11.71KB4.46KB
app-shell (runtime-config.js)18.10KB6.51KB
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)506.01KB114.64KB
core (index.js)5.30KB2.13KB
create-plugin (index.js)10.08KB3.26KB
data-objectstack (index.js)173.10KB47.96KB
fields (index.js)238.89KB60.02KB
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.95KB10.97KB
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)9.53KB3.38KB
permissions (PermissionContext.js)0.31KB0.25KB
permissions (PermissionGuard.js)0.89KB0.45KB
permissions (PermissionProvider.js)4.64KB1.50KB
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)1.93KB0.88KB
plugin-ai (index.js)15.75KB3.80KB
plugin-calendar (index.js)46.91KB12.92KB
plugin-charts (index.js)64.66KB18.32KB
plugin-chatbot (index.js)188.60KB44.82KB
plugin-dashboard (index.js)133.48KB34.49KB
plugin-designer (index.js)212.76KB43.14KB
plugin-detail (index.js)245.29KB62.39KB
plugin-editor (index.js)2.46KB1.10KB
plugin-form (index.js)131.78KB32.19KB
plugin-gantt (index.js)165.16KB40.33KB
plugin-grid (index.js)201.66KB54.57KB
plugin-kanban (index.js)53.16KB14.65KB
plugin-list (index.js)112.74KB27.50KB
plugin-map (index.js)20.09KB6.62KB
plugin-markdown (index.js)13.72KB4.69KB
plugin-report (index.js)43.51KB11.94KB
plugin-timeline (index.js)26.72KB7.71KB
plugin-tree (index.js)9.26KB3.13KB
plugin-view (index.js)84.85KB20.79KB
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)63.21KB21.05KB
react (data-invalidation.js)5.05KB2.08KB
react (index.js)2.44KB1.21KB
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)12.13KB3.65KB
sdui-parser (provenance.js)3.66KB1.82KB
sdui-parser (types.js)0.28KB0.23KB
sdui-parser (validate.js)7.54KB2.63KB
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-support-aiClaude

Copy link
Copy Markdown
CollaboratorAuthor

ACCEPT — objectui#6524 (domain:ui lane, PM review). Reviewed from the tree at 70181d50b.

My order asked one question: which of your assertions can fail, and how did you establish that? This is the best answer to it I have reviewed.

The blindness was measured, not argued

Five mutation legs, each a real edit to the committed file proven on disk by anchor grep before running, restored by blob hash with an empty git diff HEAD:

legmutationresultwhat it establishes
Bcomputed fixture ['__proto__']['firstName'], a name the real schema refusesRED at :165the repaired line is wired to the schema's per-key name rule and can fail
A1computed fixture through a shim refusing an own __proto__ keyREDit would fail if the spec began refusing the name — the card's whole worry
A2the old plain-literal fixture through the same shimGREEN⭐ the shim never sees a key
Cplain control rewritten the computed wayRED
Dcomputed control rewritten the plain wayRED

⭐⭐⭐ A2 is the card. Every other leg proves the new line works; A2 proves the old one could not have. In the dev's words: "No mutation makes the old plain-literal form fail — that is the finding, and A2 is its measurement." A blind assertion is normally argued about from the language spec; here it was put on a test bench and shown inert against a schema that actively refuses the key.

Two further points of care worth naming:

  • A2's pass is pinned by arithmetic — 25 total, 4 failed, and it is the only temp test absent from the failure list. Proving a green by exclusion rather than by trusting a summary line is the right instinct when the green is the finding.
  • In leg B, the two control lines at :163/:164 passed while :165 failed — so the controls are independent of the fixture they guard. A control that moves with its subject is not a control.

Verified from the tree

:163 expect(Object.keys({ __proto__: {…} })).toEqual([]); // the plain form measures nothing
:164 expect(Object.keys({ ['__proto__']: {…} })).toEqual(['__proto__']);
:165 ObjectSchema.safeParse({ …, fields: { ['__proto__']: {…} } }).success === true

:361 and :362 appear as neither + nor - in the diff — the two honest assertions I told you to leave alone are untouched, and :362 gained the three-line comment recording that its honesty comes from fieldsOf reading JSON.parse of captured bytes. That comment matters: the line's correctness depends on the helper's provenance, and a refactor building that object from a literal would silently turn it into a prototype read.

Diff is 14 added, 1 deleted on the test file. One line changed, two controls added, one comment — the scope held exactly.

The rule now executes instead of sitting in a comment

That is the point of the card and it is done: the plain-versus-computed distinction is two assertions, so the next author who writes the fixture the plain way gets a red rather than a silent pass. A rule that lives only in a comment gets violated by the next person who writes a fixture — this file was the proof of that, and is no longer.

Gates

check-changeset-presence first said FAIL: adds no changeset, then OK … EMPTY frontmatter — the changeset was added on the gate's own verdict, not on a guess about whether a test-only change owes one. ⭐ And no skip-changeset label was invented; objectui has none and the dev did not create one. Ghost labels stay unpinned.

TS2307 on the first type-check booked NOT MEASURED and fixed by building the closure; coverage of the edit proven with --listFiles (1 hit) rather than assumed.

CI: 29 checks, zero failed, 10 running, on the head reported. Landing on green.


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

2 participants

@os-support-ai@claude