You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Lists at 1024 px: a status chip is not cut to "Publish…", and a table wider than its panel shows that it scrolls #12708
Knowledge Base (/crm_knowledge_article, grouped by Category): the Status column is narrow enough that the chip reads "Publish…" (Published) and "Draft" fits; the Actions column at the right is an empty header.
Setup › Approvals Inbox: the table's columns Request / Record / Requester / Status / Submitted / … overflow the panel; "Submitted" is cut to "Submitt…" and the dates to "7/27/2…", with no scrollbar or shadow to say the table continues.
Target
A chip cell never ellipsizes a single-word value: the column's minimum width is the widest chip of the visible rows, measured once per data load; where the grid must shrink, text columns give way before chip columns.
A table wider than its container shows a right-edge shadow and a scrollbar on hover; the last column (Actions) stays pinned.
The Actions column is not drawn when no row action is offered.
File surface
packages/plugin-grid/src/ (column sizing, the overflow affordance), the approvals inbox table in packages/app-shell/src/views/setup/ (or wherever it mounts its own table), tests beside.
Acceptance
Tests: a chip column's min width equals its widest chip; the overflow shadow appears when scrollWidth > clientWidth.
Screenshots of Knowledge Base and the Approvals Inbox at 1024 px.
pnpm --filter @object-ui/plugin-grid test, pnpm --filter @object-ui/app-shell test green.
Filed by the epic PM session session_cdb7a717-def7-4e71-9988-f089fbaa18ff from the 2026-10-16 fourth review: objectui main efa9cb4fa, HotCRM 4f82b815 on platform 18.0.0-next.2, the vite console on :5180 against objectstack dev on :4001, the maintainer's browser pane at 1024 × 768 (dark, en). The maintainer asked for every finding to be filed on the repository it belongs to, verbatim: 「全部立卡,平台问题到 objectui,审计日志核查到 objectstack,HotCRM 转化流程到 hotcrm。」
Claim: PM loop round 3 (priority:p2; filed by the epic PM session from the 2026-10-16 fourth review, on the maintainer's 「全部立卡,平台问题到 objectui…」)
Session: session_01SJn1GEkrbbBQkhGC26wEDw
Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-12708-list-chip-width-overflow
Worktree: objectui-issue-12708 (in the cloud session's own container)
Domain: domain:ui
Seat: domain:ui#4
File surface: packages/components/src/renderers/complex/data-table.tsx (column sizing and the horizontal-overflow affordance), packages/plugin-grid/src/ObjectGrid.tsx (when the Actions column is drawn), apps/console/src/pages/system/ApprovalsInboxPage.tsx (its own table's overflow), tests beside each, one .changeset/12708-*.md. Measured on main0171c24: the card names plugin-grid and views/setup/, but the sizing lives in components' data table and the inbox lives in apps/console. packages/components/src/ui/ (Shadcn-synced primitives) is out of bounds (stop on breach; explain in the report)
Container & model: L, mode:cloud, model: opus (cloud on the maintainer's stance 「可以的话,ui 后续开发尽量用云 session,并且要求截图给我看」; lane scope on 「你也可以接ui车道的卡」; batch 3 on 「当前任务处理完,后续并发降到3」, this session's chat, 2026-10-16)
Clause-②: yes
Clause-② reason: DataTableSchema and its column type are exported from @object-ui/types, and sizing a chip column by its widest chip may need the column to say it draws chips (one optional member). «拿不准 ⇒ 按 yes». The seat's TIER_DEFAULT review comes before ready; if nothing published moves, the review says so.
Responsibility: n/a — measured list layout defects at 1024 px
Thread-read: none
Serial constraints cleared: PR #12775 (objectui#12697, another seat) edits plugin-grid's ImportWizard.tsx only. This seat's objectui#12695 (the list toolbar) landed as 0171c24. No open objectui PR touches data-table.tsx, ObjectGrid.tsx or ApprovalsInboxPage.tsx (read at dispatch).
Claim: PM loop round 3 · amended (file surface and Clause-② reason, as measured in the report) (priority:p2; filed by the epic PM session from the 2026-10-16 fourth review, on the maintainer's 「全部立卡,平台问题到 objectui…」)
Session: session_01SJn1GEkrbbBQkhGC26wEDw
Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-12708-list-chip-width-overflow
Worktree: objectui-issue-12708 (in the cloud session's own container)
Domain: domain:ui
Seat: domain:ui#4
File surface: packages/components/src/renderers/complex/data-table.tsx (column sizing and the horizontal-overflow affordance), packages/components/src/custom/ (the one shared overflow box and its hook, new use-horizontal-overflow.ts and horizontal-overflow.tsx, and custom/index.ts), packages/plugin-grid/src/ObjectGrid.tsx (when the Actions column is drawn, and the chip flag), apps/console/src/pages/system/ApprovalsInboxPage.tsx (its own table's overflow), packages/types/src/data-display.ts and packages/types/src/zod/data-display.zod.ts (the one optional TableColumn member, its zod mirror and its static-table tombstone), content/docs/components/complex/data-table.mdx (the guide), tests beside each (including packages/types/src/__tests__/static-table-narrow-surface.test.ts and the updated RowActionMenu.emptyGuard.test.tsx case), one .changeset/12708-*.md. Amended on the dev's report d2b81345: the types files, the shared custom/ files and the guide were not in the first surface. packages/components/src/ui/ (Shadcn-synced primitives) stays out of bounds
Container & model: L, mode:cloud, model: opus (cloud on the maintainer's stance 「可以的话,ui 后续开发尽量用云 session,并且要求截图给我看」; lane scope on 「你也可以接ui车道的卡」; batch 3 on 「当前任务处理完,后续并发降到3」, this session's chat, 2026-10-16)
Clause-②: yes
Clause-② reason: two published faces move. (1) TableColumn in @object-ui/types takes one optional member, chips?: boolean, mirrored in zod and tombstoned on the static table surface, because the table cannot otherwise tell a chip column from text. (2) @object-ui/components' entry (export * from './custom') exports the one shared overflow box, because its consumers sit in other packages: HorizontalOverflowBox and its props type (plugin-grid's grouped scroller, the console's Approvals Inbox) and PINNED_RIGHT_SEAM_SHADOW_CLASSES (plugin-grid's pinned Actions cells). Only those names are exported; helpers no other package reads stay module-internal. The seat's TIER_DEFAULT review comes before ready.
Responsibility: n/a — measured list layout defects at 1024 px
Thread-read: 6223619921
Serial constraints cleared: PR #12775 (objectui#12697, another seat) edits plugin-grid's ImportWizard.tsx only. This seat's objectui#12695 (the list toolbar) landed as 0171c24. No open objectui PR touches data-table.tsx, ObjectGrid.tsx or ApprovalsInboxPage.tsx (read at dispatch).
{
"issue": 12708,
"status": "done",
"branch": "claude/issue-12708-list-chip-width-overflow",
"pr": 12822,
"session": "session_01SPYcVSSndYVgRePzs2F7sD",
"premise_still_valid": true,
"summary": "Built all three targets on objectui main (merged up to 8cc02b4; head cc75474). (1) Chip columns: data-table floors any column flagged with the new optional TableColumn member `chips` at the widest chip drawn on screen. The floor is measured once per data load (a new data prop, page or page size) and sits on top of a declared width or a user's resize; text columns keep their width and truncate. ObjectGrid sets the flag at its single column fold for select, status, multiselect, radio, checkboxes and tags. (2) Overflow: there is now one shared piece in packages/components/src/custom: `useHorizontalOverflow` and `HorizontalOverflowBox` in horizontal-overflow.tsx, plus use-horizontal-overflow.ts. It draws a right-edge shadow while the box can scroll further right, and shows the scrollbar on hover or focus. data-table's own scroll box uses it to decide when to show its shadow; it keeps its existing #12463 overlay to place it. The grouped grid's scroller, which had no shadow, now uses it, and the right-pinned Actions cells draw the shadow on their own left edge. The Approvals Inbox table, which also had none, now uses it. The Actions column stays pinned right. (3) ObjectGrid draws the Actions column only when at least one row's plan offers an inline button or a menu item. The plan is computed with planRowActionMenu, the same function and inputs RowActionMenu draws from. Premise check: the card's symptoms are real, but two causes differ from the card and the PM's assumptions. On KB the 'Publish…' is the pinned Actions column covering the Status chip at the scroll seam, not an ellipsis. The Actions column there is not empty: four ⋮ triggers are present, hidden until row hover (see findings).",
"findings": [
"KB (Knowledge Base, grouped by Category) at 1024 px, measured on 0171c24: the Status column is 130 px. That is HotCRM's own view width (`{ field: 'status', width: 130 }` in src/service/views/knowledge_article.view.ts), an explicit width, so data-table never auto-sizes it. The 'Published' chip draws about 70 px, so it fits and has no ellipsis (each chip element has scrollWidth equal to clientWidth). It reads 'Publish…' because the grouped table is 1477 px wide inside ObjectGrid's grouped scroller, which is 768 px wide. The sticky right-pinned Actions header and cells (67 px, left edge at x=957) cover the Status cell, which spans up to x=1026. Nothing said the table continues. data-table's #12463 shadow is skipped for grouped tables (disableInnerScroll), and the grouped scroller drew none.",
"KB Actions header: it is not empty. All 4 rows render a ⋮ trigger (data-testid row-action-trigger) with Edit and Delete. Each trigger stays at opacity 0 until its row is hovered or focused (objectui#12423), so at rest the column looks empty.",
"Approvals Inbox at 1024 px, with 3 pending account approvals created on the local backend: the table is 1071 px inside a 718 px box (the shadcn Table wrapper, `overflow-auto`). The Record column takes 470 px despite `w-[30%]`, because the long record titles are nowrap. Requester, Status, Submitted and the inline Approve/Reject column run past the card's right edge. On current main the dates and 'Submitted' are not cut to 'Submitt…'; they sit past the edge with no shadow or other cue.",
"PM assumption 1 is partly false: auto-sized columns are already sized from the drawn content in px (#11682), not from a character estimate. The KB Status column has an explicit width, so neither path sizes it.",
"PM assumption 2 is true as a reading of the code (the column was drawn whenever operations.update or operations.delete was set, or any row-action list was non-empty). It is not the cause on KB, whose rows do offer actions.",
"PM assumption 3 is confirmed: the inbox uses its own Table with w-[22%], w-[30%], w-[16%], w-[110px] and w-[132px], and has no overflow cue.",
"PM assumption 4 is false: data-table already draws a right-edge shadow for its own scroller (objectui#12463). The shadow was missing only for the grouped table, which scrolls in ObjectGrid, and for the inbox."
],
"tests": "All runs are on head cc75474 unless noted. New pins: packages/components/src/renderers/complex/__tests__/data-table-chip-floor-12708.test.tsx (5 tests): a chip column's min width equals its widest chip after a data load (114px for 'Published' under the stub); a text column keeps its declared 80px while the chip column beside it rises to its floor; a wider declared width is kept; the floor is measured once per data load (a re-draw within the same load keeps it, a new load measures again); CONTROL: an unflagged column is unchanged. packages/components/src/__tests__/horizontal-overflow-12708.test.tsx (5 tests): the shadow and data-overflow-right appear when scrollWidth > clientWidth and go away at the right end; no overflow, no shadow; seam=pinned draws no edge overlay; hover-scrollbar classes; one pixel of slack. packages/plugin-grid/src/__tests__/actionsColumnAndChips-12708.test.tsx (7 tests): no Actions column when every row is refused every action; the column is drawn as soon as one row offers an action; no column when nothing is wired; CONTROL: a list with row actions keeps its pinned Actions column (sticky right-0 on the header and every cell); a legacy row action alone draws the column; a select column is measured under an explicit width and a text column is not; the grouped scroller is the overflow box and the pinned Actions cells carry the seam shadow, with no edge overlay when overflowing. apps/console/src/pages/system/ApprovalsInboxPage.tableOverflow-12708.test.tsx (2 tests): the inbox table sits inside the overflow box, and the shadow appears while overflowing and goes away at the right end. Updated pin: RowActionMenu.emptyGuard.test.tsx, case 'every row suppressed', now asserts no Actions column (it pinned the empty header that Target 3 removes). Ablations, run through objectstack scripts/ablation-replace.mjs with an anchor that must hit, restored blob checked against HEAD and git diff HEAD empty after every leg (vitest aliases @object-ui/* to src, so no dist rebuild applies): A1 floor removed: 3 failed of 5. A2 once-per-load guard removed: first run stayed GREEN (5 passed), so that pin was vacuous; the test was rewritten to rename a measured header, and the re-run gave 1 failed of 5. A3 hasOverflowRight returns false: 5 failed of 11 (overflow box, inbox and #12463 suites). A4 Actions verdict reverted to wiring only: 2 failed of 41. A5 chips stamp set to false: 1 failed of 7. A6 grouped box seam forced to edge: first run stayed GREEN (7 passed) because it had no overflow geometry, so that pin was vacuous; geometry was added, and the re-run gave 1 failed of 7. Final `git diff HEAD` is empty.",
"gates": "Each exit code below was captured before any pipe, from the os-verify-lock VERDICT line or an echoed $?. Results: `pnpm --filter @object-ui/plugin-grid test` (full, once, foreground) exit 0: Test Files 223 passed (223), Tests 2049 passed (2049). `pnpm --filter @object-ui/app-shell test` (full, once; run detached and waited on in the foreground with `tail --pid` because the container caps foreground commands at about 10 min) APP_SHELL_EXIT=0: Test Files 1495 passed, 1 skipped (1496), Tests 14039 passed, 9 skipped. An earlier app-shell run was stopped by its PID before it reported, because the code changed under it; the run above is the only complete one. Components, types and the console Approvals Inbox suites: `pnpm exec vitest run --maxWorkers=2 packages/components/ packages/types/src/__tests__/ apps/console/src/pages/system/` exit 0: Test Files 814 passed, 1 skipped, Tests 14694 passed, 24 skipped. type-check for @object-ui/types, @object-ui/components and @object-ui/plugin-grid: Done for each. `pnpm --filter '@object-ui/console^...' build && pnpm --filter @object-ui/console type-check && pnpm --filter @object-ui/console build` exit 0. A first console type-check failed only with TS2307 on dependency dist/ files that were not built yet; it went green after the dependency build. `eslint` on the touched .ts/.tsx files exit 0 (0 errors; warnings are pre-existing `no-explicit-any` and two `any` in the new test helper). With `--no-inline-config` it reports 3 errors, all pre-existing `react-hooks/static-components` lines that already carry inline disables (ApprovalsInboxPage, data-table and ObjectGrid lines this diff does not touch). `pnpm check:i18n-keys` exit 0, `pnpm check:i18n-drift` exit 0, `pnpm check:new-line-citations` exit 0 (0 new), `node scripts/check-changeset-presence.mjs` exit 0, `node scripts/check-changeset-no-major.mjs` exit 0. No i18n keys were added or retired, so designer-parity and dead-keys were not run. CI check-runs: NOT MEASURED, because no PR is open; the PM opens it.",
"mcp_calls": "2 — mcp__github__issue_read (get; get_comments) on objectui#12708. No GitHub write tool was called. Also 2 calls to mcp__claude-code-remote__add_repo, access read, for objectstack and hotcrm: both returned read_available and attached nothing.",
"api_writes": "0 REST writes. git push only: the feature branch claude/issue-12708-list-chip-width-overflow (empty probe, then 7 pushes, head cc754746e3f0) and the evidence branch claude/issue-12708-evidence (1 push, 0b585d4fe3ae). No PR, no comment, no label, no assignee write.",
"deviations": [
"Published member: `chips?: boolean` on TableColumn in packages/types/src/data-display.ts, with its zod mirror (`z.boolean()`), a static-table tombstone in the StaticTableColumn interface and in StaticTableColumnSchema, and the static-table-narrow-surface.test.ts key lists. The table cannot otherwise tell a chip column from text: TableColumnType has no select member and the `cell` renderer is opaque. Clause-② covers exactly this one member. These files are outside the claim's surface.",
"New files outside the claimed surface, as the ruling asks (outside packages/components/src/ui/): packages/components/src/custom/use-horizontal-overflow.ts (hook, hasOverflowRight, class constants) and horizontal-overflow.tsx (HorizontalOverflowBox), exported from custom/index.ts.",
"Overflow placement has two forms on one shared detection. data-table's own scroller keeps its pre-existing #12463 overlay, placed by measured right and bottom offsets: that is a pre-existing inline `style`, now gated by the shared hook. The grouped grid uses the pinned-cell seam classes, and the inbox uses the box's edge overlay. New code has no inline style.",
"HOVER_SCROLLBAR_CLASSES uses Tailwind's important modifier (`!`). The console themes every scrollbar with an unlayered `* { scrollbar-color }` rule (apps/console/src/index.css), which beats any layered utility; this was measured live.",
"An existing pin was updated: packages/plugin-grid/src/components/__tests__/RowActionMenu.emptyGuard.test.tsx, case 'every row suppressed', asserted the empty Actions header that Target 3 removes.",
"Docs: content/docs/components/complex/data-table.mdx gains 'Chip columns' and 'Wide tables' sections (AGENTS.md #2).",
"Contract conflict, reported rather than settled silently: os-dev.md says the dev opens the draft PR and posts the report as an issue comment. This dispatch's degraded channel forbids both, because the fleet-write relay gets 403 from this container. I followed the dispatch: pr null, body under pr_body_draft.",
"Commit hygiene: the merge commit from origin/main was amended locally, before its first push, to carry the model-free trailer pair; no pushed history was rewritten. The first commit's body contains a plain 'Refs objectui#12708' paragraph. It is not a trailer and not a closing keyword.",
"Worktree cleanup: the evidence worktree is removed. The feature worktree ../objectui-issue-12708 is kept because the PR is not open yet (contract: remove after the PR opens). The backend (:4127) and console (:5297) I started were stopped by PID tree, and both ports read free."
],
"files_changed": [
".changeset/12708-list-chip-width-overflow.md",
"apps/console/src/pages/system/ApprovalsInboxPage.tsx",
"apps/console/src/pages/system/ApprovalsInboxPage.tableOverflow-12708.test.tsx",
"content/docs/components/complex/data-table.mdx",
"packages/components/src/__tests__/horizontal-overflow-12708.test.tsx",
"packages/components/src/custom/horizontal-overflow.tsx",
"packages/components/src/custom/use-horizontal-overflow.ts",
"packages/components/src/custom/index.ts",
"packages/components/src/renderers/complex/data-table.tsx",
"packages/components/src/renderers/complex/__tests__/data-table-chip-floor-12708.test.tsx",
"packages/plugin-grid/src/ObjectGrid.tsx",
"packages/plugin-grid/src/__tests__/actionsColumnAndChips-12708.test.tsx",
"packages/plugin-grid/src/components/__tests__/RowActionMenu.emptyGuard.test.tsx",
"packages/types/src/data-display.ts",
"packages/types/src/zod/data-display.zod.ts",
"packages/types/src/__tests__/static-table-narrow-surface.test.ts"
],
"screenshots": [
"https://github.com/objectstack-ai/objectui/blob/0b585d4fe3ae5ed2d9ad313da291869c74563c99/docs/design/findings/12708/kb-grouped-1024-light.png?raw=true — Status chips Published, Published, Published, Draft (none ellipsized); Actions column drawn, 4 triggers; grouped scroller scrollWidth 1477, clientWidth 768; seam shadow on",
"https://github.com/objectstack-ai/objectui/blob/0b585d4fe3ae5ed2d9ad313da291869c74563c99/docs/design/findings/12708/kb-grouped-1024-dark.png?raw=true — same readings, dark theme",
"https://github.com/objectstack-ai/objectui/blob/0b585d4fe3ae5ed2d9ad313da291869c74563c99/docs/design/findings/12708/kb-grouped-1280-light.png?raw=true — same chips; Actions drawn, 4 triggers; scrollWidth 1477, clientWidth 1024",
"https://github.com/objectstack-ai/objectui/blob/0b585d4fe3ae5ed2d9ad313da291869c74563c99/docs/design/findings/12708/approvals-inbox-1024-light.png?raw=true — Status chips Pending ×3 (none ellipsized); the inbox's own Approve/Reject column is present; scrollWidth 1071, clientWidth 718; edge shadow on; scrollbar shown on hover",
"https://github.com/objectstack-ai/objectui/blob/0b585d4fe3ae5ed2d9ad313da291869c74563c99/docs/design/findings/12708/approvals-inbox-1024-dark.png?raw=true — same readings, dark theme",
"https://github.com/objectstack-ai/objectui/blob/0b585d4fe3ae5ed2d9ad313da291869c74563c99/docs/design/findings/12708/dom-readings.json?raw=true — the DOM readings above, as captured"
],
"line_budget": "n/a",
"open_questions": [
{
"question": "Target 1's 'where the grid must shrink, text columns give way before chip columns'. data-table never shrinks a column below its declared or estimated width (each cell's style sets min-width equal to width), so a too-wide table scrolls. I implemented this as precedence of floors: a chip column never goes below its widest chip, and a text column keeps its width and truncates. At 1024 the KB table still scrolls (1477 vs 768), and the new seam shadow now shows it.",
"options": [
"A. Keep the floor-precedence reading (as built): no column is shrunk to fit; the shadow signals scrolling.",
"B. Let text columns shrink below their declared width, toward a floor, until the table fits the panel, before it scrolls. This changes every list's layout and many min-width pins."
],
"recommendation": "A. Business need: the measured KB defect is the covered chip plus no scroll cue, and both are now closed. Long-term: B rewrites the sizing model of every list. Guarding against AI mistakes: neutral. Startup focus: B widens scope with no measured need behind it."
},
{
"question": "Actions-column verdict reading (the smallest correct reading, as the ruling asks).",
"options": [
"As built: computed in ObjectGrid's render over every row the grid holds (all groups when grouped), using planRowActionMenu with the same inputs RowActionMenu uses: capability-gated defs, per-record visibility predicates, canEdit/canDelete ANDed with rowOperations and the record-level verdicts (now one shared `rowCrudVerdicts` helper). It stops at the first row that offers an action. It is not memoised, because no hook may sit below the early returns. A disabled item counts as offered, as the ⋮ guard already counts it. Record-level verdicts arrive asynchronously, so the column can appear or vanish once they settle."
],
"recommendation": "Accept as built."
},
{
"question": "On KB the ⋮ triggers stay at opacity 0 until hover (objectui#12423), so the Actions column looks empty at rest even when rows offer Edit and Delete. Not changed here: that hover rule is objectui#12423's, outside this card.",
"options": [
"A. Leave it",
"B. A separate card if the maintainer wants a resting cue"
],
"recommendation": "A, unless the maintainer asks for B."
}
],
"out_of_scope_findings": [
"class: a · reach: Setup › Approvals Inbox at 1024 px (HotCRM, 3 pending requests). The table's scrollWidth is 1071 against a clientWidth of 718, and the inline Approve/Reject column is unpinned and sits past the card's right edge until the user scrolls. The Record column (w-[30%], nowrap titles) takes 470 px. This PR adds the overflow cue but does not pin the inbox's actions column or let Record truncate · evidence: approvals-inbox-1024-light.png and dom-readings.json on the evidence branch · dedupe words: approvals inbox, actions column, pinned, record column width, 1024"
],
"pr_title_draft": "fix(grid): chip columns never ellipsize, wide tables show they scroll, Actions only when a row offers one",
"pr_body_draft": "(elided here: it is the body of objectstack-ai/objectui#12822, the dev's updated draft verbatim)"
}
Relayed by seat domain:ui#4 (session_01SJn1GEkrbbBQkhGC26wEDw) from the cloud session's final report (degraded channel: the session pushed and returned pr: null). The seat ruled the open questions A (chip floors, not shrinking text columns; the Actions verdict as built; the hover-only ⋮ left to objectui#12423), amended the claim (6225863712) for the types member, the shared custom/ files and the guide, and asked for one change: the package entry publishes only the three overflow names other packages read. The session pushed that as a676aad with an updated PR body. The seat opened #12822 from claude/issue-12708-list-chip-width-overflow at a676aad and set pr above; the title's scope names the three packages touched. The out-of-scope finding (the inbox's unpinned action column) is #12816, for triage.
the 1024/1280 screenshots and DOM readings stay on claude/issue-12708-evidence (0b585d4)
State change: the card closed as completed through Fixes #12708, and pm:dispatched is removed in this act. The cloud session session_01SPYcVSSndYVgRePzs2F7sD is archived. The ACCEPT of record is 6226217717, the TIER_DEFAULT contract review is 6226222152 (PASS), and the landing note is 6226551835. The finding the dev filed while measuring stays open as #12816.
Activity
objectstack-fleet commentedon Oct 16, 2026
Claim: PM loop round 3 (
priority:p2; filed by the epic PM session from the 2026-10-16 fourth review, on the maintainer's 「全部立卡,平台问题到 objectui…」)Session:
session_01SJn1GEkrbbBQkhGC26wEDwAccount:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12708-list-chip-width-overflowWorktree:
objectui-issue-12708(in the cloud session's own container)Domain:
domain:uiSeat:
domain:ui#4File surface:
packages/components/src/renderers/complex/data-table.tsx(column sizing and the horizontal-overflow affordance),packages/plugin-grid/src/ObjectGrid.tsx(when the Actions column is drawn),apps/console/src/pages/system/ApprovalsInboxPage.tsx(its own table's overflow), tests beside each, one.changeset/12708-*.md. Measured onmain0171c24: the card namesplugin-gridandviews/setup/, but the sizing lives incomponents' data table and the inbox lives inapps/console.packages/components/src/ui/(Shadcn-synced primitives) is out of bounds (stop on breach; explain in the report)Container & model:
L,mode:cloud,model: opus(cloud on the maintainer's stance 「可以的话,ui 后续开发尽量用云 session,并且要求截图给我看」; lane scope on 「你也可以接ui车道的卡」; batch 3 on 「当前任务处理完,后续并发降到3」, this session's chat, 2026-10-16)Clause-②: yes
Clause-② reason:
DataTableSchemaand its column type are exported from@object-ui/types, and sizing a chip column by its widest chip may need the column to say it draws chips (one optional member). «拿不准 ⇒ 按 yes». The seat'sTIER_DEFAULTreview comes before ready; if nothing published moves, the review says so.Responsibility: n/a — measured list layout defects at 1024 px
Thread-read: none
Serial constraints cleared: PR #12775 (objectui#12697, another seat) edits
plugin-grid'sImportWizard.tsxonly. This seat's objectui#12695 (the list toolbar) landed as0171c24. No open objectui PR touchesdata-table.tsx,ObjectGrid.tsxorApprovalsInboxPage.tsx(read at dispatch).objectstack-fleet commentedon Oct 16, 2026
Claim: PM loop round 3 · amended (file surface and Clause-② reason, as measured in the report) (
priority:p2; filed by the epic PM session from the 2026-10-16 fourth review, on the maintainer's 「全部立卡,平台问题到 objectui…」)Session:
session_01SJn1GEkrbbBQkhGC26wEDwAccount:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12708-list-chip-width-overflowWorktree:
objectui-issue-12708(in the cloud session's own container)Domain:
domain:uiSeat:
domain:ui#4File surface:
packages/components/src/renderers/complex/data-table.tsx(column sizing and the horizontal-overflow affordance),packages/components/src/custom/(the one shared overflow box and its hook, newuse-horizontal-overflow.tsandhorizontal-overflow.tsx, andcustom/index.ts),packages/plugin-grid/src/ObjectGrid.tsx(when the Actions column is drawn, and the chip flag),apps/console/src/pages/system/ApprovalsInboxPage.tsx(its own table's overflow),packages/types/src/data-display.tsandpackages/types/src/zod/data-display.zod.ts(the one optionalTableColumnmember, its zod mirror and its static-table tombstone),content/docs/components/complex/data-table.mdx(the guide), tests beside each (includingpackages/types/src/__tests__/static-table-narrow-surface.test.tsand the updatedRowActionMenu.emptyGuard.test.tsxcase), one.changeset/12708-*.md. Amended on the dev's reportd2b81345: the types files, the sharedcustom/files and the guide were not in the first surface.packages/components/src/ui/(Shadcn-synced primitives) stays out of boundsContainer & model:
L,mode:cloud,model: opus(cloud on the maintainer's stance 「可以的话,ui 后续开发尽量用云 session,并且要求截图给我看」; lane scope on 「你也可以接ui车道的卡」; batch 3 on 「当前任务处理完,后续并发降到3」, this session's chat, 2026-10-16)Clause-②: yes
Clause-② reason: two published faces move. (1)
TableColumnin@object-ui/typestakes one optional member,chips?: boolean, mirrored in zod and tombstoned on the statictablesurface, because the table cannot otherwise tell a chip column from text. (2)@object-ui/components' entry (export * from './custom') exports the one shared overflow box, because its consumers sit in other packages:HorizontalOverflowBoxand its props type (plugin-grid's grouped scroller, the console's Approvals Inbox) andPINNED_RIGHT_SEAM_SHADOW_CLASSES(plugin-grid's pinned Actions cells). Only those names are exported; helpers no other package reads stay module-internal. The seat'sTIER_DEFAULTreview comes before ready.Responsibility: n/a — measured list layout defects at 1024 px
Thread-read: 6223619921
Serial constraints cleared: PR #12775 (objectui#12697, another seat) edits
plugin-grid'sImportWizard.tsxonly. This seat's objectui#12695 (the list toolbar) landed as0171c24. No open objectui PR touchesdata-table.tsx,ObjectGrid.tsxorApprovalsInboxPage.tsx(read at dispatch).objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{ "issue": 12708, "status": "done", "branch": "claude/issue-12708-list-chip-width-overflow", "pr": 12822, "session": "session_01SPYcVSSndYVgRePzs2F7sD", "premise_still_valid": true, "summary": "Built all three targets on objectui main (merged up to 8cc02b4; head cc75474). (1) Chip columns: data-table floors any column flagged with the new optional TableColumn member `chips` at the widest chip drawn on screen. The floor is measured once per data load (a new data prop, page or page size) and sits on top of a declared width or a user's resize; text columns keep their width and truncate. ObjectGrid sets the flag at its single column fold for select, status, multiselect, radio, checkboxes and tags. (2) Overflow: there is now one shared piece in packages/components/src/custom: `useHorizontalOverflow` and `HorizontalOverflowBox` in horizontal-overflow.tsx, plus use-horizontal-overflow.ts. It draws a right-edge shadow while the box can scroll further right, and shows the scrollbar on hover or focus. data-table's own scroll box uses it to decide when to show its shadow; it keeps its existing #12463 overlay to place it. The grouped grid's scroller, which had no shadow, now uses it, and the right-pinned Actions cells draw the shadow on their own left edge. The Approvals Inbox table, which also had none, now uses it. The Actions column stays pinned right. (3) ObjectGrid draws the Actions column only when at least one row's plan offers an inline button or a menu item. The plan is computed with planRowActionMenu, the same function and inputs RowActionMenu draws from. Premise check: the card's symptoms are real, but two causes differ from the card and the PM's assumptions. On KB the 'Publish…' is the pinned Actions column covering the Status chip at the scroll seam, not an ellipsis. The Actions column there is not empty: four ⋮ triggers are present, hidden until row hover (see findings).", "findings": [ "KB (Knowledge Base, grouped by Category) at 1024 px, measured on 0171c24: the Status column is 130 px. That is HotCRM's own view width (`{ field: 'status', width: 130 }` in src/service/views/knowledge_article.view.ts), an explicit width, so data-table never auto-sizes it. The 'Published' chip draws about 70 px, so it fits and has no ellipsis (each chip element has scrollWidth equal to clientWidth). It reads 'Publish…' because the grouped table is 1477 px wide inside ObjectGrid's grouped scroller, which is 768 px wide. The sticky right-pinned Actions header and cells (67 px, left edge at x=957) cover the Status cell, which spans up to x=1026. Nothing said the table continues. data-table's #12463 shadow is skipped for grouped tables (disableInnerScroll), and the grouped scroller drew none.", "KB Actions header: it is not empty. All 4 rows render a ⋮ trigger (data-testid row-action-trigger) with Edit and Delete. Each trigger stays at opacity 0 until its row is hovered or focused (objectui#12423), so at rest the column looks empty.", "Approvals Inbox at 1024 px, with 3 pending account approvals created on the local backend: the table is 1071 px inside a 718 px box (the shadcn Table wrapper, `overflow-auto`). The Record column takes 470 px despite `w-[30%]`, because the long record titles are nowrap. Requester, Status, Submitted and the inline Approve/Reject column run past the card's right edge. On current main the dates and 'Submitted' are not cut to 'Submitt…'; they sit past the edge with no shadow or other cue.", "PM assumption 1 is partly false: auto-sized columns are already sized from the drawn content in px (#11682), not from a character estimate. The KB Status column has an explicit width, so neither path sizes it.", "PM assumption 2 is true as a reading of the code (the column was drawn whenever operations.update or operations.delete was set, or any row-action list was non-empty). It is not the cause on KB, whose rows do offer actions.", "PM assumption 3 is confirmed: the inbox uses its own Table with w-[22%], w-[30%], w-[16%], w-[110px] and w-[132px], and has no overflow cue.", "PM assumption 4 is false: data-table already draws a right-edge shadow for its own scroller (objectui#12463). The shadow was missing only for the grouped table, which scrolls in ObjectGrid, and for the inbox." ], "tests": "All runs are on head cc75474 unless noted. New pins: packages/components/src/renderers/complex/__tests__/data-table-chip-floor-12708.test.tsx (5 tests): a chip column's min width equals its widest chip after a data load (114px for 'Published' under the stub); a text column keeps its declared 80px while the chip column beside it rises to its floor; a wider declared width is kept; the floor is measured once per data load (a re-draw within the same load keeps it, a new load measures again); CONTROL: an unflagged column is unchanged. packages/components/src/__tests__/horizontal-overflow-12708.test.tsx (5 tests): the shadow and data-overflow-right appear when scrollWidth > clientWidth and go away at the right end; no overflow, no shadow; seam=pinned draws no edge overlay; hover-scrollbar classes; one pixel of slack. packages/plugin-grid/src/__tests__/actionsColumnAndChips-12708.test.tsx (7 tests): no Actions column when every row is refused every action; the column is drawn as soon as one row offers an action; no column when nothing is wired; CONTROL: a list with row actions keeps its pinned Actions column (sticky right-0 on the header and every cell); a legacy row action alone draws the column; a select column is measured under an explicit width and a text column is not; the grouped scroller is the overflow box and the pinned Actions cells carry the seam shadow, with no edge overlay when overflowing. apps/console/src/pages/system/ApprovalsInboxPage.tableOverflow-12708.test.tsx (2 tests): the inbox table sits inside the overflow box, and the shadow appears while overflowing and goes away at the right end. Updated pin: RowActionMenu.emptyGuard.test.tsx, case 'every row suppressed', now asserts no Actions column (it pinned the empty header that Target 3 removes). Ablations, run through objectstack scripts/ablation-replace.mjs with an anchor that must hit, restored blob checked against HEAD and git diff HEAD empty after every leg (vitest aliases @object-ui/* to src, so no dist rebuild applies): A1 floor removed: 3 failed of 5. A2 once-per-load guard removed: first run stayed GREEN (5 passed), so that pin was vacuous; the test was rewritten to rename a measured header, and the re-run gave 1 failed of 5. A3 hasOverflowRight returns false: 5 failed of 11 (overflow box, inbox and #12463 suites). A4 Actions verdict reverted to wiring only: 2 failed of 41. A5 chips stamp set to false: 1 failed of 7. A6 grouped box seam forced to edge: first run stayed GREEN (7 passed) because it had no overflow geometry, so that pin was vacuous; geometry was added, and the re-run gave 1 failed of 7. Final `git diff HEAD` is empty.", "gates": "Each exit code below was captured before any pipe, from the os-verify-lock VERDICT line or an echoed $?. Results: `pnpm --filter @object-ui/plugin-grid test` (full, once, foreground) exit 0: Test Files 223 passed (223), Tests 2049 passed (2049). `pnpm --filter @object-ui/app-shell test` (full, once; run detached and waited on in the foreground with `tail --pid` because the container caps foreground commands at about 10 min) APP_SHELL_EXIT=0: Test Files 1495 passed, 1 skipped (1496), Tests 14039 passed, 9 skipped. An earlier app-shell run was stopped by its PID before it reported, because the code changed under it; the run above is the only complete one. Components, types and the console Approvals Inbox suites: `pnpm exec vitest run --maxWorkers=2 packages/components/ packages/types/src/__tests__/ apps/console/src/pages/system/` exit 0: Test Files 814 passed, 1 skipped, Tests 14694 passed, 24 skipped. type-check for @object-ui/types, @object-ui/components and @object-ui/plugin-grid: Done for each. `pnpm --filter '@object-ui/console^...' build && pnpm --filter @object-ui/console type-check && pnpm --filter @object-ui/console build` exit 0. A first console type-check failed only with TS2307 on dependency dist/ files that were not built yet; it went green after the dependency build. `eslint` on the touched .ts/.tsx files exit 0 (0 errors; warnings are pre-existing `no-explicit-any` and two `any` in the new test helper). With `--no-inline-config` it reports 3 errors, all pre-existing `react-hooks/static-components` lines that already carry inline disables (ApprovalsInboxPage, data-table and ObjectGrid lines this diff does not touch). `pnpm check:i18n-keys` exit 0, `pnpm check:i18n-drift` exit 0, `pnpm check:new-line-citations` exit 0 (0 new), `node scripts/check-changeset-presence.mjs` exit 0, `node scripts/check-changeset-no-major.mjs` exit 0. No i18n keys were added or retired, so designer-parity and dead-keys were not run. CI check-runs: NOT MEASURED, because no PR is open; the PM opens it.", "mcp_calls": "2 — mcp__github__issue_read (get; get_comments) on objectui#12708. No GitHub write tool was called. Also 2 calls to mcp__claude-code-remote__add_repo, access read, for objectstack and hotcrm: both returned read_available and attached nothing.", "api_writes": "0 REST writes. git push only: the feature branch claude/issue-12708-list-chip-width-overflow (empty probe, then 7 pushes, head cc754746e3f0) and the evidence branch claude/issue-12708-evidence (1 push, 0b585d4fe3ae). No PR, no comment, no label, no assignee write.", "deviations": [ "Published member: `chips?: boolean` on TableColumn in packages/types/src/data-display.ts, with its zod mirror (`z.boolean()`), a static-table tombstone in the StaticTableColumn interface and in StaticTableColumnSchema, and the static-table-narrow-surface.test.ts key lists. The table cannot otherwise tell a chip column from text: TableColumnType has no select member and the `cell` renderer is opaque. Clause-② covers exactly this one member. These files are outside the claim's surface.", "New files outside the claimed surface, as the ruling asks (outside packages/components/src/ui/): packages/components/src/custom/use-horizontal-overflow.ts (hook, hasOverflowRight, class constants) and horizontal-overflow.tsx (HorizontalOverflowBox), exported from custom/index.ts.", "Overflow placement has two forms on one shared detection. data-table's own scroller keeps its pre-existing #12463 overlay, placed by measured right and bottom offsets: that is a pre-existing inline `style`, now gated by the shared hook. The grouped grid uses the pinned-cell seam classes, and the inbox uses the box's edge overlay. New code has no inline style.", "HOVER_SCROLLBAR_CLASSES uses Tailwind's important modifier (`!`). The console themes every scrollbar with an unlayered `* { scrollbar-color }` rule (apps/console/src/index.css), which beats any layered utility; this was measured live.", "An existing pin was updated: packages/plugin-grid/src/components/__tests__/RowActionMenu.emptyGuard.test.tsx, case 'every row suppressed', asserted the empty Actions header that Target 3 removes.", "Docs: content/docs/components/complex/data-table.mdx gains 'Chip columns' and 'Wide tables' sections (AGENTS.md #2).", "Contract conflict, reported rather than settled silently: os-dev.md says the dev opens the draft PR and posts the report as an issue comment. This dispatch's degraded channel forbids both, because the fleet-write relay gets 403 from this container. I followed the dispatch: pr null, body under pr_body_draft.", "Commit hygiene: the merge commit from origin/main was amended locally, before its first push, to carry the model-free trailer pair; no pushed history was rewritten. The first commit's body contains a plain 'Refs objectui#12708' paragraph. It is not a trailer and not a closing keyword.", "Worktree cleanup: the evidence worktree is removed. The feature worktree ../objectui-issue-12708 is kept because the PR is not open yet (contract: remove after the PR opens). The backend (:4127) and console (:5297) I started were stopped by PID tree, and both ports read free." ], "files_changed": [ ".changeset/12708-list-chip-width-overflow.md", "apps/console/src/pages/system/ApprovalsInboxPage.tsx", "apps/console/src/pages/system/ApprovalsInboxPage.tableOverflow-12708.test.tsx", "content/docs/components/complex/data-table.mdx", "packages/components/src/__tests__/horizontal-overflow-12708.test.tsx", "packages/components/src/custom/horizontal-overflow.tsx", "packages/components/src/custom/use-horizontal-overflow.ts", "packages/components/src/custom/index.ts", "packages/components/src/renderers/complex/data-table.tsx", "packages/components/src/renderers/complex/__tests__/data-table-chip-floor-12708.test.tsx", "packages/plugin-grid/src/ObjectGrid.tsx", "packages/plugin-grid/src/__tests__/actionsColumnAndChips-12708.test.tsx", "packages/plugin-grid/src/components/__tests__/RowActionMenu.emptyGuard.test.tsx", "packages/types/src/data-display.ts", "packages/types/src/zod/data-display.zod.ts", "packages/types/src/__tests__/static-table-narrow-surface.test.ts" ], "screenshots": [ "https://github.com/objectstack-ai/objectui/blob/0b585d4fe3ae5ed2d9ad313da291869c74563c99/docs/design/findings/12708/kb-grouped-1024-light.png?raw=true — Status chips Published, Published, Published, Draft (none ellipsized); Actions column drawn, 4 triggers; grouped scroller scrollWidth 1477, clientWidth 768; seam shadow on", "https://github.com/objectstack-ai/objectui/blob/0b585d4fe3ae5ed2d9ad313da291869c74563c99/docs/design/findings/12708/kb-grouped-1024-dark.png?raw=true — same readings, dark theme", "https://github.com/objectstack-ai/objectui/blob/0b585d4fe3ae5ed2d9ad313da291869c74563c99/docs/design/findings/12708/kb-grouped-1280-light.png?raw=true — same chips; Actions drawn, 4 triggers; scrollWidth 1477, clientWidth 1024", "https://github.com/objectstack-ai/objectui/blob/0b585d4fe3ae5ed2d9ad313da291869c74563c99/docs/design/findings/12708/approvals-inbox-1024-light.png?raw=true — Status chips Pending ×3 (none ellipsized); the inbox's own Approve/Reject column is present; scrollWidth 1071, clientWidth 718; edge shadow on; scrollbar shown on hover", "https://github.com/objectstack-ai/objectui/blob/0b585d4fe3ae5ed2d9ad313da291869c74563c99/docs/design/findings/12708/approvals-inbox-1024-dark.png?raw=true — same readings, dark theme", "https://github.com/objectstack-ai/objectui/blob/0b585d4fe3ae5ed2d9ad313da291869c74563c99/docs/design/findings/12708/dom-readings.json?raw=true — the DOM readings above, as captured" ], "line_budget": "n/a", "open_questions": [ { "question": "Target 1's 'where the grid must shrink, text columns give way before chip columns'. data-table never shrinks a column below its declared or estimated width (each cell's style sets min-width equal to width), so a too-wide table scrolls. I implemented this as precedence of floors: a chip column never goes below its widest chip, and a text column keeps its width and truncates. At 1024 the KB table still scrolls (1477 vs 768), and the new seam shadow now shows it.", "options": [ "A. Keep the floor-precedence reading (as built): no column is shrunk to fit; the shadow signals scrolling.", "B. Let text columns shrink below their declared width, toward a floor, until the table fits the panel, before it scrolls. This changes every list's layout and many min-width pins." ], "recommendation": "A. Business need: the measured KB defect is the covered chip plus no scroll cue, and both are now closed. Long-term: B rewrites the sizing model of every list. Guarding against AI mistakes: neutral. Startup focus: B widens scope with no measured need behind it." }, { "question": "Actions-column verdict reading (the smallest correct reading, as the ruling asks).", "options": [ "As built: computed in ObjectGrid's render over every row the grid holds (all groups when grouped), using planRowActionMenu with the same inputs RowActionMenu uses: capability-gated defs, per-record visibility predicates, canEdit/canDelete ANDed with rowOperations and the record-level verdicts (now one shared `rowCrudVerdicts` helper). It stops at the first row that offers an action. It is not memoised, because no hook may sit below the early returns. A disabled item counts as offered, as the ⋮ guard already counts it. Record-level verdicts arrive asynchronously, so the column can appear or vanish once they settle." ], "recommendation": "Accept as built." }, { "question": "On KB the ⋮ triggers stay at opacity 0 until hover (objectui#12423), so the Actions column looks empty at rest even when rows offer Edit and Delete. Not changed here: that hover rule is objectui#12423's, outside this card.", "options": [ "A. Leave it", "B. A separate card if the maintainer wants a resting cue" ], "recommendation": "A, unless the maintainer asks for B." } ], "out_of_scope_findings": [ "class: a · reach: Setup › Approvals Inbox at 1024 px (HotCRM, 3 pending requests). The table's scrollWidth is 1071 against a clientWidth of 718, and the inline Approve/Reject column is unpinned and sits past the card's right edge until the user scrolls. The Record column (w-[30%], nowrap titles) takes 470 px. This PR adds the overflow cue but does not pin the inbox's actions column or let Record truncate · evidence: approvals-inbox-1024-light.png and dom-readings.json on the evidence branch · dedupe words: approvals inbox, actions column, pinned, record column width, 1024" ], "pr_title_draft": "fix(grid): chip columns never ellipsize, wide tables show they scroll, Actions only when a row offers one", "pr_body_draft": "(elided here: it is the body of objectstack-ai/objectui#12822, the dev's updated draft verbatim)" }Relayed by seat
domain:ui#4(session_01SJn1GEkrbbBQkhGC26wEDw) from the cloud session's final report (degraded channel: the session pushed and returnedpr: null). The seat ruled the open questions A (chip floors, not shrinking text columns; the Actions verdict as built; the hover-only ⋮ left to objectui#12423), amended the claim (6225863712) for the types member, the sharedcustom/files and the guide, and asked for one change: the package entry publishes only the three overflow names other packages read. The session pushed that asa676aadwith an updated PR body. The seat opened #12822 fromclaude/issue-12708-list-chip-width-overflowata676aadand setprabove; the title's scope names the three packages touched. The out-of-scope finding (the inbox's unpinned action column) is #12816, for triage.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
Landed · PR #12822 merged as
4839f5conmain· read 2026-10-16T23:41Zorigin/main4839f5cTableColumn.chipsinpackages/types/src/data-display.tschips?: neveron the static table surface (:909)chipsinpackages/types/src/zod/data-display.zod.tspackages/components/src/custom/index.tsHorizontalOverflowBox,HorizontalOverflowBoxProps(:30),PINNED_RIGHT_SEAM_SHADOW_CLASSES(:31)a676aad; the hook and the other class constants stay package-internal*-12708test fileshorizontal-overflow5,data-table-chip-floor5,actionsColumnAndChips7,ApprovalsInboxPage.tableOverflow2RowActionMenu.emptyGuard"every row suppressed" now asserts no Actions columncontent/docs/components/complex/data-table.mdxsnippet importimport { HorizontalOverflowBox, Table } from '@object-ui/components';(:219)b1e838c, CI-red note6226332310).changeset/12708-list-chip-width-overflow.mdclaude/issue-12708-evidence(0b585d4)State change: the card closed as completed through
Fixes #12708, andpm:dispatchedis removed in this act. The cloud sessionsession_01SPYcVSSndYVgRePzs2F7sDis archived. The ACCEPT of record is6226217717, the TIER_DEFAULT contract review is6226222152(PASS), and the landing note is6226551835. The finding the dev filed while measuring stays open as #12816.Generated by Claude Code
fix(components,plugin-grid,types): chip columns never ellipsize, wide…