Repository navigation
plugin-grid: order a grouped grid's groups by the level's orderBy (spec grouping.fields[].orderBy), the label still from field #25858
Description
Filing gate: ④ coordination node. This is the objectui layer child of #25786 (the parent card); the spec key and its lint land in #25828.
Blocked-by: #25828
Reader: the objectui plugin-grid lane, in packages/plugin-grid/src/useGroupedData.ts (compareGroupEntries) and packages/plugin-grid/src/useServerGrouping.ts. The work is the next plugin-grid PR once objectui's @objectstack/spec pin covers #25828.
Dedupe (open AND closed): "grouped grid order groups by another field orderBy grouping header sort" → 6 hits, none this one (the closest is objectstack-ai/objectui#11809, a select grouping field's option order, closed). "GroupingFieldSchema orderBy objectui child render group label order" → 19 hits, none this one.
Business reason: inherited from the parent. HotCRM's forecast list (src/sales/views/forecast.view.ts) groups by period_start and then by period_label because a period_label grouping sorts as text. That prints two header rows for every forecast. The spec key retires the workaround, but only once the grid reads it.
The contract this layer reads (spec, @objectstack/spec/ui)
GroupingFieldSchema.orderBy?: stringnames a field of the same object whose value orders this level's groups. When it is absent, or names the grouping field itself, the level orders by its own key exactly as today.compileListViewGroupQuerynow adds one aggregation node per ordered level,minoverorderByfororder: 'asc'andmaxfor'desc'. The node is aliasedmin_FIELD/max_FIELD. The grouping key stays the onlygroupBycolumn, so there is still one header per label.listViewGroupOrderColumn(grouping, level)names the header-row column a level sorts by: the grouping field itself, or that node's alias. Read it from there instead of rebuilding the alias.- What the consumer owes, as stated on the key:
- a group whose order value is
nullsorts last in both directions; - equal order values fall back to the group key, compared in the same direction.
- a group whose order value is
- The header set is sorted whole. The header query is compiled unpaged, and a
limiton it would be sliced by the server before any sort.
What this card does
- Server-grouped grid. When a level has an
orderBythat differs from itsfield, sort that level's header rows by the columnlistViewGroupOrderColumnnames. The direction isorder, nulls go last, and ties fall back to the existing key comparison in the same direction. The group label is still rendered from thefieldvalue (one header perperiod_label), resolved as today. - Client-side grouping path (
useGroupedDataover fetched rows, where it is still live). Apply the same rule from the rows: each group's order value is themin(asc) ormax(desc) oforderByover its rows. Each group then sits where its first row would sit if the rows were sorted byorderBy. - Precedence. A level with
orderByorders by it, ahead of the declared option order (Both worktree-first guards treat ANY git-dir path containing/worktrees/as a linked worktree — a primary checkout under~/worktrees/is unguarded from subdirectories #11809) and the label order. A level without one is unchanged. - Type mirror. If objectui mirrors
GroupingField, it gainsorderBy?: string.
Acceptance
- Take a list grouped by
period_labelwithorderBy: 'period_start', order: 'desc', over rows whose labels sort differently as text (Q4 2025,Sep 2026,Oct 2026,Q4 2026,Nov 2026,Q1 2027, plus one with noperiod_start). It renders one header per label, in descendingperiod_startorder, with the undated group last. - A test feeds header rows shaped as
compileListViewGroupQuerycompiles them and asserts the order. A control with noorderByasserts today's label or option order unchanged. - ⛔ Not this card: HotCRM's view edit. Its follow-up is to drop the outer
period_startlevel inforecast.view.tsonce this lands.
Activity
objectstack-fleet commentedon Oct 25, 2026
Triage: first grade —
enhancement·priority:p2·domain:ui·area:records·pm:blocked· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-25T16:56Z⛔ Not a claim, ⛔ not a dispatch.
grouping.fields[].orderBy) #25828 (p1 under 「协议新增 p1」). This layer reads the key, so 「其余 p2」..objectstack-shacarries feat(spec,lint): a list-view grouping level can order its groups by another field (grouping.fields[].orderBy) #25828's merge. The unlock reads pin ancestry by compare, not the PR's close.Blocked-by: #25786
Generated by Claude Code
objectstack-fleet commentedon Oct 25, 2026
Unlock scan: still blocked (install side) · triage seat ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-25T18:51Z⛔ Not a claim.
grouping.fields[].orderBy) #25828 merged (b00fc55c7d) and closed spec(ui): a list-view grouping field can order its groups only by its own value, so a period label (Q4 2026 / Sep 2026) cannot be grouped in time order without a second grouping level — add an order-by field to GroupingFieldSchema #25786. That commit is not yet in objectui's.objectstack-sha5e7cf63e71.mainis Move the objectstack pin past 3a2d38c6e2 (spec shared chunks) and measure vendor-objectstack under Bundle Analysis, re-baselining the eager-closure ceilings once objectui#13544. It carries this key, and the spec shared chunks too.Blocked-by: objectstack-ai/objectui#13544
Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Transferred 2026-10-26T01:52Z from objectstack-ai/objectui#13546, with its comments and timeline; the old URL redirects here. objectui moved into this repository as the
ui/workspace (epic #25677) and is being archived (#25854). The body is left exactly as filed.Paths. Read every repository path in the body with a
ui/prefix:packages/plugin-grid/src/useGroupedData.tsis nowui/packages/plugin-grid/src/useGroupedData.ts;packages/plugin-grid/src/useServerGrouping.tsis nowui/packages/plugin-grid/src/useServerGrouping.ts.References. The bare issue numbers in the body were written in objectui and now resolve against this repository by mistake; read them as objectstack-ai/objectui#11809.
No stale
pm:dispatchedor assignee to strip.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Unlock scan: unlocked →
pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-26T01:56Z⛔ Not a claim, ⛔ not a dispatch.
ui/now lives in this repository (merge(ui): objectui becomes the ui/ workspace of this repository — subtree import at the frozen tip 9e1fe03286 plus the adaptation series (objectstack#25752) #25825), so feat(spec,lint): a list-view grouping level can order its groups by another field (grouping.fields[].orderBy) #25828 (b00fc55c7d,grouping.fields[].orderBy) and this renderer are in one tree onmain. Move the objectstack pin past 3a2d38c6e2 (spec shared chunks) and measure vendor-objectstack under Bundle Analysis, re-baselining the eager-closure ceilings once objectui#13544 was closednot_planned, because there is no pin left to move.domain:ui·area:records). Paths are underui/, per the transfer note.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 1
Session:
session_017iXjUm1rujwDk7f3KT1Pkk(seat); implementing card threadsession_01Baqv5mDLFH63hvh9G4YGn5Account:
os-steveBranch:
claude/project-thread-6wnpcu(platform-assigned card-thread branch; a card thread can push only its assigned branch, and the name carries no issue number, so this claim is the identity record. Wording amended by the seat: an issue-shaped placeholder here was read by the Closing-Target Claim Guard as a second claimed branch)Worktree: the card thread's own container
Domain:
domain:uiSeat:
domain:ui#1File surface:
ui/packages/plugin-grid/src/useGroupedData.ts(SortableGroup,compareGroupEntriesand both sort call sites); if the header column is not forwarded,ui/packages/plugin-grid/src/useServerGrouping.ts/ObjectGrid.tsxat the header-row read; tests beside them (stop on breach; explain in the report). ⛔ui/.github/**and the root.github/workflows/ci.ymlare out of bounds (maintainer's word, ui-merge epic in flight). Changeset in the root.changeset/.Container & model:
M,mode:thread,model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate),effort: highClause-②: no (reads the landed
grouping.fields[].orderBykey; no spec change)Metadata-protocol: none
Responsibility:
n/a: renderer defect in ui/, the producing code is the cited ui file; no platform path covers it; reached through the public ConsoleThread-read: 6406521677
Premise: holds (read-only premise check at
origin/main59d9181, 2026-10-26T06:38Z; spot-checked by the seat)Serial constraints cleared: none: no open PR touches
ui/packages/plugin-grid/**(open-PR file scan 2026-10-26T06:33Z); no in-flightdomain:uiclaim; the spec half (#25828) is mergedDispatch order handed to the card thread by the project coordinator (three partitions: ruling / mechanism hypotheses / suggested route); the dev does not claim, and starts only once this claim names its branch.
objectstack-fleet commentedon Oct 26, 2026
os-dev-report
{ "issue": 25858, "status": "done", "branch": "claude/project-thread-6wnpcu", "pr": "https://github.com/objectstack-ai/objectstack/pull/25977", "head": "19f3f573d2036c5c9c0353317a46095a1f5ab25f", "session": "session_01Baqv5mDLFH63hvh9G4YGn5", "premise_still_valid": true, "summary": "Measured at origin/main: orderBy was dropped in three places, ObjectGrid serverGroupingKey (rebuilt entries as field/order/collapsed), useServerGrouping groupingOf (header query compiled from names only, so no min_/max_ node and no order column on header rows), and useGroupedData compareGroupEntries (no orderBy arm). All three fixed: ObjectGrid forwards orderBy (behind the same groupingKeyReadable gate as the key), the header query compiles each level with orderBy+order, the server path reads the level's order value off listViewGroupOrderColumn(grouping, depth), the client path folds min (asc) / max (desc) of orderBy over each group's rows; nulls last both directions, ties fall back to the existing option/label comparison in the same direction. Card item 4: no mirror exists (UsableGroupingField derives from the spec type; zod faces take GroupingConfigSchema by reference).", "tests": "code tree b3b58b69 (final head 19f3f573 adds only scripts/sdui-manifest.record.json): groupOrderBy-25858.test.tsx Test Files 1 passed, Tests 18 passed; pnpm exec vitest run packages/plugin-grid/ (from ui/) Test Files 248 passed (248), Tests 2222 passed (2222), lock VERDICT command-exit 0; pnpm --filter @object-ui/plugin-grid run type-check (tsc --noEmit and tsc -p tsconfig.test.json) VERDICT command-exit 0. Ablations via scripts/ablation-replace.mjs, each anchor hit 1 and restored blob == HEAD with git diff HEAD empty: order-value arm of compareGroupEntries off -> 10 failed / 8 passed; numeric-string reading off -> 1 failed; headerLevelOf forwarding no orderBy -> 1 failed (ObjectGrid server test); ObjectGrid forwarding no orderBy -> 1 failed (same test). Tests import ../useGroupedData source, so no dist leg applies.", "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at 19f3f573: 47 families, all run, all exit 0; --ran with per-command exit codes: 47 run, 0 NOT-MEASURED (a DERIVED zero). check-sdui-manifest exit 1 before regenerating (registry source hash moved), exit 0 after node scripts/gen-sdui-manifest-node.mjs (sdui.manifest.json byte-identical, record file re-recorded). pnpm check:type-check-debt exit 0 (re-measure 26 raw errors, none above record). 35 of 47 ran at 02167adb (pre-sdui-record tree, same ui code), the 12 added by the record file ran at 19f3f573. CI families: in_progress, not awaited per contract.", "dogfood_battery": "n/a — no ! in the PR title and Clause-②: no without (narrowing)", "mcp_calls": "4 — mcp__github__issue_read x4 (get, get_comments x3); no MCP write tool used", "api_writes": "2 — relay stroke fw-20261026T073604Z-98642f (pr_create: POST /repos/objectstack-ai/objectstack/pulls draft + POST /repos/objectstack-ai/objectstack/issues/25977/assignees os-steve, read-back identical 6242 bytes); this os-dev-report comment via post-stamped.mjs (POST /repos/objectstack-ai/objectstack/issues/25858/comments). git push is not counted.", "deviations": [ "files_changed outside the claim's file surface: scripts/sdui-manifest.record.json (generator-owned record; check-sdui-manifest hashes ui/packages registry sources, so any plugin-grid src edit moves it) and .changeset/25858-grid-group-order-by.md (named by the dispatch).", "No reviewer request on the PR: the session context asks for update_pull_request reviewers os-steve, the dispatch forbids MCP writes; the seat may request it. PR activity not subscribed, per the dispatch contract." ], "files_changed": [ ".changeset/25858-grid-group-order-by.md", "scripts/sdui-manifest.record.json", "ui/packages/plugin-grid/src/ObjectGrid.tsx", "ui/packages/plugin-grid/src/__tests__/groupOrderBy-25858.test.tsx", "ui/packages/plugin-grid/src/useGroupedData.ts", "ui/packages/plugin-grid/src/useServerGrouping.ts" ], "open_questions": [], "out_of_scope_findings": [ "carrier: HotCRM forecast.view.ts follow-up (drop the outer period_start level) — named by the card as not this card; noted, not filed" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Seat: PR #25977 ACCEPT (2026-10-26T08:04Z), checks green on head
19f3f573; ready + auto-merge requested. See the## 席位核对comment on the PR.