Skip to content

fix(components,plugin-list): the Sort picker lists a field it keeps only for the current sort as removable only (objectui#11943) - #11981

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-11943-sort-disabled-entry
Oct 8, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-11943-sort-disabled-entry

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #11943
Clause-②: yes — this widens a published prop type of @object-ui/components: SortBuilderProps.fields entries gain an optional disabled (one optional key; existing callers never pass it and behave as before).

The list's Sort picker now lists a field it keeps only because the current sort names it as removable only: marked unavailable, never offered as a new choice. This is the half objectui#11962 left (triage's ruling on the card, direction A1 from the retriage). With objectui#11962's read check already on main, it completes the card.

What changes

  • SortBuilder (packages/components/src/custom/sort-builder.tsx). A fields entry takes an optional disabled. It is passed to SelectItem, so every row's dropdown draws the entry as an unavailable option (aria-disabled="true", data-disabled) that cannot be chosen by click or keyboard. A row whose current field it is still shows its label, and can be changed to another field or removed. Add sort seeds the first entry that is not disabled, and is disabled when no entry is choosable (it was disabled only for an empty list). No i18n key, so A2 is not taken.
  • ListView's sortFields memo (packages/plugin-list/src/ListView.tsx). An entry carries disabled: true exactly when the in-use exception alone keeps it: unreadable (canReadField denies), relational (listed with the by-ID suffix), or refused by the served projection (or, with no projection served, by the type read). A field the two rules list anyway carries no flag, whether or not the sort names it. Every entry it listed before is still listed with the same label: only the flag is new.
  • Comments. The block above sortFields and the effectiveFields comment stop promising a follow-up and state what holds. The canReadField docblock makes no such promise and is unchanged.
  • The objectui#11962 in-use pin (ListView.sortFieldRead-11943.test.tsx, "a field the current sort already names stays listed") keeps its assertions. Its title and comment stop describing the half-state this change ends.
  • Changeset .changeset/11943-sort-in-use-removable-only.md: @object-ui/components minor (an additive optional prop), @object-ui/plugin-list patch.

