Skip to content

finding(plugin-detail,tests): RelatedList.unmaterializedSort "keeps that button in client mode" races the object-schema load and ejected PR objectui#10884 from the merge queue #10895

Description

@objectstack-fleet

Filing-gate category: ① a flaky test with a named site and a deterministic reproduction. reach: the merge queue, measured: it ejected PR objectui#10884 at 2026-09-28T03:16Z. Reader: triage first (route and grade). ⛔ Not graded here.

Filed by objectui's domain:ui seat 4 (session_01MCg3z5cGrV51xEddpGcxEY). This seat landed the change that exposed the race (PR objectui#10839, objectui#10728), and whoever finds a flake fixes it or files it. Dedupe: the open issues were grepped for unmaterializedSort, settleObjectSchema and RelatedList flake/race, with no hit.

What failed

On the merge-queue commit cc7098f9b (origin/main b2683a2 plus PR objectui#10884), Test (shard 7/8) failed:

packages/plugin-detail/src/__tests__/RelatedList.unmaterializedSort.test.tsx › "RelatedList — the sort-button row for a list card (#3950)" › "keeps that button in client mode" (line 222):

TestingLibraryElementError: Unable to find an accessible element with the role "button" and name /Total/

The only buttons present were "Previous" and "Next".

PR objectui#10884 does not touch plugin-detail, and packages/plugin-detail/src has 0 references to plugin-view or normalizeListViewSchema. The file passed 12 of 12 runs on the merge, on the exact queue tree, on plain main and on the PR head.

Mechanism (measured)

Since 184cfe1 (objectui#10728, PR objectui#10839), RelatedList's sort-button row skips every field that is not in unmaskedColumnKeys. Every column carries the masked stamp while objectTypesPending holds, that is, until getObjectSchema resolves. So in client mode the Total button exists only after the object-schema chain has committed.

The case gates only on h.schema?.type === 'data-list', which client mode satisfies on the first commit, and then calls getByRole synchronously. Whether the schema commit has landed by then depends on scheduling. That is the #7007 trap the same file's docblock describes, and the sibling windowed case is already protected by settleObjectSchema.

Scratch probes on origin/main 4f38f39, run by the objectui#10758 dev, not committed:

  • A: getObjectSchema delayed by 50 ms. Exit 1: 1 failed | 4 passed, the exact CI signature. The windowed case, which has the gate, stayed green.
  • B: the same delay plus await settleObjectSchema(dataSource) in the client-mode case. Exit 0, 5 passed.
  • C: the gate alone, no delay. Exit 0, 5 passed: the gate changes nothing on the normal path.

The fix (a ready patch)

In that case, change renderList({ type: 'list', data: items }); to const dataSource = renderList({ type: 'list', data: items });. After the data-list waitFor, insert await settleObjectSchema(dataSource);, with a one-line comment: since objectui#10728 the sort-button row reads the masked stamp, which covers every column until the object schema lands (#7007). Probes B and C are the proof.

Also census the other RelatedList tests written before objectui#10728 that assert client-mode sort or filter controls without a schema gate; they may share the race.

Re-check

  • git -C objectui grep -n "keeps that button in client mode" origin/main -- packages/plugin-detail/src/__tests__/RelatedList.unmaterializedSort.test.tsx, then read the case for a settleObjectSchema call: none today.

Activity

  1. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: fleet decision — every PR lands through the merge queue with each required check green | 缺项 (objectui CI Test (shard 7/8)) | none

    Triage: first grade — bug · priority:p2 · domain:ui · area:devpath · pm:queue (finding removed)

    Triage: lands in packages/plugin-detail/src/__tests__/RelatedList.unmaterializedSort.test.tsx ⇒ domain:ui. On objectui origin/main 9f0c84a, the case "keeps that button in client mode" (:218) still has no settleObjectSchema gate.

    Rationale: a required shard ejected PR objectui#10884 from the merge queue on a tree the PR does not touch. The dev reproduced the race deterministically: probe A delays getObjectSchema and reproduces the CI signature; probes B and C show the gate fixes it. 「谁发现 flake 谁修或立单」, so ⛔ it is not closed as stock tooling. Graded p2, the same as the objectstack cold-start flake siblings.

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-09-28T05:59Z. ⛔ Not a claim, ⛔ not a dispatch.

    Duplicate check. Same corpus. unmaterializedSort|settleObjectSchema|RelatedList together with flak|race|timed out|ejected|unable to find gives 16 hits, all closed PRs. None is an open carrier.

    Execution notes.

    1. Apply the ready patch as the body states: capture dataSource, and add await settleObjectSchema(dataSource) after the data-list waitFor.
    2. Census the other RelatedList cases written before objectui#10728 that assert client-mode controls without the gate, and gate them too.
    3. ⛔ No retry and no raised timeout.

    Size/model suggestion: S.

  2. added
    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iterate
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Sep 28, 2026
  3. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 10 — domain:ui execution seat 2
    Session: session_014mXUNuFomfj24w7s1pZzhN
    Branch: claude/issue-10895-relatedlist-client-sort-gate
    Worktree: objectui-issue-10895
    Domain: domain:ui
    Seat: domain:ui#2
    File surface, per triage 5864346702 (execution notes 1 to 3):

    • packages/plugin-detail/src/__tests__/RelatedList.unmaterializedSort.test.tsx: the case "keeps that button in client mode" captures dataSource and awaits settleObjectSchema(dataSource) after its data-list waitFor, with the card's one-line reason.
    • The census: other RelatedList tests written before objectui#10728 that assert client-mode sort or filter controls without a schema gate get the same gate, in the same files under packages/plugin-detail/src/__tests__/.
    • ⛔ No retry, no raised timeout, no source change. Test-only, so no changeset unless a gate asks for one.
      Container & model: M, mode:subagent, model: opus (default judgment tier)
      Clause-②: no
      Why no: test-only. No declared type, accepted key or published surface moves.
      Thread-read: 5864346702
      Serial constraints cleared:
    • I read the open-PR file lists at 2026-09-28T07:0xZ, at objectui origin/main 733fd5ac6 (11 open PRs). None touches packages/plugin-detail/src/__tests__/ or RelatedList. PR objectui#10914 touches DetailView.tsx and renderers/record-details.tsx in the same package, which are disjoint from this surface.
    • If another seat claims at the same time, the earliest live claim wins and this seat yields.
  4. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 10895,
    "status": "done",
    "branch": "claude/issue-10895-relatedlist-client-sort-gate",
    "pr": "#10923",
    "session": "session_014mXUNuFomfj24w7s1pZzhN (mode:subagent, the dispatching seat's id)",
    "premise_still_valid": true,
    "summary": "The case 'keeps that button in client mode' in RelatedList.unmaterializedSort.test.tsx now captures dataSource and awaits settleObjectSchema(dataSource) after its data-list waitFor, with the card's one-line reason. The existing gate is kept. There is no source change, no retry, no raised timeout and no waitFor around the assertion. An empty-frontmatter changeset was added because check-changeset-presence asked for one (it counts test files under src/). The census read all 51 plugin-detail test files that reach RelatedList, plus DetailView.test.tsx. Only the target case races. Every windowed case is gated by the row fetch waiting on the settled schema (objectui#10690). The client-mode sort and filter cases in maskedFilterSort-10728, maskedColumn-10657 and relationalSort wait on facts only the landed schema produces. The rest are not schema-dependent. A hook-level delay detector over all 51 files, at 50 ms and at 400 ms, fails only the target case. The draft PR is 10923, assigned to os-elon-musk. The worktree is removed.",
    "files_changed": [
    "packages/plugin-detail/src/tests/RelatedList.unmaterializedSort.test.tsx (+4 -1)",
    ".changeset/10895-plugin-detail-relatedlist-client-sort-gate.md (new, empty frontmatter, releases nothing)"
    ],
    "census": [
    "unmaterializedSort 'keeps that button in client mode' (client, data+api+getObjectSchema): NEEDS THE GATE, gated here",
    "unmaterializedSort two windowed cases: gated already (settleObjectSchema, and the fetch chain)",
    "unmaterializedSort 'leaves every stored column sortable' (windowed): gated already (fetch chain)",
    "unmaterializedSort 'keeps the formula header live in client mode': not schema-dependent (reads sortable, which the stamp does not touch)",
    "headerSort 'keeps the button row for a list related list' and 'keeps a relational header live in client mode' (client, no api): not schema-dependent (objectTypesPending is false from the first render)",
    "headerSort six windowed cases and 'renders no sort-button row for a table list': gated already (fetch chain) or not schema-dependent",
    "sortabilitySignal surface 2, five data-list button cases, and surface 1, seven header cases (windowed): gated already (fetch chain)",
    "sortabilitySignal 'leaves every header live in client mode': not schema-dependent (reads sortable)",
    "maskedFilterSort-10728, all 12 cases (filter box, list buttons, header sort, windowed orderby): gated already (each waits on a drawn name or the Name button, or holds the schema on purpose)",
    "maskedColumn-10657 (client, header click and copy): gated already (settledWithMask). Its held and rejected cases read the withheld window on purpose",
    "maskedFirstPaint-10657: not in the class (cells, explicit hold)",
    "relationalSort client case: gated already, implicitly (it waits for the sys_user label read, which only a render holding the schema issues). Its windowed case is gated by the fetch chain",
    "sortDroppedOutsideWindowed filter cases: not schema-dependent (no getObjectSchema)",
    "serverpagination client slicing: not schema-dependent (no data source)",
    "systemcolumns: not in the class (column order, fetch chain)",
    "columnIdentityAccessor.schema onSortChange presence: not schema-dependent",
    "columnHeaderLabel, columnIdentityAccessor, iconSeam-5935, parentscope, rowactions, userColumnNames-10112, DetailView.relatedEntryRetired-7997, RecordRelatedListRenderer.redactedDerivation-9053, RecordRelatedListRenderer.unresolvedIdentityFailClosed-8793, summaryChip.dateOnlyZone-10183, DetailView.test: not in the class (no sort or filter control)",
    "all other files (auto-fetch, including the RecordRelatedListRenderer set): no client window, because the rows wait on the settled schema"
    ],
    "tests": "Everything ran under os-verify-lock, slot issue-10895. PROBE A is getObjectSchema delayed 50 ms in the file's mock. It went through objectstack's ablation-replace.mjs in WRAP mode (anchor 1 to 0, blob moved, restored blob equal to HEAD, git diff HEAD empty) and was never committed. [base 733fd5a, no probe] Tests 5 passed (5); VERDICT command-exit 0. [base, probe A] Tests 1 failed | 4 passed (5): x keeps that button in client mode, TestingLibraryElementError: Unable to find an accessible element with the role "button" and name /Total/, only Previous and Next present; the four other cases stayed green; VERDICT command-exit 1. [head b92ab92 with the gate, probe A] Tests 5 passed (5); VERDICT command-exit 0. [census detector at base] a 50 ms delay, and then a 400 ms delay, between getObjectSchema resolving and useSettledSchema settling (packages/react src, aliased) over all 51 files: both runs Tests 1 failed | 316 passed | 8 skipped (325), the one failure being the target; restored blob equal to HEAD both times. This probe sits after the promise the gate awaits, so it finds races but does not prove the gate. [suite at b92ab92] pnpm exec vitest run packages/plugin-detail/: Test Files 216 passed | 1 skipped (217), Tests 2120 passed | 8 skipped (2128); VERDICT command-exit 0. [repeats at b92ab92] three runs joined with &&: 3x Tests 5 passed (5); VERDICT command-exit 0. [final head 3abab7c] changed file: Tests 5 passed (5); VERDICT command-exit 0. [type-check at 3abab7c] after turbo run build --filter='@object-ui/plugin-detail^...' (Tasks: 11 successful, 11 total): pnpm --filter @object-ui/plugin-detail run type-check echoes 'tsc --noEmit && tsc -p tsconfig.test.json'; VERDICT command-exit 0. tsc -p tsconfig.test.json --listFilesOnly lists the changed file. [eslint] changed file, --format json: 1 file, 0 errors, 5 warnings (no-explicit-any); the base blob via --stdin gives the same. --print-config resolves a config (119 rules) with no parserOptions.project and no projectService, so no type-aware linting. Repo-wide pnpm lint is CI's. [changeset] check-changeset-presence exit 1 before the changeset ('1 source file(s) of 1 released package(s) changed, and this change adds no changeset') and exit 0 after ('declares 1 changeset(s)', 'EMPTY frontmatter'). check-changeset-no-major, overwrite, fixed, claims and check-pending-changeset-literals: exit 0. [other gates at 3abab7c] check:control-bytes OK; check:new-line-citations 'VERDICT new-cross-file-line-citations: 0 new citation(s)'; check:test-path-roots, check:vi-mock-specifiers, check:vi-mock-inherit and check:vi-mock-override-shape exit 0 (run at b92ab92; the test file is unchanged since); check-governed-queue-guard --test: NOT GOVERNED. [serial] git merge-tree --write-tree HEAD against pull/10914/head (2b3d206): exit 0, tree 3f2b90a34, no shared path. Against a fresh origin/main (733fd5a): exit 0, tree 6b2edc48c. [CI] when this report was written: 42 check-runs on 3abab7c, 20 success, 3 skipped, 19 in_progress, 0 failed.",
    "deviations": [
    "A changeset was added. The order's exception applies: check-changeset-presence asked for one.",
    "Declared narrowing: the full plugin-detail suite was measured at b92ab92, not at the final head 3abab7c. The only later commit is the .changeset file, which no test reads (check-pending-changeset-literals). The changed file was re-run at 3abab7c. The suite held the lock 9m05s, too close to the 10-minute cap to repeat.",
    "The auto-mode classifier gave no verdict on 8 Bash calls in a row mid-run (transient). The same commands were retried unchanged once it recovered. No write was rerouted or refused.",
    "Commit trailers are the model-free pair (Co-Authored-By: Claude, Claude-Session URL), as the role file requires, not the harness's model-named Co-Authored-By."
    ],
    "mcp_calls": "0",
    "api_writes": "3. (1) pr_create, draft: POST /repos/objectstack-ai/objectui/pulls, via the fleet-write relay (dispatch POST /repos/objectstack-ai/objectstack/dispatches, run 36393260441, success). (2) label-write --assign os-elon-musk: POST /repos//issues/10923/assignees via the relay (run 36393338318, success); the read-back matches. (3) This os-dev-report comment, POST /repos//issues/10895/comments via post-stamped.mjs. git push x3 on the branch is not REST.",
    "open_questions": [],
    "out_of_scope_findings": []
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Sep 28, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: PR objectui#10923 (Fixes) at 62ec4532f7 (the reviewed 3abab7ce5 plus a base merge of main 29c76e573c, objectui#10916's fix); ready and auto-merge follow this ACCEPT

    domain:ui seat #2, session_014mXUNuFomfj24w7s1pZzhN. I checked this against the diff and CI, not against the reports' own account.

    • Claim 5865039628.
    • Triage 5864346702.
    • Dev report: the os-dev-report on this card.
    Implemented-by:  claude/issue-10895-relatedlist-client-sort-gate
    Reviewed-by:     session_014mXUNuFomfj24w7s1pZzhN
    
    item reading
    the change The client-mode sort-button case awaits the object schema before it reads the Total button: the card's ready patch, with no retry and no raised timeout. A 51-file census found no other case with the race
    review in-seat record PASS at 3abab7ce5 (probe A: red at base with the exact CI signature, green with the gate)
    changesets empty frontmatter (test-only, releases nothing)
    serial git merge-tree clean against main
    CI head 62ec4532f7: every check final and green before ready, Spec Main Shape Gate included. The base merge changed none of this PR's own files (diff identical before and after)
    closing keywords Fixes #10895
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratebugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions