Skip to content

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

Open

Description

@objectstack-fleet

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

  1. 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 a query body) 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.
  2. An app-scoped metadata bundle: GET /api/v1/meta/app/<appId>/bundle returns 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 sends If-None-Match after. Today's per-object GET /meta/object/<name> stays for Studio and for objects outside the bundle.
  3. 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=1 count probes, the per-open child metadata loads and the separate field-access / lock / explain calls on open retire (startup posture: no dual path kept).
  4. 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 carry Cache-Control + ETag.
  5. 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

  1. 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=1 probes.
  2. The cloud reading re-run by the epic seat on huayan.objectos.app after landing: record open under 1 s warm, list route under 3 s cold, with Server-Timing in the PR body.
  3. The epic seat verifies in the console preview before ACCEPT (maintainer: 「车队开发完后你审核效果」).

Activity

  1. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    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 for huayan.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 (REST Cache-Control + ETag + Server-Timing) is what that card's edge rule and timing split consume; the two land independently.

  2. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#3 · os-support-ai · session_01WGufzgbJhFoXYsLSe5WSPP

    Claim: PM loop round 3 · 2026-10-29T15:31Z
    Session: session_01WGufzgbJhFoXYsLSe5WSPP
    Account: os-support-ai
    Branch: claude/project-thread-2m7jqd (card thread; the platform assigns the thread's branch and it can push only that one, written as measured instead of claude/issue-27627-<slug>)
    Worktree: objectstack-27627
    Domain: domain:spec
    Seat: domain:spec#3
    Ruling-ref: the maintainer's ruling on epic #26676 quoted in the card body (「立…B(接缝卡:记录页一次取齐 + 元数据打包)两张卡,p1」); two PRs from this branch, the first Part of #27627
    File surface: producer: packages/rest/src/**, plugin-hono-server, packages/spec/contracts/** (the two shapes), packages/objectql only 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: high
    Clause-②: yes (widening)
    Metadata-protocol: added (the app bundle's shape), as measured by the dev
    Responsibility: an unclaimed p1 domain:spec card; seat 1 is off duty
    Thread-read: none
    Premise: to be measured first by the dev on origin/main 178b5238: one record open issues 23 (warm) to 45 (cold) requests in three serial stages
    Serial 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

  3. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#3 · os-support-ai · session_01WGufzgbJhFoXYsLSe5WSPP

    Claim amendment: one declaration and a surface extension (PR 1, #27713) · domain:spec seat 3 · session session_01WGufzgbJhFoXYsLSe5WSPP · 2026-10-29T20:18Z · same claim 6501011060, same branch claude/project-thread-2m7jqd

    • Metadata-protocol: added, as measured by the dev: the two new shapes enter packages/spec/authorable-surface/api.json, and check-changeset-no-major refuses none. Every moved package is minor.
    • File surface extends to every reader that retires its separate read in this PR. There is no dual path, so each reader takes its part of the payload: ui types, the components related-count store, plugin-detail (useRecordEditable, queueClaim, recordFieldAccess, RelatedList, index, package.json sideEffects), and app-shell useRecordApprovals plus a new recordPagePayload.ts. It also covers the dogfood fixture and test, the rest census tests and rest-server-registrar-files.ts, the scripts/check-route-envelope.mjs MODULES row, the generated spec artifacts and content/docs/references/api. The PR body gives the reason. Size 2980 lines (under the 3000 human-merge threshold), 0 governed paths.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:recordsBusiness objects, records, the views that show data, usable forms, searchdomain:specenhancementNew feature or requestpm:dispatchedpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions