Repository navigation
Seam: one request opens a record — a record-page payload (record + related counts and first rows + field access + lock) and an app-scoped metadata bundle, so a record open is 2–3 requests instead of 23–45 serialized in three stages #27627
Description
epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff
Filed from the record-page epic #26676 as a sub-issue after the maintainer's 「别人的界面很丝滑,但是我们的界面总觉得卡顿,云端更卡」 and the seat's measurement (2026-10-29). Ruling: 「立…B(接缝卡:记录页一次取齐 + 元数据打包)两张卡,p1」. A Seam: card: the producer is the REST surface (packages/rest / plugin-hono-server, the data and metadata routes), the consumer is the console's data layer (ui/packages/data-objectstack) and the record page; the seat that claims it declares both ends. The client-side waste on the same path is #27626.
Measured
Local (console main 273137d227, dev mode, HotCRM on localhost:4001): opening one contact = 45 requests cold / 23 warm, in three serial stages — ① the record (GET /data/crm_contact?populate=…) + security/explain ×2 + approvals/requests; ② after the record arrives (+130 ms of client work): one GET /meta/object/<child> per related object (9 on a contact), field-access ×4, the approvals lock; ③ after the metadata arrives: one GET /data/<child>?top=1&filter=… count probe per related list (10 on a contact) + attachments + the related rows query + the account. Locally each stage is tens of milliseconds; the drawer is visible at 300 ms.
Cloud (huayan.objectos.app, Cloudflare colo ARN, h2, prod bundle 2.2 MB JS in 93 scripts): the same endpoints cost 0.5–1.4 s each when called alone (GET /api/v1/runtime/config, 293 bytes: 555 / 604 / 729 / 1068 / 1377 ms over five sequential calls; GET /meta/app 0.9–1.8 s; GET /meta/view 1.8–3.8 s; no cache-control, no server-timing). The HotCRM contacts list route fired 42 API calls on load, of which auth/me/localization 8.2 s, packages 7.9 s, sys_inbox_message 7.5 s, meta/dashboard 6.1 s, meta/object/sys_user 5.3 s, data/crm_contact 5.3 s — the same calls that take ~1 s alone take 5–8 s when fifteen of them run at once, and the list had not rendered after 50 s. A three-stage, 23-request record open on that path is a multi-second wait by arithmetic alone.
Linear, Attio and Twenty open a record with one aggregated request (counts and permissions ride with the record), load metadata once at boot, and prefetch on hover.
Target
- A record-page payload endpoint (shape to be designed by the claiming seat and recorded under
packages/spec/contracts/**):GET /api/v1/data/<object>/<id>/page(or aquerybody) returns in ONE response — the record with its populated lookups; for every related list the console would draw (the inbound lookups, in the object's declared order) the count and the first N rows (N = 3, the compact panel's face) with the related-list columns; the caller's field access for the record; the approval lock state; the record's security verdict (explain) for the actions the header draws. One request, one round trip, one server-side transaction. - An app-scoped metadata bundle:
GET /api/v1/meta/app/<appId>/bundlereturns the app, its navigation, every object the app's navigation and those objects' inbound relations reach, their views, pages and dashboards, with an ETag; the console loads it once per session and sendsIf-None-Matchafter. Today's per-objectGET /meta/object/<name>stays for Studio and for objects outside the bundle. - The console consumes both: the record page (split pane, overlay, full page, Record page v4: same-shaped related panels with no collapse, a persistent tab row under the header (the enlarged state is the active tab), Activity as the first panel and a tab, one top bar on every face, tiers by container width #27524 / Record drawer v4: a bounded peek over the list — md cap instead of the field-count bucket, no dim overlay, the list stays readable and clickable, ⤢ maximizes in place at the same URL; the full page is reached only from the row ↗ and the header ⋯ #27545) opens from the page payload with no second stage; stepping ↓ ↑ prefetches the next and previous records' payloads; the per-related
top=1count probes, the per-open child metadata loads and the separatefield-access/lock/explaincalls on open retire (startup posture: no dual path kept). - Server-side observability for the cloud reading: the REST layer emits
Server-Timing(db, meta, total) on every response, so the next measurement separates origin time from the network path; metadata responses carryCache-Control+ETag. - Clause-②: yes (new REST routes, declared in the spec contracts); Metadata-protocol: added (the bundle's shape).
File surface
Producer: packages/rest/src/** (routes), packages/plugin-hono-server/** (wiring), packages/spec/contracts/** (the two shapes), packages/objectql only if a batched count query is needed, tests beside, changeset. Consumer: ui/packages/data-objectstack/src/index.ts, ui/packages/app-shell/src/views/RecordDetailView.tsx + recordDrawerStack.tsx (prefetch), ui/packages/plugin-detail/src/renderers/recordBinding.tsx + RelatedCardStack.tsx (counts and rows from the payload), tests beside, changeset.
Acceptance
- Contract tests for both shapes; a console test that a record open issues exactly one page-payload request (plus at most one metadata bundle on a cold session) and zero
top=1probes. - The cloud reading re-run by the epic seat on
huayan.objectos.appafter landing: record open under 1 s warm, list route under 3 s cold, withServer-Timingin the PR body. - The epic seat verifies in the console preview before ACCEPT (maintainer: 「车队开发完后你审核效果」).
Activity
objectstack-fleet commentedon Oct 29, 2026
epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff
Deployment half filed · 2026-10-29T15:22Z · objectstack-ai/cloud#3378 (Ops, p1,
pm:queue): the path and origin reading forhuayan.objectos.app— the ~0.5 s floor per call, the Stockholm (ARN) colo for a China user, the 5–8 s per call under a 15-call burst, edge caching for/meta/*. Item 4 of this card (RESTCache-Control+ETag+Server-Timing) is what that card's edge rule and timing split consume; the two land independently.objectstack-fleet commentedon Oct 29, 2026
domain:spec#3 · os-support-ai · session_01WGufzgbJhFoXYsLSe5WSPP
Claim: PM loop round 3 · 2026-10-29T15:31Z
Session:
session_01WGufzgbJhFoXYsLSe5WSPPAccount:
os-support-aiBranch:
claude/project-thread-2m7jqd(card thread; the platform assigns the thread's branch and it can push only that one, written as measured instead ofclaude/issue-27627-<slug>)Worktree:
objectstack-27627Domain:
domain:specSeat:
domain:spec#3Ruling-ref: the maintainer's ruling on epic #26676 quoted in the card body (「立…B(接缝卡:记录页一次取齐 + 元数据打包)两张卡,p1」); two PRs from this branch, the first
Part of #27627File surface: producer:
packages/rest/src/**,plugin-hono-server,packages/spec/contracts/**(the two shapes),packages/objectqlonly for a batched count; consumer:ui/packages/data-objectstack,RecordDetailView.tsx,recordDrawerStack.tsx,recordBinding.tsx,RelatedCardStack.tsx; tests; one root changeset per touched published package (stop on breach; explain in the report)Container & model:
L,mode:thread,model: TIER_DEFAULT — --tier: no path-derived mandate (new REST routes; the contract review is triage's),effort: highClause-②: yes (widening)
Metadata-protocol: added (the app bundle's shape), as measured by the dev
Responsibility: an unclaimed p1
domain:speccard; seat 1 is off dutyThread-read: none
Premise: to be measured first by the dev on
origin/main178b5238: one record open issues 23 (warm) to 45 (cold) requests in three serial stagesSerial constraints cleared: #27626 (client-side waste, same path) and cloud#3378 (deployment half) are left untouched; the dev re-checks open PRs on these files before editing
objectstack-fleet commentedon Oct 29, 2026
domain:spec#3 · os-support-ai · session_01WGufzgbJhFoXYsLSe5WSPP
Claim amendment: one declaration and a surface extension (PR 1, #27713) ·
domain:specseat 3 · sessionsession_01WGufzgbJhFoXYsLSe5WSPP· 2026-10-29T20:18Z · same claim 6501011060, same branchclaude/project-thread-2m7jqdMetadata-protocol: added, as measured by the dev: the two new shapes enterpackages/spec/authorable-surface/api.json, andcheck-changeset-no-majorrefusesnone. Every moved package isminor.types, thecomponentsrelated-count store,plugin-detail(useRecordEditable,queueClaim,recordFieldAccess,RelatedList,index,package.jsonsideEffects), and app-shelluseRecordApprovalsplus a newrecordPagePayload.ts. It also covers the dogfood fixture and test, the rest census tests andrest-server-registrar-files.ts, thescripts/check-route-envelope.mjsMODULES row, the generated spec artifacts andcontent/docs/references/api. The PR body gives the reason. Size 2980 lines (under the 3000 human-merge threshold), 0 governed paths.objectstack-fleet commentedon Oct 30, 2026
domain:spec#3 · os-support-ai · session_01WGufzgbJhFoXYsLSe5WSPP
ACCEPT → PR #27713 comment 6524688486 (Tier H: approved by the maintainer, enqueued)
objectstack-fleet commentedon Oct 30, 2026
domain:spec#3 · os-support-ai · session_01WGufzgbJhFoXYsLSe5WSPP
Landed (PR 1 of 2) ·
domain:specseat 3 ·os-support-ai· 2026-10-30T21:07Z6293d95ef, merge commit0027c4a2b8origin/maingit merge-base --is-ancestor)Delivered:
POST /api/v1/data/{object}/{id}/pageanswers in one request everything a record page needs when it opens: the record with its lookups expanded, each related list's total and first rows, field access, approvals and lock, and the header actions' permission verdicts. Each part is computed as the same caller, through the same function as the read it replaces.Released. PR 2 of this card (metadata bundling) is not started. Spec seat 3 goes off duty with this landing, so it releases its claim (6501011060) and assignee here. The card stays open with
pm:queuefor the next seat. The deployment half is cloud#3378.objectstack-fleet commentedon Oct 31, 2026
domain:spec#1 · os-tesla · session_01L9qibwb6hBqszPTYJobitm
Claim: PM loop round 6 · 2026-10-31T02:57Z
Session:
session_01L9qibwb6hBqszPTYJobitmAccount:
os-teslaBranch:
claude/project-thread-dd5lh4(card thread; the platform assigns the thread's branch and it can push only that one, written as measured instead ofclaude/issue-27627-<slug>)Worktree:
objectstack-issue-27627-timingDomain:
domain:specSeat:
domain:spec#1Card thread: session
session_01L9qibz5qPfjVU7Mns3iE1H; Target 4 (metadata caching headers + Server-Timing coverage),Part of #27627; the dispatch order is in the seat's project, frame + card file.Ruling-ref: the maintainer's 「27627 插队」 (2026-10-31T02:51Z) and the epic ruling quoted in the card body
File surface:
packages/rest/src/**(list routes,runtime/config, shared header helper),packages/plugin-hono-server/**(timing middleware if there), tests, changeset; never the bundle route (stop on breach; explain in the report)Container & model:
M,mode:thread,model: TIER_DEFAULT,effort: highClause-②: no
Metadata-protocol: none
Responsibility: metadata list routes and
runtime/configanswer withoutETag/Cache-Control, and Server-Timing does not cover every REST route | add both under the item routes' per-caller folds and the existing gate | console sessions on the cloud pathThread-read: 6533483101
Premise: to be measured first by the dev on
main69ce01d (per-route ETag / Cache-Control / 304 table); PR 1 #27713's body says the item routes already send all threeSerial constraints cleared: PR 1 #27713 merged 2026-10-30T21:06Z; the sibling bundle-producer thread owns the bundle route only; the dev re-checks open PRs on
meta-item-read-routes.ts/meta-collection-routes.tsobjectstack-fleet commentedon Oct 31, 2026
domain:spec#1 · os-tesla · session_01L9qibwb6hBqszPTYJobitm
Claim: PM loop round 6 · 2026-10-31T02:58Z
Session:
session_01L9qibwb6hBqszPTYJobitmAccount:
os-teslaBranch:
claude/project-thread-hw8j7d(card thread; the platform assigns the thread's branch and it can push only that one, written as measured instead ofclaude/issue-27627-<slug>)Worktree:
objectstack-issue-27627-bundleDomain:
domain:specSeat:
domain:spec#1Card thread: session
session_01L9qibwM9YxDuiRDQYmA1Yd; PR 2, Target 2 producer (GET /api/v1/meta/app/{appId}/bundle+ per-callerETag),Part of #27627; the console half is ui seat 2's, landing after this PR; the dispatch order is in the seat's project, frame + card file.Ruling-ref: the maintainer's 「27627 插队」 (2026-10-31T02:51Z) and the epic ruling quoted in the card body
File surface:
packages/rest/src/**(the bundle route),packages/plugin-hono-server/**wiring if needed,packages/speccontracts + generated artifacts, rest census tests, thecheck-route-enveloperow,content/docs/references/api, dogfood fixture if the census needs it, tests, changeset (stop on breach; explain in the report)Container & model:
L,mode:thread,model: TIER_DEFAULT(new REST route, so a contract review is owed),effort: highClause-②: yes (widening)
Metadata-protocol: added
Responsibility: a record open still loads
/meta/object/<object>per object after PR 1, and there is no app-scoped metadata bundle | one per-caller bundle with anETag, built through the item read gate | console sessions on the cloud pathThread-read: 6533483101
Premise: holds — on
main69ce01d no/meta/app/{id}/bundleroute exists, and PR 1 #27713's body names the remaining second-wave/meta/objectread as PR 2'sSerial constraints cleared: PR 1 #27713 merged 2026-10-30T21:06Z; the sibling Target 4 thread (
claude/project-thread-dd5lh4) never touches the bundle routeobjectstack-fleet commentedon Oct 31, 2026
domain:ui#2 · os-tesla · session_01L9qibxgfM8p8t3fo6y9eFK
Claim: PM loop round 1 · 2026-10-31T02:59Z · console half (Target 3 metadata), split out by
domain:spec#1beside its producer claim 6538108232Session:
session_01L9qibxgfM8p8t3fo6y9eFK(seat; card threadsession_01L9qibzFu8iUogiZhxL9tAo)Account:
os-teslaBranch:
claude/project-thread-xoio4p(card thread; the platform assigns the thread's branch)Worktree:
objectstack-27627-consoleDomain:
domain:ui(the consumer half of adomain:specSeam card)Seat:
domain:ui#2File surface:
ui/packages/data-objectstack/**,ui/packages/app-shell/**(app entry, record views),ui/packages/plugin-detail/**only if it reads child metadata, tests, root changeset (stop on breach; explain in the report)Container & model:
M/L,mode:thread,model: TIER_DEFAULT (default judgment tier),effort: xhighClause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6538108232
Premise: holds
Serial constraints cleared: lands after the producer PR of claim 6538108232 (consumer-after-producer); serial after PR #28116 (#27524 ②) on
RecordDetailView.tsx/ plugin-detail — the dev mergesorigin/mainafter it mergesPart of #27627 (PR 3); the maintainer's 「27627 插队」 makes it this seat's top card.
objectstack-fleet commentedon Oct 31, 2026
domain:spec#1 · os-tesla · session_01L9qibwb6hBqszPTYJobitm
Claim correction · 2026-10-31T03:04Z · amends claim 6538102504 (branch
claude/project-thread-dd5lh4), append-onlyFile surface, amended by seat ruling:
packages/cloud-connection/src/runtime-config-plugin.tsand its tests.GET /runtime/configgets a body-hashETagwithprivate, no-cache, andIf-None-Matchanswers 304. It stays anonymous. The caller is resolved only whenX-OS-Debug-Timingis sent, so the existing timing gate can open there.GET /auth/me/localization(plugin-hono-server, already in surface), which gets the sameETagwithprivate, no-cache.main69ce01d) is the premise.objectstack-fleet commentedon Oct 31, 2026
domain:spec#1 · os-tesla · session_01L9qibwb6hBqszPTYJobitm
Claim correction · 2026-10-31T03:10Z · amends claim 6538108232 (branch
claude/project-thread-hw8j7d), append-onlyFile surface, amended by seat ruling: also
packages/client(meta.getAppBundle(appId)), so the route ledger row takes dispositionsdk. PR 1'sdata.recordPageis the precedent. PR: #28287 (draft,Part of #27627). Shape:packages/spec/src/api/app-bundle.zod.tsAppMetadataBundleResponseSchema.objectstack-fleet commentedon Oct 31, 2026
domain:spec#1 · os-tesla · session_01L9qibwb6hBqszPTYJobitm
Claim correction · 2026-10-31T03:11Z · amends claim 6538102504 (branch
claude/project-thread-dd5lh4), append-onlyFile surface, amended by seat ruling. Both additions are forced by the dependency graph, and the PR body should name both:
packages/types/src/representation-validator.ts(new), plus one export line inpackages/types/src/index.ts. This is the one copy of the body-hash ETag /If-None-Matchrule.rest,plugin-hono-serverandcloud-connectionall depend ontypes, and onlyrestkeeps the/metawrapper.packages/cloud-connection/package.jsongains@objectstack/observability(workspace:*), plus its lockfile lines. It is needed for the existing admin perf-disclosure gate onruntime/config.