Repository navigation
feat(plugin-detail)!: the record page's related panels lose collapse; the header opens the whole list and empty panels fold into one line - #27920
Conversation
…-only menu, empty-panel line WIP: panel header anatomy, the arrangement without collapsed, the empty panel line and the compact row's first column. Tests follow. Claude-Session: https://claude.ai/code/session_01Utp6CaA9ZdTcj97mzGtHYt Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CaA9ZdTcj97mzGtHYt Co-authored-by: Claude <noreply@anthropic.com>
…ition Claude-Session: https://claude.ai/code/session_01Utp6CaA9ZdTcj97mzGtHYt Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CaA9ZdTcj97mzGtHYt Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Utp6CaA9ZdTcj97mzGtHYt Co-authored-by: Claude <noreply@anthropic.com>
|
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC Contract-review-claim: session_01Utp6CY2n1JmZZKrHgtp9jC · head b8c4395 · 2026-10-30T03:59Z The face is Generated by Claude Code |
… the row, not a clickable div Claude-Session: https://claude.ai/code/session_01Utp6CaA9ZdTcj97mzGtHYt Co-authored-by: Claude <noreply@anthropic.com>
|
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC Contract reviewServed-tier: Read: card #27524 (body, all six comments incl. the claim 6513005479, the split plan 6513066595 and the dev report 6514580814), PR #27920 body and file list (29 files), the net diff against Check-runs on ① Derived judgmentsPublic surface of
Stored preference
Accept-set changes inside the tree, each judged:
② Semver level
③ Boundary flagsDev report 6514580814 —
Standing question on the card not yet answered by the epic seat: comment 6513066595 asks whether Acceptance 3 (preview verification against the boards) applies to each of the four PRs or to the end state after ④. The seat said it holds each PR for that answer. Escalated: this record does not decide it; a PASS here would still wait on that answer before landing. Out-of-scope observation (dev): ④ Design judgmentScope files, each against the code it cites and Target items 1, 2, 10, 11, 12 (3–9 are later PRs; the interim face "header click opens the existing
Tests updated, not deleted ( i18n packs. Shapes per ④ ①; the zh strings match the card's quoted text (「查看全部 N 条」, 「还有 N 个空关联」); Findings: must-fix 1 (④-1); should-fix 3 (②-1, ④-2, ④-3). Implemented-by: VERDICT: FAIL Generated by Claude Code |
…s through to the declared fields, the Related tab list does not collapse Claude-Session: https://claude.ai/code/session_01Utp6CaA9ZdTcj97mzGtHYt Co-authored-by: Claude <noreply@anthropic.com>
|
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC Contract-review-claim: session_01Utp6CY2n1JmZZKrHgtp9jC · head 8b20d10 · 2026-10-30T04:28Z Delta review of the findings commits (1d1d2d6, 8b20d10) after record 6514756946 (FAIL on Generated by Claude Code |
|
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC Contract reviewServed-tier: Delta review after record 6514756946 (FAIL on Check-runs on ① Derived judgmentsEach finding of record 6514756946, checked in the tree (not the body):
The census fix (
No accept-set or public-surface change beyond those four: the delta touches no export, no stored shape, no i18n pack. ② Semver levelUnchanged from record 6514756946 on everything but the marker: ③ Boundary flagsNo new dev flag arrived with the delta. Carried from record 6514756946 and still standing, not decided here: Acceptance 2's HotCRM evidence branch (shots are on app-crm in project files — seat / epic seat), and the epic seat's unanswered cadence question on comment 6513066595. The PR body's ④ Design judgment
Tests. The four new / changed cases each pin the fix they name against the real components (the header through Findings: must-fix 0; should-fix 0. Implemented-by: VERDICT: PASS Generated by Claude Code |
…ens the list
The console scales a pressed button (`button:active { transform: scale(0.96) }`).
A transformed button is the containing block of its own absolute ::after, so
mid-press the stretched hit area shrank back to the title and chip, the release
landed on the header row, and the click went nowhere. The open button now
carries `transform: none`, so the row stays the target for the whole press.
Found by the proxy preview review on 8b20d10.
Claude-Session: https://claude.ai/code/session_01Utp6CaA9ZdTcj97mzGtHYt
Co-authored-by: Claude <noreply@anthropic.com>
|
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC Preview review (proxy for epic:#26676#1) · 8b20d10 · REWORK Performed per the #26676 ruling B (6520722066) by a context-free review agent with no development context. Its inputs were #27524's Target and Acceptance, the v4 boards ( Reason, Target 1 (blocking). A press on the header row's free space opens nothing. That is everything right of › and left of ⠿, about 40 % of the row at 1440, and the URL stays the same. The row is a pointer target with › on hover, so a reader expects the list. The clause broken is 「Clicking the header row (or the chip) switches to that panel's tab」 (interim here: the
Passed.
Screenshots, in project files (not a repository branch):
The fix follows as a new head on this PR, and the review is run again on it. Generated by Claude Code |
|
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC Contract-review-claim: session_01Utp6CY2n1JmZZKrHgtp9jC · head e6436dd · 2026-10-30T11:10Z Delta review of Generated by Claude Code |
|
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC Contract reviewServed-tier: Delta review after record 6515042489 (PASS on Check-runs on ① Derived judgmentsThe one finding of preview review 6521198256 (Target 1, blocking), checked in the tree (not the body):
No accept-set or public-surface change: the delta touches no export, no stored shape, no i18n pack, no changeset ( ② Semver levelUnchanged from record 6515042489: ③ Boundary flagsNo new dev flag arrived with the delta. Carried from record 6515042489 and still standing, not decided here: Acceptance 2's HotCRM evidence branch (shots are on app-crm in project files — seat / epic seat), and the epic seat's unanswered cadence question on comment 6513066595. New on this head: the preview review 6521198256 says it is run again on the fix; that re-run is the only check that exercises ④ Design judgment
Tests. The new assertion sits in the case that pins the row as one control, where it belongs; it reads the DOM property, not the class string, so a later move of the override to a stylesheet would red it and force the author to re-decide the layer question above, which is the right pressure. Holds. Findings: must-fix 0; should-fix 1.
Implemented-by: VERDICT: PASS Generated by Claude Code |
|
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC Preview review (proxy for epic:#26676#1) · e6436dd · PASS The same context-free review agent ran this round after REWORK 6521198256 (on Reason.
No deviation in scope. Not judged, as they belong to later PRs: the Screenshots, in project files (not a repository branch):
Generated by Claude Code |
|
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC 席位核对ACCEPT · head
Generated by Claude Code |
domain:ui#2 · os-bill · session_01Utp6CY2n1JmZZKrHgtp9jC
Requested by Bill · project thread
Part of #27524
Clause-②: yes (narrowing)
Metadata-protocol: none
Skills-docs: none — no file under
skills/**orcontent/docs/**teaches the related card's collapse, its⋯items or the stored arrangement (agit grepfor each names nothing); what an author writes is unchanged.维护者速读
collapsed(版本升到 2,旧记录读作「没选过」)。行内首列跳过指回本记录的外键。@object-ui/plugin-detail、十个语言包和 app-shell 的偏好编码;已拖过顺序的读者会回到默认顺序一次(无兼容读取,按裁定)。回滚即 revert 本 PR。ui/packages/plugin-detail/src/RelatedList.tsx(紧凑面表头与首列);RelatedListEmptyFold.tsx(面板行的折叠规则);synth/relatedArrangement.ts(去掉 collapsed)。pnpm --dir ui exec vitest run packages/plugin-detail/src/__tests__/RelatedPanel.header-27524.test.tsx packages/plugin-detail/src/renderers/__tests__/record-related-list.emptyPanels-27524.test.tsx,预期 2 个文件、14 个测试全绿。What changes
The record page's related column becomes a stack of same-shaped panels (record page v4, canvas 60). This is PR ① of four serial PRs for #27524, approved by the seat as a split; each leaves
maincoherent on its own.?related=face; PR ③ makes it the panel's tab)main(RelatedCardStack-27055pins it); unchanged herecollapsedmain; this PR adds the pinhighlights-27524.test.ts1. The panel header (
RelatedListcompact face,RelatedCardMenu.tsx). Title, then the count chip, inside one<button data-related-panel-open>whosetitlereads 「查看全部 N 条」 (detail.relatedPanelViewAll); › shows at the title's end on hover or focus. The header row itself is the target too: the button's::afterstretches over the positioned header, so a click on its free space opens the list while the row stays one control. The controls sit above it (relative z-10). On hover or focus-within the right end shows ⠿ (RelatedPanelHandle: the drag source on desktop; focused, ⌥↑ / ⌥↓ move the panel and keep focus on it), + and ⋯, which holds 隐藏 only. No collapse, no ⤢, nothing reserved on the left. On a touch screen (hover: none) the controls stay drawn. The full face of an arranged list (the Related tab of an authored page) draws the same ⠿ and ⋯ and no longer collapses from its header.The header has no click handler of its own, so a click inside the ⋯ menu's portaled content cannot open the list, even though it bubbles through React. The test
⋯ holds 隐藏 and nothing elsepins this. The first version, a clickable headerdivguarded byevent.currentTarget.contains(event.target), failedclickableNonControlCensus-13461in CI onb8c4395be2.1d1d2d6e6freplaces it with the stretched button. The census is red onb8c4395be2and green after the fix, locally.2. Empty panels (
RelatedListEmptyFold.tsx). In the panel column (RelatedListCompactContext) a stack of two or more folds its 0-row panels into one line after the others: 「还有 N 个空关联 · 名字 · … · 展开」 (detail.relatedEmptyFold, plural per pack; 展开 reusesdetail.relatedCardExpand). Unlike the Related tab's line (#25895, unchanged), the panel line folds a panel with anaddpicker too, folds a column whose panels are all empty, and carries no New: 展开 draws the panels again in their places, each with its own +. Non-empty panels keep their order. 展开 holds for the record it was pressed on (Stack.expandedFor,object:idfrom the record context). A view that steps to another record in place, such as the pane's ↓ ↑, keeps its column element, and the next record folds again.10. The stored arrangement (
synth/relatedArrangement.ts,useRelatedArrangement.ts, app-shellworkspacePreference.ts).record.related.arrangementis{ v, order, hidden, basedOn, seen }.collapsed, thecollapseedit and thedefaultCollapsedargument are removed; the codec's version isRELATED_ARRANGEMENT_RECORD_VERSION(2), so a version-1 record reads as no choice (no compatibility read, per the ruling).CollapsedRelatedCardand the stack's collapsed branch go.11. The compact row's first column (
RelatedList). When the child's name field is the FK pointing back at this record, the row is named by the first column that is not that FK, not redacted, not a platform column and readable. The authored columns are read first, then the declared fields. The FK is skipped even when it is the only authored column. So a product's line items name the opportunity.i18n.
detail.relatedCardMoveUp,detail.relatedCardMoveDown,detail.relatedCardCollapseanddetail.relatedCardCountleave all ten packs;detail.relatedPanelViewAll,detail.relatedPanelReorder,detail.relatedPanelReorderHintanddetail.relatedEmptyFoldarrive in all ten, with each pack's plural set.Declared.
Clause-②: yes (narrowing):RelatedArrangement,ResolvedRelatedArrangementandRelatedArrangementEditare exported from the plugin-detail entry and lose members.@object-ui/i18n's exportedTranslationKeys(typeof en) also loses the four removed keys. At()call naming one still compiles, because its key parameter is astring. The changeset's BREAKING banner names both packages. Its ADR-0087 marker,not-required (runtime-interface-only …), names all four symbols, and the gate verifies it. The changeset also carries the one-line fix.RELATED_ARRANGEMENT_RECORD_VERSIONis a new export.Source re-measured (main
273137d227→ this branch's base). S1, S3 and S4 hold. S2 drifted, description only: the page's crumb row carries ‹ back and 「收为抽屉」, with no ↑ ↓. The card namessynth/recordTimelineStream.ts; the file issrc/recordTimelineStream.ts, and the promotion logic it means lives insynth/recordPageColumns.ts(PR ② retires it there). Neither drift changes what a Target item asks.File surface. One file outside the card's list:
ui/packages/app-shell/src/utils/workspacePreference.ts, the codec ofrecord.related.arrangement(item 10 cannot land without its version).RelatedList.tsx,RelatedCardMenu.tsx,relatedListCompact.ts,renderers/record-related-list.tsxandindex.tsxare the files the header and fold are drawn in, beside the listed ones.Tests
New:
__tests__/RelatedPanel.header-27524.test.tsx(10): the realRelatedListcompact face with the stack's chrome. Header anatomy (title, chip, no toggle, no ⤢, 「View all 2」); row, title and chip each open the list; ⠿ + ⋯ at the right end, hover/focus classes, their clicks do not open the list; ⋯ holds Hide only and Hide does not open the list; ⌥↑ / ⌥↓ on the handle, a bare arrow does nothing; the handle is the drag source, the header row is not; the first column on the product page (Acme renewal,Globex pilot), also when the FK is the only authored column, and on the opportunity page (Widget); the arranged full face (the Related tab) draws ⠿ and ⋯, no chevron, and a header click leaves its rows drawn.renderers/__tests__/record-related-list.emptyPanels-27524.test.tsx(4): the line's text and order; 展开 restores the panels in place; stepping to a sibling record in the same view (same column element) folds again; an all-empty column folds.synth/__tests__/highlights-27524.test.ts(3): lead and contact sets minusfull_name, the cut at six.app-shell/src/utils/workspacePreference.relatedArrangement-27524.test.ts(2): the v2 shape round-trips; a v1 record carryingcollapsedreads as no choice.Updated, not deleted:
RelatedCardStack-27055(the collapse case becomes "a panel has no collapse"; moves via the handle),relatedArrangement-27055(collapse cases become order + hidden),RelatedList.compactColumn-26678(the new header anatomy).Runs and ablation
Pre-merge head
96652743f2, under the verify lock:pnpm --dir ui exec vitest run packages/plugin-detail packages/app-shell/src/utils packages/i18n→Test Files 479 passed | 2 skipped (481),Tests 5209 passed | 39 skipped (5248).Merged head
fbf43d1dfa(main04740b7c9fmerged once, no conflict;pnpm install --frozen-lockfile && pnpm build→ 115/115 tasks): the same command →Tests 1 failed | 5241 passed | 39 skipped (5281). The one red was⋯ holds 隐藏 and nothing elsereading aRelatedList.tsxtransformed before the commit below landed mid-run (it asserts that commit's class). On the final headb8c4395be2:RelatedPanel.header-27524,record-related-list.emptyPanels-27524,RelatedList.compactColumn-26678,RelatedCardStack-27055→Test Files 4 passed (4),Tests 30 passed (30).type-check(tsc --noEmit+tsc -p tsconfig.test.json) for@object-ui/plugin-detail,@object-ui/i18n,@object-ui/app-shellon the merged head: 0 errors each, lock VERDICT command-exit 0.A bug the screenshots found, fixed in
b8c4395be2: with ⋯ open the controls faded out, because the pointer and the focus are both inside the portaled menu. The controls now stay drawn while a control holdsdata-state=open; the menu test pins it.Ablation (
scripts/ablation-replace.mjs, wrap mode, one8a0e9f9fd): inRelatedListEmptyFold.tsx,if (!panels && !ordered.some((m) => m.empty === false)) return NO_FOLD;→if (!ordered.some((m) => m.empty === false)) return NO_FOLD; /* ABLATED_27524 */(anchor 1 → 0, blobcfb2543146ea→e4a7af3d6923, marker count 1). Red:a column whose panels are all empty folds them too(Tests 1 failed | 2 passed (3)); the two cases with a non-empty panel stay green, as they should. Restored: blob equal to HEAD's,git diff HEADempty. The suite imports the renderer from source, so nodistis involved.Contract review 6514756946 (FAIL on
b8c4395be2), fixed in8b20d10d4c. Each pin was ablated (ablation-replace.mjs, wrap mode) and restored:stack?.expandedFor === recordKey→stack?.expandedFor != null. Result: the sibling case is red,Tests 1 failed | 3 passed (4).[...authored, ...declared]→(authored.length > 0 ? authored : declared). Result: the authored-only case is red,Tests 1 failed | 9 passed (10).arrangedFullput back intoisCollapsible. Result: the full-face case is red,Tests 1 failed | 9 passed (10).@object-ui/i18n, and the marker addsui/packages/i18n/src/locales/en.ts#TranslationKeys.check-adr-0087-registration --base origin/main: exit 0, four symbols verified.On
8b20d10d4c:pnpm --dir ui exec vitest run packages/plugin-detail packages/app-shell/src/views/RecordDetailView packages/components/src/__tests__/clickableNonControlCensus-13461.test.tsunder the lock →Test Files 425 passed | 1 skipped (426),Tests 3635 passed | 18 skipped (3653);@object-ui/plugin-detailtype-check VERDICT command-exit 0; eslint on the four touched files 0 errors.Proxy preview review 6521198256 (REWORK on
8b20d10d4c, Target 1: a press on the header row's free space opened nothing), fixed ine6436ddc12. The open button carriestransform: none, so the console's pressed-buttonscale(0.96)no longer makes it the containing block of its stretched::after. One6436ddc12, under the lock:RelatedPanel.header-27524,RelatedList.compactColumn-26678,RelatedCardStack-27055,clickableNonControlCensus-13461→Test Files 4 passed (4),Tests 33 passed (33);@object-ui/plugin-detailtype-check VERDICT command-exit 0; eslint on the two touched files 0 errors. Ablation (thestyle={{ transform: 'none' }}line replaced by/* ABLATED_27920 */withsed, marker count 1): the pin is red (expected '' to be 'none',Tests 1 failed | 9 passed (10)), and a live press on the free space (Playwright, 120 ms hold, an app-crm account at 1440) opens nothing. Restored withgit checkout:git diff HEADempty, and the same press opens?related=crm_activity.account.Acceptance notes
examples/app-crm, not HotCRM (deviation, as the order says); six shots in/mnt/project-files/ui-seat2/card-27524/shots/(project files, not a repository branch), zh, 1440 × 900, on the final head through the console's dev server:01-account-1440-panels(three panels with rows, then 「还有 1 个空关联 · 线索列表 · 展开」),02-panel-header-hover(›, ⠿ + ⋯),03-panel-menu-hide-only,04-empty-panels-expanded,05-header-click-opens-list(the interim?related=face),06-opportunity-1440(all three panels empty → one line). The tier, the 时间线 | 关联 control and the folded 动态 card in them aremain's and change in PR ② and ④. app-crm has no product object, so Target 11 is shown by its test only.Verification
Gates (34 derived, all exit 0) and the dogfood run
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, each run with its exit code captured before any pipe;--ran→34 derived famil(ies) accounted for. The run started on the merged headfbf43d1dfa;b8c4395be2(one class and one comment inRelatedList.tsx, one test assertion) landed while it ran, so the gates that read sources after it read the final tree, and the two that could see the change (check-comment-mask-corpus,check-adr-0087-registration --base origin/main) were run again onb8c4395be2: exit 0 both.Full dogfood once on the final head, because the title carries
!(pnpm --filter @objectstack/dogfood testunder the verify lock, afterpnpm build):b8c4395be2·VERDICT command-exit 1·Test Files 3 failed | 353 passed | 3 skipped (359)·Tests 4 failed | 2867 passed | 20 skipped (2891). All four aremain's, red at this branch's merge base04740b7c9fand repaired onmainafter it; this diff touches nothing they read (onlyui/**and the changeset):error-sentences-live.dogfood.test.ts›POST /data/OBJECTandPOST /data/OBJECT/createMany(expect a code-less hook refusal; the server answersHOOK_ABORTEDsince fix(rest): an undeclared hook abort answers the data door with HOOK_ABORTED #27667), andobject-hook-lifecycle-scratch.dogfood.test.ts› acceptance[6] (same cause).maincarries the repair: test(dogfood): flip the three hook-abort pins to the ruled HOOK_ABORTED wire #27878.platform-app-object-entry-views.test.ts›every object an entry names is declared…(sys_exchange_rate, since feat(platform-objects): sys_exchange_rate — an organization's dated exchange rates, admin-written and audited, with a Setup list under Localization #27246).maincarries the repair: test(dogfood): boot PlatformObjectsPlugin in the Setup/Account entry-view roster, so sys_exchange_rate resolves #27873.Body refreshed 2026-10-30T11:19Z: the runs line for
e6436ddc12(contract review 6521350798, should-fix).Generated by Claude Code