Skip to content

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

Closed

Description

@os-steve

Filing gate ① a defect with a named landing site (an app is carrying a workaround for a missing spec key; hotcrm AGENTS.md says the gap belongs here). Batch-of: 2026-10-25 browser pass on @objectstack/* 18.1.0 (23 cards: hotcrm 10, objectui 10, objectstack 3). Filed by the maintainer's account (os-steve) from the HotCRM pass driven in session session_01AwpXhq2nFKU6J7GmbCEmZu; ⛔ not a claim. Grade and routing are triage's.

What is seen

HotCRM's 销售预测 list groups by period_start (desc) and then by period_label, because grouping on period_label alone sorts the periods as text ("Sep 2026 → Q4 2026 → Q4 2025 …"). The result is two header rows for every single forecast: 「周期起始 10月1日 · 1」 then 「Oct 2026 · 1」 then the row — six forecasts take 18 rows. 45-crm_forecast.png. The view's own comment (src/sales/views/forecast.view.ts ~:40–63) says why: GroupingFieldSchema is field / order / collapsed, with no key that orders one field's groups by another.

reach: a named producer (HotCRM forecast.view.ts, shipped) and the public UI, measured once.

Expected

GroupingFieldSchema gains an orderBy (a field of the same object whose value orders the groups; default = the grouping field), the server-side grouping query (#14556's shape) sorts groups by it, and objectui's grouped grid renders the group label from field while ordering by orderBy. HotCRM then drops its outer level (one line in the view).

Cross-layer: spec first, then the engine's grouping aggregate, then objectui — the usual parent + per-layer children if triage files it that way.

Dedupe

MCP search_issues objectstack "view grouping sort groups by a different field than the grouping field" → 1 hit, #14556 (closed: server-side grouping, per-group counts, paging), which does not add an ordering field. Report: REPORT.md §3.2.

Activity

  1. added
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    enhancementNew feature or request
    priority:p1High: required for production / M2
    and removed on Oct 25, 2026
  2. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    Contributor

    Triage: first grade — enhancement · priority:p1 · domain:spec · area:records · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-25T11:58Z

    ⛔ Not a claim, ⛔ not a dispatch. finding comes off.

    • p1 by 「协议新增 p1」: the platform answer adds an authorable key (a protocol addition). Spec first, then the runtime. A group-order key on a list-view grouping field (order a label group by another field). Then objectui renders it, and HotCRM drops its two-level workaround.

    Generated by Claude Code

  3. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 2
    Session: session_01E1GNboWQRhh5vidwh2hxTY (the card thread; seat session session_01K2Tat8JEt4AhLASV4V4QWE posts this claim)
    Account: marchtian
    Branch: claude/project-thread-zciswd
    Worktree: objectstack-issue-25786
    Domain: domain:spec
    Seat: domain:spec#4 (seat post #18917)
    File surface: packages/spec/src/ui/view.zod.ts (GroupingFieldSchema) + lint/validate + any server grouping read the measurement needs + tests + changeset; objectui child filed, not edited
    Container & model: M, mode:thread, model: default tier (dispatch-gates --tier: "no path-derived mandate", floor sonnet · default opus · ceiling fable), effort: xhigh (p1)
    Clause-②: yes
    Metadata-protocol: added
    Responsibility: n/a — not a defect card
    Thread-read: 6397346626
    Premise: holds (at main 5f5f0b42: GroupingFieldSchema is field / order / collapsed, no order-by key; no open PR names #25786)
    Ruling-ref: none; triage first grade under 「协议新增 p1」
    Serial constraints cleared: #25755 (seat 2) is in field schemas, not view.zod.ts; no overlap.

    Order: /mnt/project-files/spec-seat/orders/25786.md with _common.md (project files). Batch of six on the project owner's order 「你应该并发6」 (2026-10-25). Independent full Fable review of the diff before the seat's contract review and ACCEPT.

  4. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    Contributor

    os-dev-report

    {
      "issue": 25786,
      "status": "done",
      "branch": "claude/project-thread-zciswd",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/25828",
      "session": "session_01E1GNboWQRhh5vidwh2hxTY",
      "head": "21dc9ad78953f57f991b69c25800a2db82db8d9d",
      "premise_still_valid": true,
      "summary": "GroupingFieldSchema gains orderBy (a field of the same object; absent or equal to field = order by the group key). compileListViewGroupQuery adds one min (asc) / max (desc) node per ordered level, and listViewGroupOrderColumn names the header-row column to sort by. Lint judges orderBy on validate/build/runtime save: list-view-field-unknown, list-view-field-dotted (new aggregate axis), and the new list-view-group-order-unorderable. Measured: the server needs no new code. The sort stays the consumer's; the existing aggregate door serves min/max over date/datetime identically on both tiers and refuses text and dotted operands 400 INVALID_FIELD (rest door test §11). The card's Expected line 'the server-side grouping query sorts groups by it' is answered as: the header query carries the value, the consumer sorts the whole header set.",
      "tests": "final head 21dc9ad7: `pnpm --filter @objectstack/spec exec vitest run` 800 files, 23119 passed / 1 todo; `pnpm --filter @objectstack/lint exec vitest run` 163 files, 7331/7331; `pnpm --filter @objectstack/rest exec vitest run src/list-view-grouping-query-door.test.ts` 53/53 (9 new in §11). Ablations via scripts/ablation-replace.mjs, each restored blob == HEAD: lint verdict `|| true` 8 red; compiler `...orders.slice(0, 0)` 5 red; padded-name regex widened to `/[\\s\\S]*/` 6 red.",
      "gates": {
        "derived": "node scripts/pm/dispatch-gates.mjs --commands on the final diff: 114 commands",
        "local": "114 run on head 21dc9ad7, exit codes recorded before any pipe: 114 x exit 0",
        "ran_reconciliation": "Run reconciliation — 114 derived, 114 run, 0 NOT-MEASURED, 0 UNRUN (exit 0)",
        "prerequisites": "full `pnpm build` 76/76 (check:dual-build-cjs-loads, check:skill-examples read built output); `git fetch --depth=1 origin 621a4876` for check-plugin-teardown-shape --self-test's pinned fixture",
        "extra_run": "roster gates with a roster under this diff's paths + typechecks, all exit 0: check-changeset-fixed, check:meta-url-spelling, check:authz-resolver, check:error-code-casing, check:filter-alias-parity, check:error-code-provenance, spec/lint/rest typecheck",
        "ci_at_report": "PR head check-runs: 45 total — 15 success, 10 skipped, 20 in_progress, 0 failure"
      },
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "review": {
        "reviewer": "isolated Agent subagent at CONTRACT_REVIEW_TIER (the user's full-review rule for spec changes), whole diff origin/main...HEAD",
        "verdict_comment_id": 6400202111,
        "verdict_comment": "https://github.com/objectstack-ai/objectstack/pull/25828#issuecomment-6400202111",
        "rounds": "review 1 CHANGES-REQUIRED (1 major, 2 minor, 2 nit) → fixed in 5d64c378, 6a3bf131, d37faf8f → review 2 ACCEPT-WITH-NITS (5/5 closed, 1 new nit) → 5df57e7f → nit confirmed closed"
      },
      "line_budget": {
        "changed_lines": 992,
        "insertions": 972,
        "deletions": 20,
        "files": 15,
        "human_merge_threshold": 3000,
        "verdict": "under",
        "ratchet_ledgers_touched": 0,
        "dropped_refinements_baseline": "unchanged"
      },
      "files_changed": [
        ".changeset/25786-grouping-order-by.md",
        "content/docs/references/ui/component.mdx",
        "content/docs/references/ui/view.mdx",
        "packages/lint/src/index.ts",
        "packages/lint/src/rule-explanations.ts",
        "packages/lint/src/validate-list-view-field-refs.test.ts",
        "packages/lint/src/validate-list-view-field-refs.ts",
        "packages/rest/src/list-view-grouping-query-door.test.ts",
        "packages/spec/api-surface/ui.json",
        "packages/spec/authorable-surface/ui.json",
        "packages/spec/export-origins/ui.json",
        "packages/spec/src/ui/view-grouping-query.test.ts",
        "packages/spec/src/ui/view-grouping-query.ts",
        "packages/spec/src/ui/view.test.ts",
        "packages/spec/src/ui/view.zod.ts"
      ],
      "deviations": [
        "First spec ablation was a no-op: the replacement contained the anchor, so ablation-replace counted 1 → 1. Re-run with `...orders.slice(0, 0)`: 5 red.",
        "After a scratch probe, `git checkout HEAD -- packages/rest/src/list-view-grouping-query-door.test.ts` wiped the uncommitted §11 tests. They were re-applied from a saved script and committed (d4a9b8fc). Nothing is missing from the final diff.",
        "The harness attribution reminder asked for a model-named Co-Authored-By trailer. The pre-push check:commit-card-trailers refused 4 unpublished commits for it. The unpushed range only (HEAD ^origin/main ^d4a9b8fc) was reworded with git filter-branch --msg-filter, with identical trees, before any push. No published history was rewritten.",
        "Review F1: the reviewer suggested superRefine, but a native .regex() was taken instead. superRefine made the spec build fail on dropped-refinements.baseline.json (18 new sites). The regex publishes the rule as a JSON Schema pattern. Review 2 accepted it, and its nit (why `field` keeps superRefine) was answered in a TSDoc.",
        "main was merged twice: at 6679644a before review, and at abebc4e1 after sibling spec card #25755 landed (no conflicts). Gates and full suites were re-run on the merged head.",
        "The order asked me to file the objectui child card, so I ran its dedupe search myself: 2 read-only MCP search calls. os-dev normally leaves dedupe to the filing seat."
      ],
      "mcp_calls": "2 — mcp__github__search_issues x2 (read-only, objectui dedupe for the child card); no MCP write tool",
      "api_writes": "4 — POST /repos/objectstack-ai/objectstack/dispatches x4 via scripts/pm (fleet-write relay, as objectstack-fleet[bot]): pr_create #25828; comment 6400202111 on #25828 (review verdict); issue_create objectstack-ai/objectui#13546; this os-dev-report comment. Plus git pushes of the branch (not REST).",
      "child_cards": [
        "https://github.com/objectstack-ai/objectui/issues/13546 — plugin-grid renders the label from `field` and orders headers by listViewGroupOrderColumn; carries Blocked-by: objectstack-ai/objectstack#25828"
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "noted, not filed (no named producer, so not class c) · a dotted grouping.fields[].field is unjudged by lint, pinned silent in packages/lint/src/validate-list-view-field-refs.test.ts, while POST /api/v1/data/:object/query refuses it with 400 INVALID_FIELD \"Unknown field 'period_start.year' … a related record's column cannot be a grouping target\" (measured through the §11 fixture) · carrier: none · in PR Acceptance notes · dedupe words: grouping field dotted path lint, list-view-field-dotted grouping.fields field, grouping target related record column",
        "noted, not filed (polish) · `field` keeps its superRefine and dropped-refinements row; the same .regex() would shrink the ledger but changes its issue code custom → invalid_format (consumer-visible) · carrier: none · in PR Acceptance notes and view.zod.ts TSDoc"
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    Contributor

    ACCEPT → PR #25828 comment 6400515519 at head 21dc9ad78953 · seat domain:spec#4 · 2026-10-25T17:01Z. Independent full review (6400202111) and contract review PASS (6400331596); CI green; not governed. Marked ready and enqueued through the relay (run 40214407071).

  6. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    Contributor

    Landed · seat domain:spec#4 · 2026-10-25T17:31Z. PR #25828 merged through the queue, merge commit b00fc55c7d71; #25786 closed completed by Fixes; pm:dispatched removed. The objectui half is objectui#13546 (renders field as the label, orders by orderBy), now unblocked; HotCRM drops its outer grouping level after a release carries this.

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

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:specenhancementNew feature or requestpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions