Repository navigation
test(plugin-detail): gate RelatedList's client-mode sort-button case on the object schema (objectui#10895) - #10923
Conversation
…on the object schema The case "keeps that button in client mode" gated only on the view type (`data-list`), which client mode satisfies on the first commit, then read the `Total` sort button synchronously. Since the sort-button row reads the masked stamp (objectui#10728), every column carries it until `getObjectSchema` resolves, so the button existed only once the schema commit had landed: a scheduling race (the #7007 trap described in the file's own docblock). It now awaits `settleObjectSchema(dataSource)`, the gate its windowed sibling already uses. Test-only: no retry, no raised timeout, no source change. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
…(empty frontmatter) `check-changeset-presence` counts every file under a released package's `src/`, test files included, so the test-only gate needs an explicit no-release declaration. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: This is an in-seat review. The card is size S, test-only, ① Derived judgments
② Semver levelNothing is released (empty frontmatter). ③ Boundary flags
Implemented-by: VERDICT: PASS |
…0895-relatedlist-client-sort-gate Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN Co-authored-by: Claude <noreply@anthropic.com>
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Fixes #10895
Clause-②: no
Test-only. Branch
claude/issue-10895-relatedlist-client-sort-gate, base objectuiorigin/main733fd5ac6, head3abab7ce5. Implemented for the PM claim5865039628in sessionhttps://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN.What changed
packages/plugin-detail/src/__tests__/RelatedList.unmaterializedSort.test.tsx, case "keeps that button in client mode":renderList(…)now returns intoconst dataSource, and after the existingdata-listwaitForthe case awaitssettleObjectSchema(dataSource), with the one-line reason: since objectui#10728 the sort-button row reads the masked stamp, which covers every column until the object schema lands (finding(plugin-detail): RelatedList.unmaterializedSort.test.tsx:153 rides a SECOND, ungated settle — its gate covers a different promise chain, and it is green only by effect-declaration order #7007). The existingdata-listgate is kept, as the file's docblock asks (the two gates prove different facts)..changeset/10895-plugin-detail-relatedlist-client-sort-gate.md: empty frontmatter, releasing nothing.check-changeset-presenceasked for it: it counts a test file under a released package'ssrc/(verdict quoted under Gates).waitForaround the assertion.Mechanism, confirmed on this base. The sort-button row skips every field that is not in
unmaskedColumnKeys. WhileobjectTypesPendingholds (anapi, agetObjectSchema, and no fields in hand),tableColumnsstamps every columnmasked, so the key list is empty. In client mode (datapassed) the rows and thedata-listview exist on the first commit, beforegetObjectSchemaresolves. So the case's only gate passed while theTotalbutton could still be absent. On the auto-fetch path this window does not exist: the row fetch waits on the settled schema (objectui#10690), and the view mounts only after the rows land.Census
Every plugin-detail test that reaches
RelatedListwas read: 51 files name it, plusDetailView.test.tsx(retiredrelated:entry, empty rows).unmaskedColumnKeysandobjectTypesPendinglive inRelatedList.tsxonly, within this package. Only a render that passesdatatogether with anapiand a data source carryinggetObjectSchemahas a window where the stamp still covers every column after the rows are in hand.RelatedList.unmaterializedSortdata+api+getObjectSchema)unmaskedColumnKeys; the only gate was the view typeRelatedList.unmaterializedSortsettleObjectSchema, and the row fetch waits on the settled schemaRelatedList.unmaterializedSortRelatedList.unmaterializedSortsortableflag; client mode never withholds it and the masked stamp does not touch itRelatedList.headerSortlistrelated list, which has no headers"apiapinothing is read, soobjectTypesPendingis false from the first renderRelatedList.headerSortapisortableRelatedList.headerSorttablelist never renders the button rowRelatedList.sortabilitySignaldata-listsort-button casesdata-listmounts only after the rows, which wait on the settled schemaRelatedList.sortabilitySignalRelatedList.sortabilitySignalsortableRelatedList.maskedFilterSort-10728listsort buttons (five cases), the table-header sort caseNamesort button), or holds the schema on purpose (the twoheldcases)RelatedList.maskedFilterSort-10728$orderbycaseRelatedList.maskedColumn-10657settledWithMaskwaits for a drawn name; theheldandrejectedcases read the withheld window on purpose, and it reads the same whether or not the read has settledRelatedList.maskedFirstPaint-10657RelatedList.relationalSortsys_userlabel read, which only the render holding the schema issues (the target comes from the schema'sreference); the order assertion sits insidewaitForRelatedList.relationalSortRelatedList.sortDroppedOutsideWindowedgetObjectSchema, soobjectTypesPendingis false throughoutRelatedList.serverpaginationRelatedList.systemcolumnsRelatedList.columnIdentityAccessor.schemaonSortChangeis a functioncolumnHeaderLabel,columnIdentityAccessor,iconSeam-5935,parentscope,rowactions,userColumnNames-10112,DetailView.relatedEntryRetired-7997,RecordRelatedListRenderer.redactedDerivation-9053,RecordRelatedListRenderer.unresolvedIdentityFailClosed-8793,summaryChip.dateOnlyZone-10183waitForRecordRelatedListRenderer.*set and the remainingRelatedList.*files)record:related_listnever passesdata; the rows wait on the settled schemaMeasured cross-check of the census. A throwaway delay between
getObjectSchemaresolving anduseSettledSchemasettling (inpackages/react/src/hooks/useSettledSchema.ts, which vitest aliases to source), run over all 51 files at base: at 50 ms, and again at 400 ms,Tests 1 failed | 316 passed | 8 skipped (325), and the one failure is the target case. It was restored both times, blob equal to HEAD andgit diff HEADempty. That delay sits after the promisesettleObjectSchemaawaits, so it can find a race but cannot prove the gate. The green-after proof below uses the mock-level probe A.Proof (probe A:
getObjectSchemadelayed 50 ms in the file's throwaway mock, never committed)Every leg ran through
ablation-replace.mjs(anchor hit 1 -> 0, blob moved, restored to the HEAD blob,git diff HEADempty) under the verify lock.733fd5ac6, no probe:Tests 5 passed (5),VERDICT command-exit 0.Tests 1 failed | 4 passed (5):× keeps that button in client mode,TestingLibraryElementError: Unable to find an accessible element with the role "button" and name /Total/, and the only buttons present werePreviousandNext. The four other cases stayed green under the probe.VERDICT command-exit 1.b92ab92b3(gate committed), probe A: green.Tests 5 passed (5),VERDICT command-exit 0.Tests 5 passed (5),VERDICT command-exit 0.Gates (head
3abab7ce5unless stated)pnpm exec vitest run packages/plugin-detail/atb92ab92b3:Test Files 216 passed | 1 skipped (217),Tests 2120 passed | 8 skipped (2128),VERDICT command-exit 0. The only later commit adds the.changesetfile, which no plugin-detail test reads (check-pending-changeset-literals:No test source names a pending changeset). The changed file was re-run at3abab7ce5:Tests 5 passed (5),VERDICT command-exit 0. This is a declared narrowing: the full suite held the lock for 9m05s, too close to the 10-minute foreground cap to repeat.pnpm --filter @object-ui/plugin-detail run type-check(echoestsc --noEmit && tsc -p tsconfig.test.json): exit 0,VERDICT command-exit 0. It ran afterturbo run build --filter='@object-ui/plugin-detail^...'(Tasks: 11 successful, 11 total).tsc -p tsconfig.test.json --listFilesOnlylists the changed test file.--format json: 1 file, 0 errors, 5 warnings (@typescript-eslint/no-explicit-any), identical to its base blob linted via--stdin(1 file, 0 errors, 5 warnings, same rule). Population:--print-configresolves a config for the file (119 rules). The config has noparserOptions.projectand noprojectService, so there is no type-aware linting and this diff cannot move a verdict in an untouched file.node scripts/check-changeset-presence.mjs: before the changeset, exit 1:1 source file(s) of 1 released package(s) changed, and this change adds no changeset. After it, exit 0:this change declares 1 changeset(s)…Every one of them has an EMPTY frontmatter.check-changeset-no-major,check-changeset-overwrite,check-changeset-fixed,check-changeset-claims,check-pending-changeset-literals: exit 0.check:control-bytes,check:new-line-citations(0 new citation(s)),check:test-path-roots,check:vi-mock-specifiers,check:vi-mock-inherit,check:vi-mock-override-shape: exit 0.check-governed-queue-guard.mjs --teston both paths:NOT GOVERNED.pnpm lintand the rest of the lint farm are CI's.Serial
PR objectui#10914 touches
DetailView.tsxandrenderers/record-details.tsxin this package. Neither is touched here. This branch changes exactly two paths, the test file above and the new.changesetfile.pull/10914/head,2b3d2061a):git merge-tree --write-tree HEADwith that ref gives exit 0, clean tree3f2b90a34. Its file list, taken against its merge-base withmain, has no path in common with this branch.origin/main(733fd5ac6, unchanged since the branch point):git merge-tree --write-treegives exit 0, clean tree6b2edc48c.Acceptance notes
settleObjectSchemagate. Since objectui#10690 the windowed row fetch already waits on the settled schema, so that gate is now a second guard there. Left as it is: nothing is flaky there, and removing it is not this card.objectTypesPendingholds) ride the same window as the stamp. The client-mode files that read cells do it insidewaitForor through an explicit hold, and the detector kept them green. They are outside the census class (no sort or filter control).Generated by Claude Code