Repository navigation
console record page: the related column loads counts first, then the first three rows of only the visible cards, and never blocks the details — one batched request, viewport-lazy cards, a measured budget #27056
Description
epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff
Unblocked 2026-10-29T09:06Z — upstream #27055 closed (the record page v3 layout and its round 2 are on main at 273137d227; #27053's tabs retirement and #26679's drawer are there too). Back in the ui lane queue; re-read the card's Source against current main at dispatch — the v3 files it names moved in PR #27310 / #27473.
Part of the record-page-v3 epic #26676 (v3 child 3 of 8). Runs after #27055 (the compact cards it loads).
Source (origin/main 7bb8084590)
Every record:related_list node mounts RelatedList, which runs its own query on mount (pageSize default 10, the child's list columns); the column tier (#26758) mounts all of them at once, so a record with N related lists opens with N concurrent queries racing the record's own read (ruling 3). The folded rail in the two-column tier reads nothing until opened.
Target
- Counts first: on open, ONE request answers the count of every related list for this parent (one aggregate per child object's FK, batched through the data source's batch door if main has one, else the
aggregateAPI per list in onePromise.allbehind a single loading state — measure which exists). Card headers render from counts; a count of 0 folds the card to one line and skips its rows. - Rows on demand: a card fetches at most 3 rows (
limit) with a projection of the name field plus one status / date field (fromrelatedListColumnswhen declared, else the first status-like or date field), ordered by the inherited sort; only cards in the viewport fetch (IntersectionObserver),primarycards first; a folded 「动态」 card fetches nothing until opened. - Details first: the details column renders from the record read alone; the related column shows skeleton cards until counts land, then rows; the page's interactive moment does not wait on related data.
- Prefetch on step: in the drawer, ↑ ↓ (console record drawer v2: the bar is ↑ ↓ · ⤢ · ✕ (no object name, no counter); a related row or a lookup opens its record in place with a trail, ← / Esc go back one level; ⤢ carries the trail into the page; on the record page a related row opens a drawer over the page #26679) prefetches the next record's counts.
- Budget, measured in the PR body on HotCRM (fresh DB, an account with ≥ 8 related lists): details interactive ≤ 300 ms after the record read returns; all card headers ≤ 500 ms; total related requests on open ≤ 2 round trips. Numbers from the browser's network panel, pasted.
File surface
ui/packages/plugin-detail/src/RelatedList.tsx (lazy + limit + projection), relatedListCompact.ts, a related-counts hook beside it, ui/packages/app-shell/src/views/RecordDetailView.tsx (the counts request and skeleton gating), the data-source batch call if present (ui/packages/data-objectstack), tests beside, changesets; ⛔ packages/spec/** (no new key: limit is an existing node prop).
Acceptance
- Tests: a record with 6 related lists issues one counts call and rows only for the 2 cards in view; a 0-count card never queries rows; the details render before any related response resolves.
- The measured budget table in the PR body; screenshots not required.
Rulings (maintainer, chat in the filing session, 2026-10-28 — verbatim, not re-decidable here)
- Record-page model (2026-10-27): 「1. 一个对象一个默认打开方式:默认抽屉(带展开整页),
page只给显式声明的对象,modal退役,split / popover 变成用户个人偏好;抽屉和整页是同一张页在两个宽度。」 - On the resident timeline column: 「时间线是不是太突出了,这个只适合部分对象吧?适合做所有业务对象的默认布局吗」 and then 「时间线是否显示,是否可以切换,比如当前版本的 Claude code,顶部有很多切换图标」 — the column's presence is a per-user toggle with an object-derived default, never metadata.
- On the related column's cost: 「关联都放在右侧,记录打开时是不是会很慢,因为需要加载所有的关联子表。」 — the related column loads counts first, rows on demand, and never blocks the details.
- On mobile: 「还要考虑手机端的效果。」
- On the definition surfaces: the PM's keep / narrow / retire table was put to the maintainer (keep object roles, relationship flags, six slotted slots and
kind: full; retire thetabsslot and the deadBuildPageOptionshints; fold the quick-look path into the one record page) and approved with the instruction in the footer.
Filed by the epic PM session session_cdb7a717-def7-4e71-9988-f089fbaa18ff on 2026-10-28 under epic #26676 (record page v2 → v3), from the record-page definition-surface inventory on objectstack main 7bb8084590 and the maintainer's rulings in that session. The maintainer's instruction to file and dispatch, verbatim: 「同意,以上界面相关的需要我确认的你负责立卡派发,如果有可能可以启动云会话,开发完你来确认。」 Design evidence: the design canvas boards 「记录页 v3」 row (RecordPageV3, RecordPageV3NoTimeline, RecordPageRef, RecordDrawerV3, RecordMobile) — exported to the never-merged branch named in the epic parent once this batch is filed.
Activity
objectstack-fleet commentedon Oct 29, 2026
epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff
Unlock scan 2026-10-29T09:07Z: every
Blocked-by:upstream of this card is closed — #27055 by PR #27310 (merged by the maintainer) and PR #27473 (the seat landed it through the queue), #26679 by PR #27038, #27053 by PR #27219. Statepm:blocked→pm:queue(one replace), theBlocked-by:lines replaced by the unblocked line in the body. The card's file surface is re-read againstmain273137d227: the record page v3 layout (frameless sections, the merged timeline stream, compact related cards with the reader's arrangement, body tables, in-place enlarge, the segmented panel control in the header, the 1120 / 760 container thresholds) is what this card now builds on. Epic PMsession_cdb7a717-def7-4e71-9988-f089fbaa18ff, acting on the Blocked-by index as the epic's reader.Generated by Claude Code