Repository navigation
rest/metadata-protocol: GET /meta/app's nav-servability gate zod-diagnoses every served object to read their enable flags, and every served metadata list recomputes _diagnostics on every read: 34% of a hosted tenant console open's server CPU, measured #25783
Description
Ruled: 6408692677 · letter A · 2026-10-26T05:08Z
Filing gate: ① a measured defect (server CPU on the console open path), found by cloud's attribution round cloud#2914 round 10 (report: https://github.com/objectstack-ai/cloud/issues/2914#issuecomment-6396581531). Platform-first: cloud's own frames are 0–3% of every call below, so the fix is here.
Session: session_01FSg7JRUiTPo8GrgT38tAWP · seat repo:cloud#1
Business reason. Opening a hosted tenant console currently costs about 501 ms of main-thread server CPU on one event loop (measured on a local hosted-shaped rig). On staging the per-call CPU reads about 4–8× that, which puts 2–4 s of the measured ~5.0 s open on the server. Two framework behaviours cost 169 ms (34%) of it, and neither is needed by anything on the open path.
Reader who acts. A framework dev for packages/rest and packages/metadata-protocol.
Measured (cloud main 36b07fdf, framework pin 888d6865, objectui 6f629a73, HotCRM 5.0.0 with sample data, zh-CN)
Each call was measured isolated and serially: 40 requests after 5 warm-ups. CPU is main-thread CPU from /proc. Profiles are V8 CPU profiles at 250 µs sampling.
| call | CPU / wall | computeMetadataDiagnostics share |
what is served |
|---|---|---|---|
tenant GET /api/v1/meta/app |
79.8 / 77.6 ms | 67.6 ms (74%) | 3 apps, whose own diagnostics cost 0.04–0.22 ms |
control plane GET /api/v1/meta/app |
67.5 / 64.3 ms | 58.5 ms (75%), over 99 objects | — |
tenant GET /api/v1/meta/object (whole registry) |
98.8 / 101.3 ms | 71.1 ms (63%), over 102 objects | 875 KB raw |
tenant GET /api/v1/meta/view |
20.5 / 20.8 ms | 7.0 ms (31%), over 110 views | — |
1. meta/app's nav-servability gate reads the SERVED object list
GET /meta/apprunsresolveNavServability(packages/rest/src/meta-item-read-gate.ts:1295-1303), which callsloadObjectItems(:441-445).- That goes through
metaListSource(rest-server.ts:3643-3652) toprotocol.getMetaItems, which is the served face. - So every object is decorated and zod-diagnosed only to learn its enable flag. A breakpoint count in one request showed 3 app items and 102 object items diagnosed.
This is the same class as #24031, which fixed it for the /data API-exposure gate. This is the /meta/app door.
Fix sketch: read a name → enable-flag projection, or the execution face, rather than the served list.
Expected: tenant meta/app CPU drops from 80 to about 12 ms, and control plane from 68 to about 10 ms.
2. Every served metadata list recomputes _diagnostics on every read
- The diagnostics are a full zod
safeParseper item (packages/metadata-protocol/src/protocol.ts:10175,metadata-diagnostics.ts:154-171). - Their reader is Studio. Nothing on the console open path reads them.
Fix sketch: memoize per stored item version, or compute them only when the caller asks (a Studio query flag).
Expected: tenant meta/object drops by 71 ms and meta/view by 7 ms.
Acceptance
- With the cloud#2914 rig, or any hosted-shaped rig with about 100 objects:
GET /meta/appcosts at most 15 ms main-thread CPU, andGET /meta/objectwith no Studio flag spends at most 5% of its CPU incomputeMetadataDiagnostics. Report the before and after numbers. - Studio's diagnostics surfaces still show the same verdicts. Re-pin the tests that read
_diagnostics. - rest: every /data request's API-exposure gate reads EVERY object's metadata (with a full schema diagnostic) to check ONE object's enable flags — 62-72% of a data request's time, measured #24031's gate test family covers the
/meta/appdoor too.
Dedupe
search_issuesfor 'computeMetadataDiagnostics served metadata list zod diagnostics every read meta/app nav servability' returned 9 hits, all closed. The nearest is rest: every /data request's API-exposure gate reads EVERY object's metadata (with a full schema diagnostic) to check ONE object's enable flags — 62-72% of a data request's time, measured #24031, the/datasibling, fixed.- No open card covers the
/meta/appgate or per-read diagnostics.
维护者速读
托管租户打开控制台时,服务端 CPU 有 34% 花在框架"每次读元数据都重新跑一遍 zod 诊断"上:/meta/app 为了判断导航项能否显示,把全部 102 个对象都诊断了一遍(只为读一个开关,和已修的 #24031 同一类问题);/meta/object 每次也全量诊断。诊断结果只有 Studio 用。修复后租户打开约省 170 ms 服务端 CPU(staging 上按 4–8 倍估计约 0.7–1.3 秒)。
Activity
objectstack-fleet commentedon Oct 25, 2026
Triage: first grade —
bug·priority:p1·domain:server·area:api·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-25T11:57Z⛔ Not a claim, ⛔ not a dispatch.
packages/rest+packages/metadata-protocol. Measure before and after on the same rig in the PR.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 1 · card thread · 2026-10-26T03:40Z
Session:
session_019d4JoyFgaZCRubmNWh91uZ(the card thread, mode:thread; the seat writes this claim for it: seat sessionsession_01TFnDdepdwyDJeM2kJ4GtMy)Account:
os-elon-musk(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/project-thread-mvtze8(platform-assigned to the card thread; a thread can push only its assigned branch, a measured platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-25783Domain:
domain:serverSeat:
domain:server#2(seat post #20966; 由 os-elon-musk 账户的项目负责)File surface (read on
origin/maincb0a2fcf; stop on a breach and explain it in the report):packages/rest/src/meta-item-read-gate.ts: the nav-servability read (loadObjectItems/resolveNavServability) reads the execution-face list instead of zod-diagnosing every served object.packages/rest/src/rest-server.ts: only the optionalgetMetaItemsForExecutionmember on theRestProtocoltype andmetaListSource.packages/metadata-protocol/src/metadata-diagnostics.ts:computeMetadataDiagnosticsmemoized on content + schema; the served wire shape stays byte-identical.patchchangeset (@objectstack/rest,@objectstack/metadata-protocol).packages/metadata-protocol/src/protocol.ts: expected 0 lines. If it must move, only the two decoration call sites in the metadata-reads region (the end ofreadFlattenedMetaItems, andgetMetaItem'sserveItem). This file is seat 1's hot file ([maintainer] refactor(metadata-protocol): splitsrc/protocol.ts(33,065 lines, +10,169 in 30 days) along itsObjectStackProtocolImplementationseams, one move-only slice per PR #25467, move-only slices): knock on [PM seat] domain:server — 🟢 os-elon-musk · 由 os-elon-musk 账户的项目负责 #6367 and declare the region before any edit there.packages/spec, ⛔ notui/. The card's other route ("Studio opts in with a query switch") changes/meta's parameters and a dozen_diagnosticsreaders inui/: a contract change, not this claim; report it as a finding if it is still wanted.Container & model:
M,mode:thread,model: default tier (dispatch-gates --tier on this surface: "no path-derived mandate"),effort: xhigh(p1)Clause-②: no
Metadata-protocol: none
Basis for the Clause-② line: a read-path cost change; no accept set, served body, route parameter or error code changes. The served
_diagnosticsstay byte-identical, pinned before and after.Responsibility:
packages/rest(meta-item-read-gate.ts) andpackages/metadata-protocol(metadata-diagnostics.ts) produce the cost | no platform path covers it: diagnostics are recomputed on every read | every console open on a hosted tenant reaches it today (34% of server CPU on the open, cloud#2914 measurement per the card)Thread-read: 6397335399
Premise: holds (
git log origin/main -5 -- packages/metadata-protocol/src/protocol.ts: the last five commits are [maintainer] refactor(metadata-protocol): splitsrc/protocol.ts(33,065 lines, +10,169 in 30 days) along itsObjectStackProtocolImplementationseams, one move-only slice per PR #25467 move-only cuts 7–11, none touching the diagnostics path; the thread re-measures before and after on the same rig, per triage)Writes: PR only through the relay
pr_create; comments, labels and bodies only through the relay write tools (post-stamped.mjs,label-write.mjs,fleet-write/dispatch.mjs). ⛔ No GitHub MCP write tool. Report both counts (writes,mcp_calls).Serial constraints cleared: seat 1's [maintainer] refactor(metadata-protocol): split
src/protocol.ts(33,065 lines, +10,169 in 30 days) along itsObjectStackProtocolImplementationseams, one move-only slice per PR #25467 (protocol.ts, no open PR at claim time; its next cut is outside the metadata-reads region, per the thread's reading). rest/i18n: GET /i18n/translations/:locale serves the whole locale bundle (288–361 KB raw, 80–88 KB compressed) on every console open with no ETag or Cache-Control, and carries Studio's metadataForms (46.8 KB) into the boot read #25785 (claim 6407675488) works onpackages/runtime/src/domains/i18n.ts, notrest-server.ts'sRestProtocoltype. No open PR namesmeta-item-read-gate.tsormetadata-diagnostics.ts.Resume-from: none
Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim amendment (file surface) ·
domain:server#2· seat sessionsession_01TFnDdepdwyDJeM2kJ4GtMy· 2026-10-26T03:43Z · amends claim 6407697525, same session, same branchclaude/project-thread-mvtze8.File surface adds:
packages/runtime/src/domains/meta.ts,metaItemReadGateSourcesonly: the object type readsgetMetaItemsForExecution(one member added to theMetaDomainProtocolPick), plus one test inpackages/runtime. Reason: it is the same nav-servability gate behind thedispatch()front door (thecreateHonoAppcatch-all and the embedded form, perroute-ledger.ts), the same defect with the same fix, and in this lane. The changeset adds@objectstack/runtime(patch). Every other line of the claim stands.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
os-dev-report
{ "issue": 25783, "status": "done", "branch": "claude/project-thread-mvtze8", "pr": "https://github.com/objectstack-ai/objectstack/pull/25941", "session": "session_019d4JoyFgaZCRubmNWh91uZ — card thread, mode:thread (Claude-Session trailer on both commits)", "premise_still_valid": true, "summary": "Head bb46886e, base cb0a2fcf, merges clean with origin/main 2a728ba2. (1) GET /meta/app's nav-servability gate reads `object` on the protocol's execution face (getMetaItemsForExecution) in RestServer.metaListSource and in the dispatcher's metaItemReadGateSources.listMetaItems (amendment 6407727649), falling back to getMetaItems; (2) computeMetadataDiagnostics memoizes verdicts per schema object (WeakMap) and per document content (SHA-256 of a faithful encoding; unkeyable documents parse every time), 4096-entry LRU, copies per caller. Measured on the showcase rig (108 objects, shared 4-core box, main-thread CPU/request): /meta/app 73.25 -> 16.75 ms (diag share 59.7% -> 1.3%), /meta/object 85.75 -> 37 ms (50.7% -> 15.6%), /meta/view 13.25 -> 11.25 ms; served bytes of app/object/view/flow/dashboard/page SHA-256-identical before vs after, cold and warm. Acceptance is short on both numbers (16.75 vs <=15 ms; 15.6% vs <=5%), so the PR says `Part of #25783`; the residue is named in the PR's Acceptance notes and in open_questions. protocol.ts 0 lines; nothing under packages/spec or ui/.", "tests": "New: packages/rest/src/meta-app-nav-servability-execution-read.test.ts (8 rows: app list + by name, unscoped + env-scoped, ZERO object diagnostics; FALLBACK without the execution face diagnoses every object with the same prune; CONTROL /meta/object still decorated) — on cb0a2fcf's rest-server.ts 4 rows fail ('expected [ listed_widget, unlisted_widget ] to deeply equal []'). packages/runtime/src/domains/meta-app-nav-object-execution-face.test.ts (3 rows via HttpDispatcher.dispatch) — on cb0a2fcf's meta.ts the 2 execution-face rows fail. packages/metadata-protocol/src/metadata-diagnostics.memo.test.ts (15 rows) — mutation to a naive JSON.stringify key fails all 6 collision rows (undefined vs missing, hole vs null, NaN vs null, both orders). Re-pinned: rest-exposure-gate-execution-read.test.ts proxy per test (beforeEach), 5 rows had recorded 0 parses under the memo. Full suites under os-verify-lock, VERDICT command-exit 0: @objectstack/metadata-protocol Test Files 264 passed | 3 skipped, Tests 21505 passed | 19 skipped; @objectstack/rest Test Files 328 passed, Tests 6730 passed | 344 skipped; @objectstack/runtime Test Files 448 passed, Tests 8011 passed | 19 skipped. typecheck (three packages, rest/runtime test layers included) exit 0. pnpm lint exit 0.", "gates": "dispatch-gates --commands: 71 commands; all 71 exit 0 on the final record; `dispatch-gates --ran`: 71 derived, 71 run, 0 NOT-MEASURED, 0 UNRUN. Red on the first pass, then green: check-rest-log-spy-declared (exit 1: new rest test spied console.warn without OS_REST_LOG; spies removed, they asserted nothing), check:engine-double-contract (exit 1: new rest test carried an unreached findOne double; removed, ledger unchanged), check-plugin-teardown-shape --self-test (exit 3: shallow clone lacked fixture 621a4876; fetched), check:dual-build-cjs-loads (exit 3: 7 packages had no dist; built them). CI on #25941: not concluded at report time; the seat reads the PR's check-runs.", "files_changed": [ ".changeset/25783-console-open-metadata-cost.md (+14/-0)", "packages/metadata-protocol/src/metadata-diagnostics.memo.test.ts (+216/-0)", "packages/metadata-protocol/src/metadata-diagnostics.ts (+146/-0)", "packages/rest/src/meta-app-nav-servability-execution-read.test.ts (+177/-0)", "packages/rest/src/meta-item-read-gate.ts (+4/-0)", "packages/rest/src/rest-exposure-gate-execution-read.test.ts (+5/-3)", "packages/rest/src/rest-server.ts (+17/-0)", "packages/runtime/src/domains/meta-app-nav-object-execution-face.test.ts (+107/-0)", "packages/runtime/src/domains/meta.ts (+14/-3)" ], "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "4 — mcp__github__issue_read ×4 (get #25783, get_comments #25783, get #6367, get #20966); no GitHub MCP write tool. Plus mcp__claude-code-remote__subscribe_pr_activity on #25941 (not a GitHub write).", "api_writes": "3 relay strokes as objectstack-fleet[bot] — (1) post-stamped comment on #6367 (hot-file knock, POST /repos/objectstack-ai/objectstack/issues/6367/comments); (2) fleet-write pr_create #25941 (POST /repos/objectstack-ai/objectstack/pulls draft + POST /repos/objectstack-ai/objectstack/issues/25941/assignees os-elon-musk; read-back 8952/8952 bytes identical); (3) this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/25783/comments). git push ×2 (not REST).", "deviations": [ "Rig servers started with `pnpm --filter @objectstack/example-showcase dev -- --fresh -p 38425` instead of `pnpm dev`: check-dev-prereqs refused `pnpm dev` (45 unbuilt packages). Same command and port for before and after.", "File surface beyond the claim: none beyond amendment 6407727649 (runtime meta.ts + one runtime test); @objectstack/runtime patch is in the changeset." ], "open_questions": [ { "question": "The card asks /meta/object (no Studio flag) at <=5% CPU in computeMetadataDiagnostics; content keying lands at 15.6% (warm reads parse nothing; the rest is the key: one JSON.stringify + SHA-256 over 694 KB, ~6 ms isolated). Is the card's other route still wanted?", "options": [ "A: land #25941 as Part of #25783 and close the card on these numbers (the <=5% / <=15 ms targets relaxed by the maintainer).", "B: land #25941, keep #25783 open for a separate card: Studio opts in to _diagnostics with a /meta query switch and plain reads skip them. Changes /meta parameters and the served body for non-opting callers plus ~a dozen ui/ _diagnostics readers: a contract change that needs contract review.", "C: land #25941, keep #25783 open for the /meta/app residue only (~4.4 ms artifact resolution lookupArtifactItem/foldExtenders, ~7.8 ms fixed request overhead), outside this claim's surface." ], "recommendation": "A, because both numbers fell 2-4x with byte-identical served bodies and no contract surface; B buys the last ~9 ms on /meta/object at the price of a contract change and a ui/ sweep, which is the maintainer's call, not the seat's." } ], "out_of_scope_findings": [ "carrier: #25783 (PR #25941 Acceptance notes) · /meta/app residue ~4.4 ms in artifact resolution (lookupArtifactItem / foldExtenders) and ~7.8 ms fixed per-request overhead, profiled on the showcase rig · noted, not filed (a cost reading, not a class a/b/c defect)" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Ruling: direct ruling (no batch) · item: os-dev-report 6408550085
open_questions[0]· letter A · maintainer 「25783 A」 · recorded bydomain:server#2(seat post #20966, seat sessionsession_01TFnDdepdwyDJeM2kJ4GtMy) · 2026-10-26T05:07ZThe maintainer's words were written 2026-10-26T05:06Z in the project timeline and relayed to this seat verbatim.
Letter A: the maintainer relaxes the acceptance targets to the numbers PR #25941 measured, and the card closes when that PR lands.
/meta/app73.25 → 16.75 ms,/meta/object85.75 → 37 ms, diagnostics share 15.6%. The original ≤15 ms and ≤5% targets are relaxed. They were never met, and nothing is owed against them.Part of #25783becomesCloses #25783, and the card closes when the PR merges. Landing still follows the landing rule in this seat's thread._diagnosticsopt-in switch, a contract change) and C (the/meta/appresidue: artifact resolution ~4.4 ms, fixed overhead ~7.8 ms). If either is wanted later, it is a new card through the filing gate.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim amendment (Clause-② line) ·
domain:server#2· seat sessionsession_01TFnDdepdwyDJeM2kJ4GtMy· 2026-10-26T05:20Z · amends claim 6407697525 (and amendment 6407727649), same session, same branchclaude/project-thread-mvtze8.Clause-②: yes (widening)replacesClause-②: no, for@objectstack/restonly. PR #25941 adds the optional membergetMetaItemsForExecution?to the exportedRestProtocoltype, the list twin ofgetMetaItemForExecution?, which #24666 (6464df3462) declaredyes (widening)and gradedminor. So@objectstack/resttakes its ownminorchangeset carrying the declaration and the before/after export reading.@objectstack/metadata-protocoland@objectstack/runtimestaypatchwithClause-②: no, unless the dev'sdist/index.d.tsreading shows their published surface moved. The declaration face owes aTIER_DEFAULTrecord, which this seat writes on the new head. Every other line of the claim stands.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim amendment (level) ·
domain:server#2· seat sessionsession_01TFnDdepdwyDJeM2kJ4GtMy· 2026-10-26T05:34Z · corrects amendment 6408853730.@objectstack/reststayspatch, ⛔ notminor. Since ruling A on #25105 (2026-10-21) the level follows theMetadata-protocolkind alone (AGENTS.md Post-Task step 3:nonetakespatch; a non-protocol widening shipspatch).check-changeset-no-majorrefuses aminorundernone, as the dev measured. The #24666 precedent predates that rule. TheClause-②: yes (widening)declaration forRestProtocolstands, in its own restpatchchangeset on head22f886e1. Every other line stands.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
ACCEPT → PR #25941 comment 6409381502 ·
domain:server#2· 2026-10-26T06:07Z · declaration-face record PASS 6409368646 · landing through the relay (standing landing word, seat thread 2026-10-26 05:53Z)Generated by Claude Code