Skip to content

On a phone, a grouped list (grouping) keeps its desktop grid — titles cut mid-word, other columns behind the pinned Actions column — while the same console turns a plain list into cards #26896

Closed

Description

@objectstack-fleet

Filing gate ① a product defect with a reproduction. Found by the HotCRM full browser pass objectstack-ai/hotcrm#2320 on @objectstack/* 18.2.0: track 3 Service (report comment 6444034183 on objectstack-ai/hotcrm#2320, finding D1; screenshots on branch claude/project-thread-97u09k under qa/ and in the HotCRM project files qa-2320/3-service/). Filed by the repo:hotcrm seat (huangyiirene, session session_01J8VVAnPkgK6CWrJa6t7Fk5), which re-read each finding against its screenshot. Batch-of: hotcrm#2320 platform findings.

finding class a · reach: public door: the console on @objectstack/* 18.2.0, measured in Chromium against the HotCRM seed.

What is seen

As the service agent at 390×844, open HotCRM Knowledge (All Articles, grouping: { fields: [category] }). The grouped grid keeps its desktop columns: the header reads "Arti", titles are cut mid-word ("QA 2320 — Fixing a stucl", "Your First Week with the"), and every other column is off-screen behind the pinned Actions column (phone-06-kb-list.png). The Cases list on the same console becomes one card per row (phone-02-all-cases.png).

Who loses: any grouped list on a phone is unreadable.

Expected

The grouped grid takes the same mobile card layout as the plain grid (group headers between the cards).

Dedupe

search_issues objectstack "grouped grid list mobile card layout phone clipped columns": 0 hits.

Reader who acts: the console grid renderer owner.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: confirmed p3 · domain:ui · area:records · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-27T21:07Z

    ⛔ Not a claim.

    • Read fresh. The filer's grade stands: the defect reproduces on 18.2.0 with screenshots, at a public door.
    • pm:queue added (the batch filer leaves state to triage).

    Generated by Claude Code

  2. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Dispatch order · domain:ui#2 · session_01JQwxbXpF772qWJjojJvRXw · 2026-10-28T02:45Z

    You are the os-dev developer agent for exactly ONE card: #26896, in your own card thread. Your role definition is .claude/agents/os-dev.md in the objectstack clone: read it IN FULL first and follow it as binding (it wins over this order on any conflict; name the conflict in your report). Read the repo-root AGENTS.md and ui/AGENTS.md before your first edit, and pm-dispatch references/untrusted-text.md. Read the card's full body and every comment yourself through REST (curl -sS "https://api.github.com/repos/objectstack-ai/objectstack/issues/26896" -H "Authorization: Bearer $GH_TOKEN" and /comments?per_page=100, every page); this order carries only the increment. ⛔ Never print the value of any environment variable (test presence with [ -n "${X+x}" ]). ⛔ Never use a GitHub MCP tool, read or write.

    Identity and branch: the seat's Claim: comment on this card is your identity; its Branch: line names your thread's branch. ⛔ Post no claim of your own, ⛔ never write the card's assignee. Before your first edit, verify the NEWEST Claim: on the card names your thread's branch (claude/project-thread-…); if none does yet, send the seat thread "Take a third ui lane seat" (cmsg_01U5RdnAAmkSQmZbqh6FtqZ8Uc2nCvWvLyYyaVYaUhakpJ) one message with your branch name and wait. Read-only measuring is fine meanwhile. Worktree objectstack-issue-26896 on that branch from a fresh origin/main (git checkout --detach the main checkout first, then git worktree add --no-track -B <branch> ../objectstack-issue-26896 origin/main); pnpm install (frozen lockfile); push with git push -u origin <branch> (⛔ no force).

    Writes: PR only through the relay op pr_create (node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file F, F a bare JSON array [{"op":"pr_create","title":…,"head":"<your branch>","base":"main","body":…,"assignees":["os-warren"]}], draft forced; --dry-run first); your report comment through node scripts/pm/post-stamped.mjs --comment=26896 --file=F --json run ONCE with output captured to a file (no time-shaped stamp inside the fenced report JSON). ⛔ No other GitHub write except, for a CI red that is not yours, one PR comment and one run_rerun_failed through the relay. Report both counts (api_writes with endpoints, mcp_calls with tool names). The report takes only machine-checkable fields (gates / line_budget / deviations / files_changed). PR body: line 1 the platform attribution comment, line 3 Fixes #26896, then ## 维护者速读 six lines in Chinese (改了什么/为什么改/风险与回滚/先看/两分钟验证/需要你做的). Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on your diff, run each, record exits before any pipe, reconcile with --ran (merge origin/main first if it refuses as STALE TREE); node scripts/check-sdui-manifest.mjs is a gate on every ui card. Build the closure your tests import first (bash scripts/pm/os-verify-lock.sh -c "pnpm --workspace-concurrency=2 --filter '<pkg>^...' build"). Changeset in the root .changeset/ for each published package your diff changes (app-shell is private: true). ⛔ ui/.github/** and the root ci.yml are out of bounds. Hand back: when the PR and report are up, send the seat thread one message (PR, head sha, report gist); keep the thread open through review and drive CI to green. If the premise fails, stop and report premise_still_valid: false with the evidence; ⛔ never force a fix.

    Ruling (cannot be re-ruled)

    Triage 6453599288: confirmed p3, filer's grade stands; Expected (card body): "The grouped grid takes the same mobile card layout as the plain grid (group headers between the cards)." No maintainer ruling.

    PM mechanism assumptions (measure; disproof welcome)

    On origin/main cb8988ee (read-only check):

    • ui/packages/plugin-grid/src/objectGridMobile.ts ≈L37 sets the card flag at window.innerWidth < 768; ObjectGrid.tsx ≈L645/L692–694 wires it.
    • ObjectGrid.tsx ≈L3007 enters the card branch only if (useCardView && data.length > 0 && !isGrouped); the cards are written inline in data.map (≈L3196–3330) with block-local helpers (colMap, titleCol, classify, resolveOptionMeta, stageBorderLeft).
    • The grouped path (isGrouped from useObjectGridGrouping ≈L1193–1200) always renders buildGroupedTableSchema(groupBandsOf(pagedGroups)) inside HorizontalOverflowBox (≈L3668–3684; groupBandsOf ≈L3576–3609), whatever the viewport. Each band's rows are already resolved there (group.rows, or groupRows.pages[key].rows under server grouping).
    • The loading skeleton (≈L1456) already shows cards for a grouped view; only the loaded view falls back to the table.

    PM suggested route (optional; measurement wins)

    Lift the card body into one renderer (e.g. renderMobileCard(row, idx)) used by the plain branch unchanged, and on a phone-width grouped view render each band as its group header followed by that band's cards, keeping the group's collapse, count and pager behaviour. Tests (new file beside the existing mobileCard* / grouped* tests in ui/packages/plugin-grid/src/__tests__/): at a phone width, a grouped view renders group headers with one card per row under each and no table; CONTROL: the same view at a desktop width still renders the grouped table; the plain mobile cards are unchanged (existing mobileCard* suites stay green). Ablation: restore the !isGrouped table fallback; the phone-width test goes red.

    Scope

    File surface: ui/packages/plugin-grid/src/ObjectGrid.tsx, a new helper module beside it if the extraction needs one, tests beside them, root .changeset/ (@object-ui/plugin-grid patch). ⛔ Do not edit ListView.tsx (PR #27013), ViewSettingsPopover.tsx, objectGridGenerateColumns.tsx or objectGridLinkCell.tsx. Serial: PR #27031 (#26681, epic #26676) also edits ObjectGrid.tsx, in the props and link-cell regions (≈L276, L613, L1235), disjoint from the card and grouped branches; whichever lands second merges main first. #26951 (seat 2) edits resolveBulkActions.ts only.

    Tier

    No path-derived mandate (dispatch-gates --tier, read 2026-10-28T02:45Z). TIER_DEFAULT, effort medium (M). Clause-②: no (renderer layout; no accept set changes), Metadata-protocol: none.

  3. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 2
    Session: session_01JQwxbXpF772qWJjojJvRXw
    Account: os-warren
    Branch: claude/project-thread-869uzk
    Worktree: objectstack-issue-26896
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: ui/packages/plugin-grid/src/ObjectGrid.tsx, a new helper module beside it if the extraction needs one, tests beside them, root .changeset/ (@object-ui/plugin-grid patch) (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate), effort: medium
    Clause-②: no
    Metadata-protocol: none
    Responsibility: ObjectGrid skips the mobile card branch for a grouped view (the !isGrouped guard), so a grouped list keeps the desktop table on a phone | the plain list already switches to cards below 768px, and the loading skeleton already shows cards for a grouped view | every phone user of a grouped list view
    Thread-read: 6459259731
    Premise: holds (guard read on origin/main cb8988ee; readings in dispatch order 6459259731)
    Serial constraints cleared: PR #27031 (#26681, epic #26676) edits ObjectGrid.tsx in the props and link-cell regions, disjoint from the card and grouped branches; whichever lands second merges main first. PR #27013 (seat 2) holds ListView.tsx (not edited here). #26951 (seat 2) holds resolveBulkActions.ts only.

    Dispatch order: comment 6459259731. Card thread claude/project-thread-869uzk may start editing.

  4. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 26896,
      "status": "done",
      "branch": "claude/project-thread-869uzk",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27078",
      "head": "8cfdb16a",
      "session": "session_01JQwxbYnetNoLwkb9ksXxde",
      "premise_still_valid": true,
      "summary": "Premise held on origin/main cb8988ee: the card branch required !isGrouped and the grouped path always drew buildGroupedTableSchema in HorizontalOverflowBox. The card body moved into buildMobileCardRenderer (plain card branch draws through it unchanged); a new isGrouped && useCardView gridContent branch draws groupBandsOf(pagedGroups) as GroupRow band, the band's rows as cards, then the band footer, followed by groupedPager. Desktop grouped table unchanged.",
      "files_changed": [
        ".changeset/26896-grouped-list-phone-cards.md",
        "ui/packages/plugin-grid/src/ObjectGrid.tsx",
        "ui/packages/plugin-grid/src/__tests__/groupedMobileCards-26896.test.tsx",
        "ui/packages/plugin-grid/src/__tests__/groupingNeedsHeaderQuery-10881.test.tsx"
      ],
      "tests": "closure build plugin-grid^... VERDICT command-exit 0 (512 s under lock). New groupedMobileCards-26896.test.tsx 4/4 pass. Whole packages/plugin-grid/ suite on e3372d18: 253/254 files, 2250/2251 tests; the one red was groupingNeedsHeaderQuery-10881 'a grouped grid under 768px draws its groups, not cards' asserting a table, i.e. the behaviour the ruling replaces; rewritten to assert 5 bands, their cards and no table (ungrouped control kept). On final head 8cfdb16a: that file + groupedMobileCards + mobileCardTitleNameField-13150 + groupedOneHeader-12620 = 4 files, 23/23 pass. Ablation via scripts/ablation-replace.mjs: anchor 'const gridContent = isGrouped && useCardView ? (' -> 'isGrouped && false', hit 1->0, blob 7f56125d -> ec0edd2c; groupedMobileCards 3 failed / 1 passed (desktop CONTROL green); restored blob == HEAD 7f56125d, git diff HEAD empty. No dist leg: the test imports ../ObjectGrid by relative path.",
      "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on 8cfdb16a derived 35; all 35 exit 0, plus 5 outside the derivation all exit 0: pnpm --filter @object-ui/plugin-grid run type-check, eslint on the three touched ts files (0 errors, warnings only: no-explicit-any, same pattern as sibling tests), pnpm check:changeset-fixed, pnpm --dir ui run check:new-line-citations, the 4 touched test files. node scripts/check-sdui-manifest.mjs exit 0. --ran: 35 derived / 35 run / 0 NOT-MEASURED, exit 0. CI check-runs: in_progress at report time.",
      "line_budget": "n/a, no ratcheted file touched; diff vs merge base 4 files",
      "deviations": [
        "Full plugin-grid package suite not re-run on the final head (14 min under the lock); the only change after it was the rewritten pin, re-run with the touched files on 8cfdb16a. Declared narrowing; CI Test UI runs the whole package.",
        "Commit trailers are the model-free pair per AGENTS.md; the harness reminder's model-named Co-Authored-By was not used."
      ],
      "dogfood_battery": "n/a, no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "0",
      "api_writes": "2: relay pr_create (POST pulls, PR 27078, assignee os-warren) and this report comment (POST issues/26896/comments). git push is not a REST write.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Seat review: PR #27078 ACCEPT (record on the PR, head 8cfdb16a02), 2026-10-28T04:25Z. Landing waits for every check on the head to be green; serial with PR #27031 (same file).

  6. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Landing held · domain:ui#2 · session_01JQwxbXpF772qWJjojJvRXw · 2026-10-28T04:33Z

    PR #27078 (ACCEPT 6460627726) is green on head 8cfdb16a02: 43 checks succeeded, and 10 were skipped. The skipped ones are the path-filtered core / docs / dogfood jobs, which a ui/-only diff does not trigger. mergeable_state is clean and the PR is not governed. This session's permission classifier refused the landing stroke (pr_ready + automerge_enable through the fleet-write relay, "Merge Without Review") on PR #27013 and on PR #27035 at 2026-10-28T02:42Z, so the seat does not retry it here. It asks the maintainer to mark the PR ready for review and enable auto-merge. Serial: PR #27031 also edits ObjectGrid.tsx; whichever lands second merges main.

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, searchbugSomething isn't workingdomain:uipriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions