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 }}
Repository navigation
Record page: opening a record sends one approvals query and one record fetch, not three and two #12701
Read on objectui main efa9cb4fa, the HotCRM lead record /apps/app.objectstack.hotcrm/crm_lead/record/lZxZJxDxLzE08lyU: the network log of one open holds
GET /api/v1/approvals/requests?object=crm_lead&recordId=… three times (identical), and
GET /api/v1/data/crm_lead?populate=organization_id,created_by,updated_by,owning_business_unit_id,owner_id,converted_account,…&top=1&filter=… and GET /api/v1/data/crm_lead?populate=converted_account,converted_contact,converted_opportunity,duplicate_of_lead,duplicate_of_contact&top=1&filter=… — the same record twice with two populate lists, the second a subset of the first.
Plus one sys_activity query and three approvals queries from the header, the activity tab and the approvals rail each asking on their own.
Target
One approvals query per record open, shared by every consumer on the page (a record-scoped query key, or a provider on the record page that the header, the rail and the Activity tab read).
One record fetch: the page computes the union of the populate lists its sections need and fetches once; a section that needs a wider populate than the page loaded triggers one refetch with the union, never a parallel second fetch.
A test that mounts the record page and counts requests by URL: approvals 1, record 1.
File surface
packages/app-shell/src/views/RecordDetailView.tsx and the hooks it mounts (the approvals rail hook, the record loader), packages/plugin-detail/src/DetailView.tsx if it fetches on its own, tests beside.
Acceptance
The request-count test; the network log of a lead open pasted in the PR shows one of each.
pnpm --filter @object-ui/app-shell test, pnpm --filter @object-ui/plugin-detail test green.
Evidence
No screenshot; the measurements above are the evidence.
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-12701-record-page-one-fetch
Worktree: objectui-issue-12701 (in the cloud session's own container)
Domain: domain:ui
Seat: domain:ui#4
File surface: packages/app-shell/src/views/RecordDetailView.tsx and the hooks it mounts (packages/app-shell/src/hooks/useRecordApprovals.ts, the record loader), the record page's approvals consumers (the header chip, RecordApprovalsPanel, the Activity tab) where they call the hook, packages/plugin-detail/src/DetailView.tsx if it fetches on its own, tests beside each, one .changeset/12701-*.md (stop on breach; explain in the report)
Container & model: M, 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: measured on maine6175b5: useRecordApprovals is not on @object-ui/app-shell's entry, but @object-ui/plugin-detail exports DetailView, and sharing one record fetch may give it a prop (a preloaded record or a populate union). «拿不准 ⇒ 按 yes». The seat's TIER_DEFAULT review comes before ready; if nothing published moves, the review says so.
Responsibility: n/a — a measured request-count defect on the record page
Thread-read: none
Serial constraints cleared: no open objectui PR touches RecordDetailView.tsx, useRecordApprovals.ts or plugin-detail/src/DetailView.tsx (PR #12685 adds only a test beside RecordDetailView). objectui#12673 (this seat) touches the list host and may share the detail bridge's record-path builder; this card does not touch the bridge.
{
"issue": 12701,
"status": "done",
"branch": "claude/issue-12701-record-page-one-fetch",
"pr": 12748,
"session": "session_01LbpjhSB18hFom4adrPRd4B; dispatched by PM seat domain:ui#4, session_01SJn1GEkrbbBQkhGC26wEDw",
"premise_still_valid": true,
"summary": "Opening a record now sends one approvals read and one record read. Measured live on a HotCRM lead and on a HotCRM opportunity that has a pending approval request. Approvals: useRecordApprovals moves its read and answer into a module-scoped store keyed by object + record (the same structural choice as sharedUserFeeds.ts, without the polling). Every consumer mounted for a record shares one read; a later mount, such as an authored record:approvals block or a tab opened after load, is served the stored answer; a read already in flight is joined. The entry is dropped when the last consumer unmounts, so re-opening the record reads again. refresh() always reads, and every consumer sees the result. RecordDetailView's invalidation effect now fires only when the nonce actually moves; it used to skip only its first run. Record: record:details passes the new DetailView prop recordFromHost, so the body waits for the page's record and never reads the row itself. PM assumption 1 is FALSIFIED as stated. Caller stacks show all three identical approvals reads came from the ONE useRecordApprovals in RecordDetailView. Two came from its mount effect, which React.StrictMode (on in apps/console main.tsx) replays in development. The third came from the page's skip-the-first-run invalidation effect, which the replay walked through. The fix still covers the real multi-consumer case (an authored page placing record:approvals mounts a second hook instance), and a test pins that case. Assumption 2 is confirmed: the second, narrower crm_lead read comes from record:details' DetailView reading while ctx.data is still null; this one is not a StrictMode effect. Assumption 3 is confirmed: hooks/index.ts exports useRecordApprovals and the app-shell entry's named list does not; the entry is unchanged. The card's assignee was not written. The shared checkout /home/user/objectui held the branch name with no commits of its own; I detached its HEAD at e6175b57f to free the name, and it is left detached.",
"tests": "All at HEAD 13a95b0 (origin/main merged in cleanly). (1) pnpm exec vitest run packages/app-shell/ --shard=K/6 for K=1..6, each under os-verify-lock: VERDICT command-exit 0 on every shard. Test Files 244 (1 skipped) + 245 + 245 + 245 + 246 + 246 = 1471 of the 1471 the sequencer reports. Tests 2291 + 2238 + 2294 + 2287 + 2125 + 2606 passed, 9 skipped. (2) pnpm --filter @object-ui/plugin-detail test: VERDICT command-exit 0; Test Files 276 passed, 1 skipped (277); Tests 2666 passed, 14 skipped. New pins: RecordDetailView.recordOpenRequests-12701.test.tsx (3 tests). It mounts the real page, the real ObjectStackAdapter and the real hook under React.StrictMode, on an authored page that also places record:approvals, with a held transport. It asserts approvals list reads 1, approvals single reads 1, record reads 1 (populate account, created_by, owner) and sys_activity reads 1. CONTROL: the record:approvals panel shows the step label, the lock band shows Locked for approval, Recall approval shows (the server viewer block reached the page's instance), and the body shows the record. A relation added to the definition triggers exactly one more record read, carrying the union. DetailView.recordFromHost-12701.test.tsx (3 tests): no findOne while the host record is pending, then the host record renders; a host naming no record renders byte-identical innerHTML with and without the prop; CONTROL: without the prop the view still reads once with expand account. Updated pin: RecordDetailView.recordOpenRequests-11699.test.tsx now expects the body's own GET /data/OBJ/ID at 0, where it used to expect 1. Ablations, one per pin, each run through objectstack's scripts/ablation-replace.mjs: the anchor had to hit, the blob changed on disk, and restore was verified as blob == HEAD with git diff HEAD empty. Sources are aliased to src in vitest.config.mts, so no dist rebuild was needed. A1, every mount reads (useRecordApprovals.ts): red, approvals list reads expected 1, got 4. A2, nonce guard removed (RecordDetailView.tsx): red, expected 1, got 3. A3, recordFromHost removed (record-details.tsx): the first attempt was a no-op that the tool refused (its replacement text already occurred in the file). On re-run, the 11699 pin went red but the 12701 count stayed green: the fixture's page and body populate lists were equal, so the adapter's in-flight findOne sharing absorbed the body's read. I widened the fixture with created_by (page-expanded, drawn by no section, the shape HotCRM has) in c085c47; re-run: red, record reads expected 1, got 2, and the union test went red too. A4, DetailView guard disabled: red, findOne called once. A5, the shared read publishes no rows: CONTROL red. A6, pageRecordExpand removed from the record effect's dependencies: union test red, expected 2 reads, got 1. A1, A2, A4, A5 and A6 ran on b2f4bc1; A3's counted run ran on c085c47.",
"gates": "Local, on HEAD 13a95b0. Exit codes were captured with redirect-then-capture; for locked runs I read the lock's VERDICT line. pnpm exec turbo run build --filter='@object-ui/app-shell...' --concurrency=2 → VERDICT 0, Tasks 29 successful of 29 (includes @object-ui/plugin-detail:build and @object-ui/app-shell:build; dist completeness passed with 1126 emitted files verified). pnpm --filter @object-ui/plugin-detail type-check, then pnpm --filter @object-ui/app-shell type-check → VERDICT 0; both script names echoed, and both scripts include their tsconfig.test.json leg. pnpm exec eslint --no-inline-config over the 7 touched source and test files → exit 0, 0 errors, 187 warnings. Warnings landing on lines this branch added: only one react-hooks/exhaustive-deps on DetailView's existing effect dependency list, which base's list also produces (it omits schema.data; I only added entries to that line). Two react-hooks warnings my first draft introduced (refs read during render, setState in an effect) are gone in 06a880b. pnpm check:i18n-keys → 0. pnpm check:i18n-drift → 0. pnpm check:new-line-citations → 0 (VERDICT: 0 new citations). node scripts/check-changeset-presence.mjs → 0 (7 source files in 2 released packages; 1 changeset declared). check:i18n-designer-parity and check:i18n-dead-keys were not run: no keys added or retired. objectui has no check-nul-bytes.mjs; a manual control-byte grep over the 8 changed files found none. CI check-runs: NOT MEASURED, no PR is open from this container.",
"mcp_calls": "2 GitHub MCP calls, both reads: issue_read get and issue_read get_comments on objectui#12701. Other MCP calls: claude-code-remote add_repo x2 (read: objectstack and hotcrm; both answered read_available, nothing attached). No GitHub MCP write tool was called.",
"api_writes": "0 REST writes. git push x5 to origin claude/issue-12701-record-page-one-fetch (the empty branch first, then b2baf16, b2f4bc1, c085c47 + 06a880b, 13a95b0). No PR was opened, no comment posted, no label written; per the dispatch's degraded channel, the PM opens the draft PR from pr_body_draft.",
"open_questions": [
{
"question": "Ruling: 'a section that needs a wider populate triggers one refetch with the union'. As built, the page already expands every declared relation the viewer may read. Any record:details section's need is the same object's relations, gated by the same field-level security, so it is always a subset. With the body no longer reading, the union equals the page's own list. The only refetch path left is the page's existing dependency on its expand list (objectui#11699), which reads once more, with the full list, when the definition gains a relation. I pinned that path as the union test. Is that the intended reading, or is a section-to-page registry wanted for populate needs (as HighlightFieldsProvider does for highlight fields)?",
"options": [
"A: keep as built: the page's list is the union by construction, and the one-refetch path is the existing dependency on the expand list, now pinned",
"B: add a registry through which sections declare populate needs to the page (new surface in @object-ui/react, outside this claim's file surface)"
],
"recommendation": "A. On real business need, no measured section asks for a relation the page does not already expand (HotCRM lead and opportunity measured). On long-term soundness, one owner of the record read keeps the contract the recordBinding module doc already states: the record blocks query nothing. On resistance to AI-authoring mistakes, B adds a second declaration surface that could drift. On startup focus, B is capability with zero demand."
},
{
"question": "Contract conflict, reported rather than silently resolved: os-dev.md tells the dev to open the draft PR and post the os-dev-report comment, but this dispatch's degraded channel forbids both, because the fleet-write relay is refused (403) from this container. The dispatch says the file wins where the two disagree; here the file's write paths are unreachable, so I followed the dispatch.",
"options": [
"A: PM opens the draft PR from pr_body_draft (assignee os-sales) and posts this report verbatim",
"B: a later session with a working relay opens it"
],
"recommendation": "A, as the dispatch planned."
}
],
"out_of_scope_findings": [
"carrier: none (承接者:无) · noted in Acceptance notes only · Dev-server artifact: after git merge origin/main under a running vite dev server, the HMR module graph served the old record-details module and the second record read reappeared. A fresh server (--force) read 1. Not a code defect.",
"carrier: none (承接者:无) · observation, not filed · In production builds (no StrictMode double-invoke) the approvals read was most likely already 1 per open on synthesized pages; NOT MEASURED (no production console build was run). The record double read does not depend on StrictMode.",
"carrier: none (承接者:无) · observation, not filed · On the opportunity page, the related-list preview and its count read each relation twice: crm_quote, crm_opportunity_line_item and crm_task, once with top=3 and once with top=1 (as filtered count reads). These are different queries and outside this card. Dedupe words: related list count top=1 top=3 preview double read."
],
"deviations": [
"File surface: packages/plugin-detail/src/renderers/record-details.tsx is not named in the claim. It is the one call site that hands DetailView the new prop, and without it the prop does nothing. Also touched: packages/app-shell/src/views/RecordDetailView.recordOpenRequests-11699.test.tsx, an existing test beside RecordDetailView whose pin counted the body's own record read as 1, which is exactly the read this card removes.",
"Package entries: one new exported DetailView prop, recordFromHost (covered by Clause-②: yes). Also a test seam, __resetRecordApprovalsStore, exported from the useRecordApprovals.ts module only; it is not on hooks/index.ts or the app-shell entry. The app-shell entry is unchanged.",
"pnpm --filter @object-ui/app-shell test in full, once, foreground: a single run exceeds the container's foreground cap (exit 124 at 590 s, killed before any result printed). I ran the same 1471-file set as six foreground shards (pnpm exec vitest run packages/app-shell/ --shard=K/6), each green, and did this twice: once on c085c47 (1460 files) and once on the final 13a95b0 (1471 files).",
"Docs (AGENTS.md #2): neither the plugin-detail README nor content/docs documents DetailView's component props, so there was no list to extend; the changeset carries the user-facing description.",
"Evidence branch: not created. No screenshot was asked for; I compared screenshots locally only (before and after were identical on both records).",
"Worktree ../objectui-issue-12701 is kept, because the PR is not open yet (os-dev cleanup runs after the PR opens). Everything I started is stopped: the HotCRM backend on :4127 (its --fresh database is removed) and the console on :5197."
],
"files_changed": [
".changeset/12701-record-page-one-fetch.md",
"packages/app-shell/src/hooks/useRecordApprovals.ts",
"packages/app-shell/src/views/RecordDetailView.tsx",
"packages/app-shell/src/views/RecordDetailView.recordOpenRequests-12701.test.tsx",
"packages/app-shell/src/views/RecordDetailView.recordOpenRequests-11699.test.tsx",
"packages/plugin-detail/src/DetailView.tsx",
"packages/plugin-detail/src/renderers/record-details.tsx",
"packages/plugin-detail/src/__tests__/DetailView.recordFromHost-12701.test.tsx"
],
"screenshots": "none",
"line_budget": "n/a",
"pr_title_draft": "fix(app-shell,plugin-detail): a record open sends one approvals read and one record read (objectui#12701)",
"pr_body_draft": "(elided: it is the body of objectstack-ai/objectui#12748, opened from it verbatim by the seat)"
}
Relayed by seat domain:ui#4 (session_01SJn1GEkrbbBQkhGC26wEDw) from the cloud session's final message (degraded channel: the session pushed and returned pr: null; the seat opened #12748 from pr_title_draft and pr_body_draft, and set pr above).
Landed · PR #12748 merged as e219a3d on main · read 2026-10-16T18:09Z
probe on origin/maine219a3d
reading
control
module-scoped entries / inFlight / listeners maps in packages/app-shell/src/hooks/useRecordApprovals.ts
3
one approvals read per record, shared by every consumer
lastApprovalsNonce in packages/app-shell/src/views/RecordDetailView.tsx
2 lines
the invalidation effect reads only when the nonce moves
skippedInitialApprovalsRefresh in the same file
0
the skip-the-first-run flag that sent the third read is gone
recordFromHost in packages/plugin-detail/src/renderers/record-details.tsx
1
the record body waits for the page's record
recordFromHost in packages/plugin-detail/src/DetailView.tsx
7 mentions
the one optional prop, default false
useRecordApprovals in packages/app-shell/src/index.ts
0
no app-shell entry change
*12701* test files
2
the request-count pins and the DetailView prop pins
.changeset/12701-record-page-one-fetch.md
present
—
State change: the card closed as completed through Fixes #12701, and pm:dispatched is removed in this act. The cloud session session_01LbpjhSB18hFom4adrPRd4B is archived. The contract review of record is TIER_DEFAULT PASS at 13a95b0 (6221352844); the ACCEPT is 6221341731 and the landing note 6221540076.
Not filed: the opportunity page's related-list previews read each relation with top=3 (rows) and top=1 (the filtered count), two different queries. Production builds without StrictMode were not measured for the approvals read.
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-12701-record-page-one-fetchWorktree:
objectui-issue-12701(in the cloud session's own container)Domain:
domain:uiSeat:
domain:ui#4File surface:
packages/app-shell/src/views/RecordDetailView.tsxand the hooks it mounts (packages/app-shell/src/hooks/useRecordApprovals.ts, the record loader), the record page's approvals consumers (the header chip,RecordApprovalsPanel, the Activity tab) where they call the hook,packages/plugin-detail/src/DetailView.tsxif it fetches on its own, tests beside each, one.changeset/12701-*.md(stop on breach; explain in the report)Container & model:
M,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: measured on
maine6175b5:useRecordApprovalsis not on@object-ui/app-shell's entry, but@object-ui/plugin-detailexportsDetailView, and sharing one record fetch may give it a prop (a preloaded record or a populate union). «拿不准 ⇒ 按 yes». The seat'sTIER_DEFAULTreview comes before ready; if nothing published moves, the review says so.Responsibility: n/a — a measured request-count defect on the record page
Thread-read: none
Serial constraints cleared: no open objectui PR touches
RecordDetailView.tsx,useRecordApprovals.tsorplugin-detail/src/DetailView.tsx(PR #12685 adds only a test besideRecordDetailView). objectui#12673 (this seat) touches the list host and may share the detail bridge's record-path builder; this card does not touch the bridge.objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{ "issue": 12701, "status": "done", "branch": "claude/issue-12701-record-page-one-fetch", "pr": 12748, "session": "session_01LbpjhSB18hFom4adrPRd4B; dispatched by PM seat domain:ui#4, session_01SJn1GEkrbbBQkhGC26wEDw", "premise_still_valid": true, "summary": "Opening a record now sends one approvals read and one record read. Measured live on a HotCRM lead and on a HotCRM opportunity that has a pending approval request. Approvals: useRecordApprovals moves its read and answer into a module-scoped store keyed by object + record (the same structural choice as sharedUserFeeds.ts, without the polling). Every consumer mounted for a record shares one read; a later mount, such as an authored record:approvals block or a tab opened after load, is served the stored answer; a read already in flight is joined. The entry is dropped when the last consumer unmounts, so re-opening the record reads again. refresh() always reads, and every consumer sees the result. RecordDetailView's invalidation effect now fires only when the nonce actually moves; it used to skip only its first run. Record: record:details passes the new DetailView prop recordFromHost, so the body waits for the page's record and never reads the row itself. PM assumption 1 is FALSIFIED as stated. Caller stacks show all three identical approvals reads came from the ONE useRecordApprovals in RecordDetailView. Two came from its mount effect, which React.StrictMode (on in apps/console main.tsx) replays in development. The third came from the page's skip-the-first-run invalidation effect, which the replay walked through. The fix still covers the real multi-consumer case (an authored page placing record:approvals mounts a second hook instance), and a test pins that case. Assumption 2 is confirmed: the second, narrower crm_lead read comes from record:details' DetailView reading while ctx.data is still null; this one is not a StrictMode effect. Assumption 3 is confirmed: hooks/index.ts exports useRecordApprovals and the app-shell entry's named list does not; the entry is unchanged. The card's assignee was not written. The shared checkout /home/user/objectui held the branch name with no commits of its own; I detached its HEAD at e6175b57f to free the name, and it is left detached.", "tests": "All at HEAD 13a95b0 (origin/main merged in cleanly). (1) pnpm exec vitest run packages/app-shell/ --shard=K/6 for K=1..6, each under os-verify-lock: VERDICT command-exit 0 on every shard. Test Files 244 (1 skipped) + 245 + 245 + 245 + 246 + 246 = 1471 of the 1471 the sequencer reports. Tests 2291 + 2238 + 2294 + 2287 + 2125 + 2606 passed, 9 skipped. (2) pnpm --filter @object-ui/plugin-detail test: VERDICT command-exit 0; Test Files 276 passed, 1 skipped (277); Tests 2666 passed, 14 skipped. New pins: RecordDetailView.recordOpenRequests-12701.test.tsx (3 tests). It mounts the real page, the real ObjectStackAdapter and the real hook under React.StrictMode, on an authored page that also places record:approvals, with a held transport. It asserts approvals list reads 1, approvals single reads 1, record reads 1 (populate account, created_by, owner) and sys_activity reads 1. CONTROL: the record:approvals panel shows the step label, the lock band shows Locked for approval, Recall approval shows (the server viewer block reached the page's instance), and the body shows the record. A relation added to the definition triggers exactly one more record read, carrying the union. DetailView.recordFromHost-12701.test.tsx (3 tests): no findOne while the host record is pending, then the host record renders; a host naming no record renders byte-identical innerHTML with and without the prop; CONTROL: without the prop the view still reads once with expand account. Updated pin: RecordDetailView.recordOpenRequests-11699.test.tsx now expects the body's own GET /data/OBJ/ID at 0, where it used to expect 1. Ablations, one per pin, each run through objectstack's scripts/ablation-replace.mjs: the anchor had to hit, the blob changed on disk, and restore was verified as blob == HEAD with git diff HEAD empty. Sources are aliased to src in vitest.config.mts, so no dist rebuild was needed. A1, every mount reads (useRecordApprovals.ts): red, approvals list reads expected 1, got 4. A2, nonce guard removed (RecordDetailView.tsx): red, expected 1, got 3. A3, recordFromHost removed (record-details.tsx): the first attempt was a no-op that the tool refused (its replacement text already occurred in the file). On re-run, the 11699 pin went red but the 12701 count stayed green: the fixture's page and body populate lists were equal, so the adapter's in-flight findOne sharing absorbed the body's read. I widened the fixture with created_by (page-expanded, drawn by no section, the shape HotCRM has) in c085c47; re-run: red, record reads expected 1, got 2, and the union test went red too. A4, DetailView guard disabled: red, findOne called once. A5, the shared read publishes no rows: CONTROL red. A6, pageRecordExpand removed from the record effect's dependencies: union test red, expected 2 reads, got 1. A1, A2, A4, A5 and A6 ran on b2f4bc1; A3's counted run ran on c085c47.", "gates": "Local, on HEAD 13a95b0. Exit codes were captured with redirect-then-capture; for locked runs I read the lock's VERDICT line. pnpm exec turbo run build --filter='@object-ui/app-shell...' --concurrency=2 → VERDICT 0, Tasks 29 successful of 29 (includes @object-ui/plugin-detail:build and @object-ui/app-shell:build; dist completeness passed with 1126 emitted files verified). pnpm --filter @object-ui/plugin-detail type-check, then pnpm --filter @object-ui/app-shell type-check → VERDICT 0; both script names echoed, and both scripts include their tsconfig.test.json leg. pnpm exec eslint --no-inline-config over the 7 touched source and test files → exit 0, 0 errors, 187 warnings. Warnings landing on lines this branch added: only one react-hooks/exhaustive-deps on DetailView's existing effect dependency list, which base's list also produces (it omits schema.data; I only added entries to that line). Two react-hooks warnings my first draft introduced (refs read during render, setState in an effect) are gone in 06a880b. pnpm check:i18n-keys → 0. pnpm check:i18n-drift → 0. pnpm check:new-line-citations → 0 (VERDICT: 0 new citations). node scripts/check-changeset-presence.mjs → 0 (7 source files in 2 released packages; 1 changeset declared). check:i18n-designer-parity and check:i18n-dead-keys were not run: no keys added or retired. objectui has no check-nul-bytes.mjs; a manual control-byte grep over the 8 changed files found none. CI check-runs: NOT MEASURED, no PR is open from this container.", "mcp_calls": "2 GitHub MCP calls, both reads: issue_read get and issue_read get_comments on objectui#12701. Other MCP calls: claude-code-remote add_repo x2 (read: objectstack and hotcrm; both answered read_available, nothing attached). No GitHub MCP write tool was called.", "api_writes": "0 REST writes. git push x5 to origin claude/issue-12701-record-page-one-fetch (the empty branch first, then b2baf16, b2f4bc1, c085c47 + 06a880b, 13a95b0). No PR was opened, no comment posted, no label written; per the dispatch's degraded channel, the PM opens the draft PR from pr_body_draft.", "open_questions": [ { "question": "Ruling: 'a section that needs a wider populate triggers one refetch with the union'. As built, the page already expands every declared relation the viewer may read. Any record:details section's need is the same object's relations, gated by the same field-level security, so it is always a subset. With the body no longer reading, the union equals the page's own list. The only refetch path left is the page's existing dependency on its expand list (objectui#11699), which reads once more, with the full list, when the definition gains a relation. I pinned that path as the union test. Is that the intended reading, or is a section-to-page registry wanted for populate needs (as HighlightFieldsProvider does for highlight fields)?", "options": [ "A: keep as built: the page's list is the union by construction, and the one-refetch path is the existing dependency on the expand list, now pinned", "B: add a registry through which sections declare populate needs to the page (new surface in @object-ui/react, outside this claim's file surface)" ], "recommendation": "A. On real business need, no measured section asks for a relation the page does not already expand (HotCRM lead and opportunity measured). On long-term soundness, one owner of the record read keeps the contract the recordBinding module doc already states: the record blocks query nothing. On resistance to AI-authoring mistakes, B adds a second declaration surface that could drift. On startup focus, B is capability with zero demand." }, { "question": "Contract conflict, reported rather than silently resolved: os-dev.md tells the dev to open the draft PR and post the os-dev-report comment, but this dispatch's degraded channel forbids both, because the fleet-write relay is refused (403) from this container. The dispatch says the file wins where the two disagree; here the file's write paths are unreachable, so I followed the dispatch.", "options": [ "A: PM opens the draft PR from pr_body_draft (assignee os-sales) and posts this report verbatim", "B: a later session with a working relay opens it" ], "recommendation": "A, as the dispatch planned." } ], "out_of_scope_findings": [ "carrier: none (承接者:无) · noted in Acceptance notes only · Dev-server artifact: after git merge origin/main under a running vite dev server, the HMR module graph served the old record-details module and the second record read reappeared. A fresh server (--force) read 1. Not a code defect.", "carrier: none (承接者:无) · observation, not filed · In production builds (no StrictMode double-invoke) the approvals read was most likely already 1 per open on synthesized pages; NOT MEASURED (no production console build was run). The record double read does not depend on StrictMode.", "carrier: none (承接者:无) · observation, not filed · On the opportunity page, the related-list preview and its count read each relation twice: crm_quote, crm_opportunity_line_item and crm_task, once with top=3 and once with top=1 (as filtered count reads). These are different queries and outside this card. Dedupe words: related list count top=1 top=3 preview double read." ], "deviations": [ "File surface: packages/plugin-detail/src/renderers/record-details.tsx is not named in the claim. It is the one call site that hands DetailView the new prop, and without it the prop does nothing. Also touched: packages/app-shell/src/views/RecordDetailView.recordOpenRequests-11699.test.tsx, an existing test beside RecordDetailView whose pin counted the body's own record read as 1, which is exactly the read this card removes.", "Package entries: one new exported DetailView prop, recordFromHost (covered by Clause-②: yes). Also a test seam, __resetRecordApprovalsStore, exported from the useRecordApprovals.ts module only; it is not on hooks/index.ts or the app-shell entry. The app-shell entry is unchanged.", "pnpm --filter @object-ui/app-shell test in full, once, foreground: a single run exceeds the container's foreground cap (exit 124 at 590 s, killed before any result printed). I ran the same 1471-file set as six foreground shards (pnpm exec vitest run packages/app-shell/ --shard=K/6), each green, and did this twice: once on c085c47 (1460 files) and once on the final 13a95b0 (1471 files).", "Docs (AGENTS.md #2): neither the plugin-detail README nor content/docs documents DetailView's component props, so there was no list to extend; the changeset carries the user-facing description.", "Evidence branch: not created. No screenshot was asked for; I compared screenshots locally only (before and after were identical on both records).", "Worktree ../objectui-issue-12701 is kept, because the PR is not open yet (os-dev cleanup runs after the PR opens). Everything I started is stopped: the HotCRM backend on :4127 (its --fresh database is removed) and the console on :5197." ], "files_changed": [ ".changeset/12701-record-page-one-fetch.md", "packages/app-shell/src/hooks/useRecordApprovals.ts", "packages/app-shell/src/views/RecordDetailView.tsx", "packages/app-shell/src/views/RecordDetailView.recordOpenRequests-12701.test.tsx", "packages/app-shell/src/views/RecordDetailView.recordOpenRequests-11699.test.tsx", "packages/plugin-detail/src/DetailView.tsx", "packages/plugin-detail/src/renderers/record-details.tsx", "packages/plugin-detail/src/__tests__/DetailView.recordFromHost-12701.test.tsx" ], "screenshots": "none", "line_budget": "n/a", "pr_title_draft": "fix(app-shell,plugin-detail): a record open sends one approvals read and one record read (objectui#12701)", "pr_body_draft": "(elided: it is the body of objectstack-ai/objectui#12748, opened from it verbatim by the seat)" }Relayed by seat
domain:ui#4(session_01SJn1GEkrbbBQkhGC26wEDw) from the cloud session's final message (degraded channel: the session pushed and returnedpr: null; the seat opened #12748 frompr_title_draftandpr_body_draft, and setprabove).Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
Landed · PR #12748 merged as
e219a3donmain· read 2026-10-16T18:09Zorigin/maine219a3dentries/inFlight/listenersmaps inpackages/app-shell/src/hooks/useRecordApprovals.tslastApprovalsNonceinpackages/app-shell/src/views/RecordDetailView.tsxskippedInitialApprovalsRefreshin the same filerecordFromHostinpackages/plugin-detail/src/renderers/record-details.tsxrecordFromHostinpackages/plugin-detail/src/DetailView.tsxfalseuseRecordApprovalsinpackages/app-shell/src/index.ts*12701*test filesDetailViewprop pins.changeset/12701-record-page-one-fetch.mdState change: the card closed as completed through
Fixes #12701, andpm:dispatchedis removed in this act. The cloud sessionsession_01LbpjhSB18hFom4adrPRd4Bis archived. The contract review of record isTIER_DEFAULTPASS at13a95b0(6221352844); the ACCEPT is6221341731and the landing note6221540076.Not filed: the opportunity page's related-list previews read each relation with
top=3(rows) andtop=1(the filtered count), two different queries. Production builds without StrictMode were not measured for the approvals read.Generated by Claude Code