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.
  4. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

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

    ACCEPT → PR #27713 comment 6524688486 (Tier H: approved by the maintainer, enqueued)

  5. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

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

    Landed (PR 1 of 2) · domain:spec seat 3 · os-support-ai · 2026-10-30T21:07Z

    reading value
    PR #27713 seat ACCEPT (6524688486, re-issued 6530577529 and verified again 6532601590) on triage's PASS (6514375644, then 6526284371); Tier H, approved by the maintainer (os-zhuang); merged through the merge queue at 2026-10-30T21:06Z, head 6293d95ef, merge commit 0027c4a2b8
    origin/main contains the merge commit (git merge-base --is-ancestor)

    Delivered:

    • POST /api/v1/data/{object}/{id}/page answers 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.
    • The console record page (full page, split pane and drawer) asks only that request, and warms both neighbours in the v4 pane. A cold open went from 13 requests in two serial batches to 1 data request plus the object definition read.

    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:queue for the next seat. The deployment half is cloud#3378.

  6. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-tesla · session_01L9qibwb6hBqszPTYJobitm

    Claim: PM loop round 6 · 2026-10-31T02:57Z
    Session: session_01L9qibwb6hBqszPTYJobitm
    Account: os-tesla
    Branch: claude/project-thread-dd5lh4 (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-issue-27627-timing
    Domain: domain:spec
    Seat: domain:spec#1
    Card 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: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: metadata list routes and runtime/config answer without ETag / 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 path
    Thread-read: 6533483101
    Premise: to be measured first by the dev on main 69ce01d (per-route ETag / Cache-Control / 304 table); PR 1 #27713's body says the item routes already send all three
    Serial 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.ts

  7. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-tesla · session_01L9qibwb6hBqszPTYJobitm

    Claim: PM loop round 6 · 2026-10-31T02:58Z
    Session: session_01L9qibwb6hBqszPTYJobitm
    Account: os-tesla
    Branch: claude/project-thread-hw8j7d (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-issue-27627-bundle
    Domain: domain:spec
    Seat: domain:spec#1
    Card thread: session session_01L9qibwM9YxDuiRDQYmA1Yd; PR 2, Target 2 producer (GET /api/v1/meta/app/{appId}/bundle + per-caller ETag), 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/spec contracts + generated artifacts, rest census tests, the check-route-envelope row, 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: high
    Clause-②: 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 an ETag, built through the item read gate | console sessions on the cloud path
    Thread-read: 6533483101
    Premise: holds — on main 69ce01d no /meta/app/{id}/bundle route exists, and PR 1 #27713's body names the remaining second-wave /meta/object read as PR 2's
    Serial constraints cleared: PR 1 #27713 merged 2026-10-30T21:06Z; the sibling Target 4 thread (claude/project-thread-dd5lh4) never touches the bundle route

  8. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    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#1 beside its producer claim 6538108232
    Session: session_01L9qibxgfM8p8t3fo6y9eFK (seat; card thread session_01L9qibzFu8iUogiZhxL9tAo)
    Account: os-tesla
    Branch: claude/project-thread-xoio4p (card thread; the platform assigns the thread's branch)
    Worktree: objectstack-27627-console
    Domain: domain:ui (the consumer half of a domain:spec Seam card)
    Seat: domain:ui#2
    File 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: xhigh
    Clause-②: 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 merges origin/main after it merges
    Part of #27627 (PR 3); the maintainer's 「27627 插队」 makes it this seat's top card.

  9. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-tesla · session_01L9qibwb6hBqszPTYJobitm

    Claim correction · 2026-10-31T03:04Z · amends claim 6538102504 (branch claude/project-thread-dd5lh4), append-only

    File surface, amended by seat ruling:

    • Plus packages/cloud-connection/src/runtime-config-plugin.ts and its tests. GET /runtime/config gets a body-hash ETag with private, no-cache, and If-None-Match answers 304. It stays anonymous. The caller is resolved only when X-OS-Debug-Timing is sent, so the existing timing gate can open there.
    • Plus GET /auth/me/localization (plugin-hono-server, already in surface), which gets the same ETag with private, no-cache.
    • Both routes are named in the card's cloud reading. The dev's Step 1 table (58 routes on main 69ce01d) is the premise.
  10. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-tesla · session_01L9qibwb6hBqszPTYJobitm

    Claim correction · 2026-10-31T03:10Z · amends claim 6538108232 (branch claude/project-thread-hw8j7d), append-only

    File surface, amended by seat ruling: also packages/client (meta.getAppBundle(appId)), so the route ledger row takes disposition sdk. PR 1's data.recordPage is the precedent. PR: #28287 (draft, Part of #27627). Shape: packages/spec/src/api/app-bundle.zod.ts AppMetadataBundleResponseSchema.

  11. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-tesla · session_01L9qibwb6hBqszPTYJobitm

    Claim correction · 2026-10-31T03:11Z · amends claim 6538102504 (branch claude/project-thread-dd5lh4), append-only

    File 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 in packages/types/src/index.ts. This is the one copy of the body-hash ETag / If-None-Match rule. rest, plugin-hono-server and cloud-connection all depend on types, and only rest keeps the /meta wrapper.
    • packages/cloud-connection/package.json gains @objectstack/observability (workspace:*), plus its lockfile lines. It is needed for the existing admin perf-disclosure gate on runtime/config.
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