The dispatch's mechanism assumptions, measured

  • H1 holds. Through the real SortBuilder and its Radix Select (sort-builder-disabled-entry-11943.test.tsx), each beside a control that runs the same interaction on the same entry without the flag: the flagged option renders aria-disabled="true" and data-disabled (control: neither); click and mouse pointer-up choose nothing and the list stays open (control: chosen); Enter on the option and typeahead on the closed trigger skip it (control: chosen); a row whose value is the flagged entry shows its label (control: a value with no entry renders blank), can be changed to another field, and can be removed.
  • H2 holds for all three cases, and each is flagged. ListView.sortRemovableOnly-11943.test.tsx runs the same sequence for an unreadable in-use field, a platform-refused in-use field (served projection, as objectui#6455's pins serve it) and a relational in-use field (Owner (by ID)), each declared first on the object so an unflagged Add sort would seed it: listed disabled and nothing else is; its row shows its label; Add sort seeds Title; the new row offers it only as unavailable and choosing it changes nothing; removing its row drops it from the list. The relational case is flagged too: objectui#4243 took relational fields out of the picker as new choices, and the in-use entry exists only so its row is not blank and the sort survives an edit. Flagging it keeps that row as it was and stops other rows re-offering it. Controls: a readable sortable field in use carries no flag and Add sort still seeds the first entry; with full read and no projection, the same unreadable and refused fields are ordinary options.
  • H3 holds. emitSortChange is untouched, and its docblock is still true. The five objectui#11962 pins ran unedited and green on the committed fix a64e78e: Test Files 1 failed | 3 passed (4), Tests 4 failed | 18 passed (22), where the one failing file was a temporary measurement probe (below) and the pin file passed 5 of 5. They stayed green under every mutation below. Only pin 4's prose was edited afterwards.
  • H4 holds. config-field-renderer.tsx passes field.fields, whose ConfigField type declares no disabled; plugin-view's SortUI.tsx and app-shell's ObjectListViewInspector.tsx map to { value, label } explicitly. Their existing tests ran unedited in the union below.

Reverse check

On the committed fix 2b80540, each mutation landed and was restored through scripts/ablation-replace.mjs (anchor 1 to 0, blob changed; restore blob equal to HEAD and git diff HEAD empty), running the two new files plus the objectui#11962 file (17 tests). Baseline: Tests 17 passed (17).

Mutation Predicted red Observed
M1: SortBuilder stops passing disabled to SelectItem 3 SortBuilder (mark, click, keyboard) + the 3 ListView cases Tests 6 failed / 11 passed (17): exactly those
M2: Add sort seeds fields[0] again SortBuilder seed case + the 3 ListView cases Tests 4 failed / 13 passed (17): exactly those
M3: Add sort disabled only for an empty list SortBuilder Add sort gate case Tests 1 failed / 16 passed (17)
M4: ListView drops disabled: true from the in-use entry the 3 ListView cases Tests 3 failed / 14 passed (17)
M5: an unreadable in-use field that the rules would list goes through the unflagged branch the unreadable case only Tests 1 failed / 16 passed (17): the unreadable case

Cross-package type, read from the rebuilt .d.ts. After the closure build, packages/components/dist/custom/sort-builder.d.ts declares disabled?: boolean. A temporary file in plugin-list (deleted by trap; git status empty after) passing disabled: 'no' answers TS2322: Type 'string' is not assignable to type 'boolean | undefined' (a stale .d.ts answers an excess-property error instead), and disabled: true compiles.

Gates

On HEAD 2b80540, heavy runs through the container's verify lock:

  • pnpm exec vitest run --maxWorkers=2 packages/components/src/ packages/plugin-list/ packages/plugin-view/src/ plus the two ObjectListViewInspector.*.test.tsx files and scripts/__tests__/: Test Files 738 passed | 3 skipped (741), Tests 11143 passed | 26 skipped (11169), VERDICT command-exit 0.
  • pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-list^...' build (13 of 47 projects): exit 0.
  • pnpm --filter @object-ui/components type-check and pnpm --filter @object-ui/plugin-list type-check (each echoes tsc --noEmit && tsc -p tsconfig.test.json): exit 0. --listFilesOnly lists the new test files in each test program.
  • node scripts/check-changeset-presence.mjs: "5 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)". check-changeset-no-major, check:changeset-claims, check:pending-changeset-literals, check:doc-types: exit 0. check:new-line-citations: 0 new citation(s). check:control-bytes: OK.
  • Not measured locally: check:skill-examples and check:readme-exports stop at their precondition (they need the app-shell closure built: exit 2 "PRECONDITION NOT MET", and "579 self-import(s) could not be judged"). The narrowing: no file under skills/, content/docs/ or any README.md names SortBuilder or sort-builder (git grep zero hits; control FilterBuilder / GroupingEditor hits), and no export name moves. CI runs both.
  • Lint, narrowed and measured. eslint --no-inline-config --format json over the 5 touched TS files (the config's **/*.{ts,tsx} population): 5 files, 0 errors. Warnings equal their base blobs linted over stdin (sort-builder.tsx 2, ListView.tsx 199, the objectui#11962 pin 0); the two new tests 0. eslint.config.js enables no type-aware linting (tseslint.configs.recommended, no parser project), and nothing under eslint-rules/ reads the file system, so untouched files cannot move. Repo-wide lint is CI's.
  • Bundle: @object-ui/components sits in the eager closure; this adds a few lines of source. The Bundle Analysis comment is CI's.

Acceptance notes

  • The hide-fields popover, measured (the retriage asked; ⛔ not fixed here). With objectui#11962's MePermissionsProvider set-up and a column list carrying labels, in a temporary probe deleted after the run: the Sort picker withholds Secret Note ([Title, Status, Priority], so the read answer is loaded and denying), while the hide-fields popover lists [Title, Status, Secret Note]. The same allFields list feeds the Group editor's field options (shown by default) and the Row color field select: both offer Secret Note too. With full read the lists are the same. Reported for the seat to file.
  • No doc page or README describes SortBuilder, so no doc changes.
  • The SortBuilderProps edit also drops one trailing space on the fields line.

Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw


Generated by Claude Code

claude added 2 commits October 8, 2026 13:38
…ld as removable, never as a new choice (objectui#11943)

SortBuilder's fields entries take an optional `disabled`: the entry is
passed to SelectItem as an unavailable option, "Add sort" seeds the first
entry that is not disabled and is disabled when none is. ListView's
sortFields flags every entry it keeps only because the current sort names
it: unreadable, platform-refused, or relational.

Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw
Co-authored-by: Claude <noreply@anthropic.com>
… restate the objectui#11962 in-use pin (objectui#11943)

The click case leaves the dropdown open when nothing is chosen, which hides
the rows from the accessibility tree; it now asserts the list is still open
and closes it. The objectui#11962 in-use pin keeps its assertions; its title
and comment stop describing the half-state this change ends. Adds the
changeset.

Claude-Session: https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 337 chunks) 3311.1 KB 3312.0 KB
Main entry chunk (gzip) 71.4 KB 350 KB
Entry file index-DcWAb12F.js —
Status PASS —

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 18.33KB 6.73KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 587.53KB 141.60KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 268.74KB 68.04KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 41.50KB 11.82KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.04KB 3.92KB
plugin-calendar (index.js) 53.43KB 15.54KB
plugin-charts (index.js) 84.71KB 23.25KB
plugin-chatbot (index.js) 199.63KB 47.46KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 231.46KB 48.87KB
plugin-detail (index.js) 248.57KB 65.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.11KB 45.88KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 248.22KB 68.84KB
plugin-kanban (index.js) 52.22KB 16.38KB
plugin-list (index.js) 117.72KB 29.41KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.10KB 11.81KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 91.09KB 22.94KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 1.13KB 0.65KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 5.78KB 2.70KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (node-slots.js) 7.18KB 2.34KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.25KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 2b80540b7675b3563b0ad1c749a3c19348bed71a
Local-runs: none

PR #11981 on card #11943, the removable-only half under triage ruling 6057478267 and retriage direction A1 (6060561797), claimed by 6060957822. Inputs: the card body and its 9 comments, the PR body and file list, the net diff against main from merge base d7e9e9ab6, and the 43 check-runs on the head: 40 success, 3 skipped (the two conditional coverage jobs and dependabot), 0 failure. Test, the 8 test shards, Test (dist pins), Type Check, Lint, Build & E2E, Bundle Analysis, Changeset Bump Policy, Changeset Declaration, Changeset Fixed Group Check, Changeset Claim Re-read, README Export Check, Skill Example Check and Governed Surface Queue Guard are all success. Six files, all inside the claim's surface; no governed path.

① Derived judgments

Public surface (@object-ui/components):

  1. SortBuilderProps.fields entries gain disabled?: boolean, reaching consumers through custom/index.ts. Additive and optional. The three other callers keep their shape: config-field-renderer.tsx passes field.fields, SortUI.tsx and ObjectListViewInspector.tsx map to { value, label }. RIGHT, and it is the widening the PR declares.
  2. No new export, no packages/i18n key, no spec key; SortItem is untouched; A2 is not taken. RIGHT.

Accept set, SortBuilder:

  1. disabled is passed to SelectItem. The ui/select.tsx wrapper spreads its props onto the Radix item, which marks the option aria-disabled and data-disabled and refuses click, Enter and typeahead; the wrapper's class already styles data-[disabled]. RIGHT, pinned beside an unflagged control for each interaction.
  2. "Add sort" seeds the first entry without the flag (firstChoosable, a fields.find on the flag). With no flagged entry that is fields[0], so every existing caller behaves as before. RIGHT.
  3. "Add sort" is disabled when no entry is choosable. With no flagged entry that equals the old fields.length === 0. RIGHT.
  4. A row whose value is a flagged entry still shows its label and can be changed to another field or removed. RIGHT, pinned with a blank-row control (a value with no entry renders nothing).

Accept set, ListView sortFields:

  1. Membership and labels are unchanged from the merge base: every field listed before is listed now with the same label. The one path that moves is an unreadable in-use field that is non-relational and platform-sortable: it used to take the plain branch and now takes the in-use branch, where its label is the same (no by-ID suffix) and it carries the flag. RIGHT.
  2. The flag sits exactly on entries the in-use exception alone keeps: unreadable, relational, or refused by the served projection (or by the type read with no projection). A readable, non-relational, platform-sortable field carries no flag whether or not the sort names it. RIGHT: the ruling's "marked unavailable and never offered as a new choice", and A1's coverage of the platform-refused case.
  3. The relational in-use entry (Owner (by ID)) is flagged too, which the retriage did not name (dev deviation 2). Judged RIGHT: objectui#4243 already removed relational fields as new choices, the in-use entry exists only so its row is not blank, and before this diff that entry was re-offerable in every other row and seedable first by "Add sort", the same shape as the other two cases. Its row still changes direction, changes field or is removed. Pinned.
  4. The relational hint is unchanged: an unreadable field never reaches excluded = true, since both unreadable paths continue before it. RIGHT.
  5. emitSortChange and the ListView's sort picker persists a platform-refused sort — the in-use exception re-emits it, and filterPlatformSortableSort has only one consumer #6455 persist boundary are untouched; the memo's dependency list is unchanged and still complete (perms, schema.objectName included). RIGHT.
  6. Before the permissions answer loads, canReadField answers true, so nothing is flagged for the read before isLoaded, matching the columns. RIGHT, unchanged from objectui#11962.
  7. The comment above sortFields and the effectiveFields comment stop promising a follow-up and state what holds. The objectui#11962 wording ("Until objectui#11943's follow-up ... still choosable") would turn false the moment this lands, and the card's Done-when asks for a true comment. RIGHT.
  8. Pins: sort-builder-disabled-entry-11943.test.tsx (7 cases, each beside an unflagged control on the same entry) and ListView.sortRemovableOnly-11943.test.tsx (unreadable, platform-refused and relational, each declared first so an unflagged "Add sort" would seed it, plus two controls). The objectui#11962 pin keeps its assertions byte for byte; only its title and comment are restated. The reverse check M1 to M5 predicted and observed the same failing sets. RIGHT.

② Semver level

  • .changeset/11943-sort-in-use-removable-only.md: @object-ui/components minor, @object-ui/plugin-list patch. Both are released at 17.7.0 and both sit in the fixed group. RIGHT.
  • The PR body's Clause-② line reads yes: the token is first after the colon, the trailing em-dash reasoning is the accepted shape, and no arm is written, so no direction is declared (a (widening) arm would have said it out loud and is optional). yes takes at least minor, and the components bump is minor. RIGHT.
  • plugin-list publishes no surface change: sortFields is an internal memo, and a bug fix in a released package takes patch. RIGHT.
  • Not breaking: nothing an author writes is removed or renamed, so no migration text and no ADR-0087 marker is owed. No skip-changeset label; the PR's labels are labeler's package: components, plugin, tests. RIGHT.
  • Every sentence of the changeset body matches the diff: the unavailable option in every row's dropdown, no choice by click or keyboard, the kept row's label and its change or removal, the "Add sort" seed and gate, the three flagged reasons, and the before-state. RIGHT.

③ Boundary flags

Dev deviations (report 6062759616), each answered:

  1. H3 said the objectui#11962 pins run unedited; they ran green unedited, then pin 4's title and comment were restated. The diff shows the it title and a comment block only; assertions unchanged. Accepted.
  2. The relational in-use case is flagged as well. Accepted, judgment 9.
  3. The Bundle Analysis verdict was not quoted at report time. The head's Bundle Analysis check-run is success. Answered.
  4. check:skill-examples and check:readme-exports not measured locally. Skill Example Check and README Export Check are success on the head. Answered.
  5. One trailing space dropped on the fields line. A touched line, cosmetic. Accepted.
  6. The PR body was staged in the scratchpad and deleted. Process only. Accepted.
  7. Commit trailer form. Process only, not judged here. Accepted.
  8. Two temporary probes never committed. The diff is the six listed files. Verified.

Fence: the effectiveFields comment edit (ListView.tsx about :3188) is outside the claim's named sortFields region. The exclusion's stated reason, that the grouping editors and ListViewProps belong to objectui#11860, does not reach this block, and both the card's Done-when and the triage ruling require this comment to state what holds. A two-line comment edit, named in the PR body. Accepted, not a breach.

PR shape: draft; first line Fixes #11943. RIGHT: with objectui#11962 landed, this completes every Done-when item (the read gate, the ruled in-use case, a pin per case with a reverse check, the true comment). Landing is the seat's act.

open_questions: none.

out_of_scope_findings, escalated to the seat:

  • The hide-fields popover, the Group editor's field options and the Row color field select are built from allFields with no read check, and the dev reproduced it in a probe. The retriage ruled "if it reproduces, it is a card": the seat files it. Not fixed here, per the claim. Neighbour: objectui#11860 owns the grouping-editor region.
  • SortBuilder's per-row remove control is an icon-only button with no aria-label while sortBuilder.removeSort is declared and never read (read from source). A note for the seat's Acceptance notes or a card; not a FAIL reason.

Implemented-by: claude/issue-11943-sort-disabled-entry
Reviewed-by: session_01DBZ9bntPZ7VKyQNtJeNsgw

VERDICT: PASS


Generated by Claude Code

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

Projects

None yet

2 participants