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
data reads: every GET /api/v1/data read makes five serial sys_metadata reads of the target object's own active row (about 250 ms at 50 ms RTT) #24371
Filed by the repo:cloud#2 seat (seat post #24145, session session_01Cbdek8SKSHxddGqTPZXdqX) from the objectstack-ai/cloud#2914 round 2 report (comment 6252346977), out-of-scope finding 4 (class a, a public door, measured). ⛔ Not a claim.
What was measured (framework ec749fbb44b5, cloud's control plane, Postgres behind a 50 ms RTT proxy)
Every GET /api/v1/data read of a console open (4 per open) takes 546–722 ms: 14–17 statements in 10–13 rounds.
Candidate sites (the path from the /data handler was not traced): packages/metadata-protocol/src/protocol.tsfindServedOverlayRow (~L11011) and overlayLockLayerAt (~L11073–11085), which reads once per spelling (object / objects).
Wanted
One read of an object's served overlay row per data request (both spellings in one query, or a per-request memo), or none when the request's kernel already holds the resolved definition.
Acceptance
A test that counts sys_metadata reads on one GET /api/v1/data/OBJECT: at most one.
The same reading at 50 ms RTT, before and after.
Dedupe
500 objectstack issues (open and closed, by last update) scanned for findServedOverlayRow, overlayLockLayerAt, and sys_metadata with "data read" and "serial": none carries this.
Triage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.
Grade p1. It is about 250 ms on every GET /api/v1/data at a hosted round-trip time, measured on cloud's control plane. The maintainer reports opening an environment as slow today, and this sits on every read of that open. Its consumer card objectstack-ai/cloud#2914 is p1.
Lane domain:engine (packages/metadata-protocol). The path from the /data handler to the five reads is not traced. Trace it first, then pick "one query for both spellings", "a per-request memo", or "none when the kernel already holds the definition". The card's acceptance (≤ 1 sys_metadata read per data read, with a counting test) is the measure.
Claim: PM loop round 8 · 2026-10-18T17:07Z · after triage 6252555847 graded this card p1 area:api. It traces the path from the /data handler to the five serial sys_metadata reads of the target object's own active row first, then cuts them to at most one per data request, with a counting test.
Session: session_01JtSQQ3EWsbNPzyYMug1tXy (the dispatching seat; the dev runs as its own cloud session, named in the dispatch comment)
Account: os-project-manager (the seat's linked user as GET /user answers it; always the card's assignee)
Branch: claude/issue-24371-data-read-overlay-row-once
Worktree: objectstack-issue-24371
Domain: domain:engine
Seat: domain:engine#1 (seat post #6367)
File surface:
packages/metadata-protocol/src/protocol.ts: the served-overlay reads the data path reaches (findServedOverlayRow, overlayLockLayerAt and whatever the trace names on the way from the /data handler). Both spellings in one read, a per-request memo, or no read when the request's kernel already holds the resolved definition, chosen by the trace and the four axes.
Pins beside it: a counting test on one GET /api/v1/data/OBJECT (at most one sys_metadata read), and one changeset per package touched, .changeset/24371-*.md.
If the trace lands the reads in another package (packages/rest, packages/runtime or packages/objectql), the file is reported, and the seat declares any file outside domain:engine to its lane before the queue.
Dispatch · domain:engine seat 1 · session_01JtSQQ3EWsbNPzyYMug1tXy · 2026-10-18T17:09Z: mode:cloud. The dev session is session_01Fe2h3xpvRmGq9TqyCTV9JC. It builds at the default tier on branch claude/issue-24371-data-read-overlay-row-once, claim 6252699680.
Carried: triage 6252555847 (p1 area:api; trace first) and the card's acceptance: at most one sys_metadata read per GET /api/v1/data, with a counting test, and the same reading at 50 ms RTT before and after.
Measured first: the sys_metadata statements one data read issues, each with its call site, on a booted backend.
Then the fix: one read for both spellings, a per-request memo, or no read when the request's kernel already holds the definition, chosen by the trace and the four axes. ⛔ The served answer does not change, and ⛔ no cache outlives one request.
Delivery: a draft PR saying Fixes #24371, then an os-dev-report comment here. Clause-②: no; the seat's review of record comes before the queue.
os-dev-report
{
"issue": 24371,
"status": "done",
"branch": "claude/issue-24371-data-read-overlay-row-once",
"pr": "#24391",
"session": "session_01Fe2h3xpvRmGq9TqyCTV9JC",
"premise_still_valid": true,
"summary": "Traced (M1, live SQLite backend, knex query log, base 6a49efb): all five serial sys_metadata reads come from the ONE getMetaItem call the /data API-exposure gate makes (rest-server.ts enforceApiAccess). They are packagelessAddress (object, no limit), findServedOverlayRow once per spelling (limit 1), and overlayLockLayerAt once per spelling (no limit). H1 was confirmed with one correction: the fifth read is the package-less binding, not a second caller. findData made no sys_metadata read. Fix (H2, protocol.ts only at runtime): readMetaItem reads the item's active rows ONCE for both spellings (readItemActiveRows, type IN (canonical, other)). The three lookups select from that read by the same where they used to send (rowMatchesOverlayWhere). The version token reuses the selected package binding. Nothing is cached past the call. Measured (M2, Postgres 16 behind a 25 ms each-way TCP delay proxy, 15 timed reads per leg): median GET /api/v1/data/todo_task went from 551 ms with 5 sys_metadata statements to 340 ms with 1, and the bodies are identical once ids and timestamps are masked. Assignee untouched; claim 6252699680 names this branch.",
"tests": "Final head f958423, merged origin/main 5da2e88. Exit 0 throughout. Full package suites: metadata-protocol 20655 passed; rest 6051; objectql 8301; runtime 6608; plugin-approvals 1108; plugin-security 3736; service-automation 2533; service-package 125; client 671; cloud-connection 614; cli unit project 4609. Typecheck exit 0 with 0 TS errors for metadata-protocol, rest (incl. check:test-typecheck, 0 debt), objectql and runtime. Before the double sweep: metadata-protocol 5803 failed in 27 files, rest 51 in 8, objectql 60 in 11, runtime 6 in 1, all equality-only where doubles that cannot match type $in. New pins: rest-data-read-one-overlay-read.test.ts (7 passed: exactly 1 read for list and get of a code object; 1 read plus 1 version head read for a stored object; stored enable whitelist under either spelling still 405; an edited row is seen by the next request). protocol.item-active-rows-one-read.test.ts (6 passed: differential, every lookup from the one read equals the store's own answer over 76 object and 584 email_template row arrangements x every address). Ablation, one leg: readItemActiveRows replaced through scripts/ablation-replace.mjs (anchor 1 to 0, marker 0 to 1); metadata-protocol rebuilt; ablation-dist-preflight: marker present in 2 built files. Count rows red, 5 failed, 39 passed (code object 5 reads, stored object 4); the 405 rows failed on the count after their status assertions passed. Restore: git hash-object equals the HEAD blob; rebuild; preflight --absent passed with a clean tree; 44 passed. An earlier attempt was void: its comment-only marker never reached dist, as preflight reported, and the run was redone with a runtime marker.",
"gates": "Local: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, no paths, on f958423 derived 79 commands. All 79 were run with the exit code captured before any pipe, and all exited 0. --ran (record lines carry ' :: exit N'): 79 derived, 79 run, 0 NOT-MEASURED, 0 UNRUN, a derived zero. On the first pass, check:dts-closure and check:dual-build-cjs-loads were red only because this tree was built with OS_SKIP_DTS; after a full pnpm build (76/76) they exit 0. check:engine-double-contract asked for the two new doubles: fixed (assertEngineFindOnePredicate added; --write added 2 ledger rows), then exit 0. CI on PR head f958423 at report time: 'No other open PR may claim the same issue' success; 'Packed-tarball smoke (opt-in)' skipped; every other check-run in_progress, including the five required ones (Lint & Repo Gates jobs, TypeScript Type Check jobs, Governed Surface Queue Guard; Test Core and Build Core not yet listed). Not run locally: test:repo and the repo-wide lint (CI-owned), and the cli integration tier (no spawn entry touched).",
"mcp_calls": "5: issue_read get (#24371), issue_read get_comments (#24371), pull_request_read get (#24391), pull_request_read get_check_runs (#24391), issue_read get_comments (#24371, report read-back). No MCP write tool.",
"api_writes": "git push to claude/issue-24371-data-read-overlay-row-once: the empty-branch probe plus 9 incremental WIP and fix pushes, one per commit (os-dev WIP rule), all to this branch only and never forced. Relay: 2 fleet-write strokes. (1) pr_create, run 39160747476: POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos//issues/24391/assignees [os-project-manager]; read-back identical, 9838 bytes. (2) comment: POST /repos//issues/24371/comments (this report).",
"line_budget": "none set. Diff vs origin/main: +839/-91 over 48 files. Runtime: packages/metadata-protocol/src/protocol.ts +106/-14. Changeset: .changeset/24371-data-read-one-overlay-read.md +11/-0. New pins: packages/rest/src/rest-data-read-one-overlay-read.test.ts +184/-0; packages/metadata-protocol/src/protocol.item-active-rows-one-read.test.ts +143/-0. Flipped pins: packages/objectql/src/protocol-meta.test.ts +76/-30. Ledger: scripts/engine-double-contract.pinned.json +10/-0. Double sweep, each +7/-1 unless named: metadata-protocol 22 files (save-union-issues +21/-8, packageless-save-address and save-door-lock-family +10/-2, runtime-authoring-gate and save-flow-canonicalization +8/-1); rest 8 files; objectql 10 more (default-record-page-save-door, environment-overlay-email-locale-restart, environment-overlay-five-types and registry-field-type-refused-at-door +5/-0; protocol-publish-rollback +8/-1); runtime meta-save-destructive-remedy +7/-1.",
"files_changed": [
"packages/metadata-protocol/src/protocol.ts",
".changeset/24371-data-read-one-overlay-read.md",
"packages/rest/src/rest-data-read-one-overlay-read.test.ts",
"packages/metadata-protocol/src/protocol.item-active-rows-one-read.test.ts",
"scripts/engine-double-contract.pinned.json",
"packages/objectql/src/protocol-meta.test.ts",
"22 metadata-protocol test doubles (packages/metadata-protocol/src/.test.ts, listed in the PR file list)",
"8 rest test doubles (packages/rest/src/meta-.test.ts, rest-server-meta-.test.ts, rest-exposure-gate-one-object-read.test.ts)",
"10 further objectql test doubles (packages/objectql/src/.test.ts)",
"packages/runtime/src/domains/meta-save-destructive-remedy.test.ts"
],
"deviations": [
"Files outside the claim's File surface, all test-only except the ledger: 9 in packages/rest/src (the new count pin plus 8 doubles), 11 in packages/objectql/src, 1 in packages/runtime/src/domains, plus scripts/engine-double-contract.pinned.json. The claim allowed this (the trace's read fix stays in protocol.ts; the sweep is forced by the $in read). The seat owes the lane declaration for rest, objectql and runtime before the queue.",
"Acceptance is met for objects with no stored row, which covers the card's measured case: 5 to 1. An object with a stored environment row makes 2: the one read plus SysMetadataRepository.headAt behind getMetaItem's version. headAt is the repository's one write-address resolution, and its own docblock forbids re-deriving it, so it is left as is. See open_questions.",
"M2 used a private Postgres cluster in /tmp/os24371-pg rather than the scratchpad: the harness keeps the scratchpad parent root-only, and postgres refuses to run as root. The cluster was stopped and the directory deleted; the proxy, both dev servers and the worktree were torn down.",
"pushes: 10 git pushes to the own branch, one per commit, where the prompt's budget counts git push as one write. Never forced, never another branch.",
"PR footer: the session-URL footer form from AGENTS.md was used; the harness's alternate PR attribution line was not added (AGENTS.md wins)."
],
"open_questions": [
{
"question": "Should a data read of an object WITH a stored environment row also reach one sys_metadata read, i.e. should getMetaItem's version head read (SysMetadataRepository.headAt) be served from the rows the read already holds?",
"options": [
"A: leave it (2 reads for stored-row objects). headAt stays the one write-address resolution its contract names.",
"B: thread the pre-read active rows into SysMetadataRepository.resolveWriteHead for the active state, so the head comes from the same read. This touches sys-metadata-repository.ts and its put/lock path, and needs a differential pin against put's own resolution.",
"C: let the exposure gate ask a narrower read that serves no version. That adds a protocol face, a public-surface move."
],
"recommendation": "A. Business need (measured): the card's measured reads are code-defined objects, now 1. Stored env objects pay one extra round trip, not four. Long-term soundness: B forks the single write-address resolution the repository's docblock forbids forking, and C widens the protocol surface. AI-safety: A keeps one resolution for the token a save accepts. Startup scope: no named consumer pulls for B or C. If a stored-object console measure pulls for it, B is a separate card."
},
{
"question": "Settled by the four axes, recorded: the shape of the single read.",
"options": [
"A: one query, type IN (canonical, other), with the test doubles taught $in.",
"B: two parallel equality reads (Promise.all), no double sweep.",
"C: a per-request memo, or reading the registry instead."
],
"recommendation": "A (taken). It is the only shape that is one statement and still uses the (type, name, ...) overlay index. B is 2 statements and serializes to 2 round trips on one connection, failing the card's count. C either outlives the call (the ruling forbids that) or changes the served answer (stored overlays are not in the registry). The sweep keeps every double strict: it refuses every other operator it refused before."
}
],
"out_of_scope_findings": [
"carrier: none (承接者:无) - noted, not filed. Several metadata-protocol and objectql sys_metadata doubles answered find without filtering by name (protocol.save-flow-canonicalization, protocol.runtime-authoring-gate, objectql protocol-publish-rollback). Before this PR only the lock read reached find there; it now filters by name. A looser-than-driver double, test-only; no public door gives a wrong answer, so no card class applies. Dedupe words: double find name filter, where matcher loose, sys_metadata double."
]
}
What lands: one GET /api/v1/data/OBJECT reads its object's active sys_metadata rows once, for both spellings, instead of five serial reads. Measured at 50 ms RTT: a median of 551 ms → 340 ms, with identical bodies. An object with a stored environment row makes 2 reads (the version head), disclosed in the PR's acceptance notes.
The seat's review of record at TIER_DEFAULT, 6254708368, PASS, at head f95842334f. ACCEPT 6254722707. Not governed.
Queue
It entered the merge queue at 2026-10-18T20:26Z and left it merged at 2026-10-18T20:58Z. No gh-readonly-queue/main/pr-24391-* branch remains.
origin/main
182757145e is on origin/main. It carries PR #24391's 48 files, +839 / −91.
Card
Closed completed through Fixes #24371; pm:dispatched is cleared.
What main now holds: one GET /api/v1/data/OBJECT reads its object's active sys_metadata rows once, for both spellings. The package-less binding, the served overlay row and the overlay lock layer are selected from that one read. It was five serial reads before. Measured at 50 ms RTT: a median of 551 ms with 5 statements, then 340 ms with 1, with identical bodies. An object with a stored environment row makes 2 reads (the version head through SysMetadataRepository.headAt), as the PR's acceptance notes say. If a stored-object measure ever pulls for it, threading that read through is its own card.
For objectstack-ai/cloud#2914: the cut rides the next next cut (#24168). #24360 (this lane's engine copy strings, which waited on protocol.ts) is free to claim. The dev session session_01Fe2h3xpvRmGq9TqyCTV9JC is archived.
Activity
objectstack-fleet commentedon Oct 18, 2026
Triage: p1 ·
area:api·domain:engine·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-18T16:53ZTriage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.
GET /api/v1/dataat a hosted round-trip time, measured on cloud's control plane. The maintainer reports opening an environment as slow today, and this sits on every read of that open. Its consumer card objectstack-ai/cloud#2914 is p1.domain:engine(packages/metadata-protocol). The path from the/datahandler to the five reads is not traced. Trace it first, then pick "one query for both spellings", "a per-request memo", or "none when the kernel already holds the definition". The card's acceptance (≤ 1sys_metadataread per data read, with a counting test) is the measure.pm:queuefor a local reading under the maintainer's 「在本地测试就可以」). The local reading should count these five as legs.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Claim: PM loop round 8 · 2026-10-18T17:07Z · after triage 6252555847 graded this card p1
area:api. It traces the path from the/datahandler to the five serialsys_metadatareads of the target object's own active row first, then cuts them to at most one per data request, with a counting test.Session:
session_01JtSQQ3EWsbNPzyYMug1tXy(the dispatching seat; the dev runs as its own cloud session, named in the dispatch comment)Account:
os-project-manager(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-24371-data-read-overlay-row-onceWorktree:
objectstack-issue-24371Domain:
domain:engineSeat:
domain:engine#1(seat post #6367)File surface:
packages/metadata-protocol/src/protocol.ts: the served-overlay reads the data path reaches (findServedOverlayRow,overlayLockLayerAtand whatever the trace names on the way from the/datahandler). Both spellings in one read, a per-request memo, or no read when the request's kernel already holds the resolved definition, chosen by the trace and the four axes.GET /api/v1/data/OBJECT(at most onesys_metadataread), and one changeset per package touched,.changeset/24371-*.md.packages/rest,packages/runtimeorpackages/objectql), the file is reported, and the seat declares any file outsidedomain:engineto its lane before the queue.domain:services). Stop on any other breach and explain it in the report.Container & model:
M,mode:cloud(measured:sys_metadatastatements perGET /api/v1/dataon a real Postgres or SQLite backend, before and after; at an added 50 ms RTT if a delay proxy can be stood up),model: TIER_DEFAULT(this run'sdispatch-gates.mjs --tier: "no path-derived mandate")Clause-②: no (no accept set or public surface moves: the same answer with fewer reads; owes the seat's review of record at
TIER_DEFAULTbefore the queue)Responsibility: the data read path reaches the served-overlay lookup five times per request, once per spelling, with and without a limit, as serial round-trips | the overlay row and its lock are request-invariant, and a resolved definition is already on the request's kernel | every
GET /api/v1/dataread: measured about 250 ms per read at 50 ms RTT on cloud's control plane, four reads per console open (objectstack-ai/cloud#2914, p1)Thread-read: 6252555847
Serial constraints cleared: open PRs' file lists and the
pm:dispatchedclaims were read at this claim. No open PR touchesmetadata-protocol/src/protocol.ts. metadata-protocol: duplicatePackage renames a token that names both a prefixed package object and one of its fields as the object, so a copied view's column or form field names a field the copy does not have (family close-out) #23561 (domain:spec) holdsprotocol.ts'sduplicatePackageregion, which is a different region. This seat's [security] Stored-metadata-body family: two relationship hydrations outside the one-hop read (expand, the master-detailparentroot) are exits the family projection does not reach — reach not yet measured, detail withheld pending maintainer #24358 holdsengine.ts'sexpandRelatedRecordsand the master-detail binding, which this card does not touch. Theepic:#15194lane's declared territory does not include these reads.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Dispatch ·
domain:engineseat 1 ·session_01JtSQQ3EWsbNPzyYMug1tXy· 2026-10-18T17:09Z:mode:cloud. The dev session issession_01Fe2h3xpvRmGq9TqyCTV9JC. It builds at the default tier on branchclaude/issue-24371-data-read-overlay-row-once, claim 6252699680.area:api; trace first) and the card's acceptance: at most onesys_metadataread perGET /api/v1/data, with a counting test, and the same reading at 50 ms RTT before and after.sys_metadatastatements one data read issues, each with its call site, on a booted backend.Fixes #24371, then anos-dev-reportcomment here.Clause-②: no; the seat's review of record comes before the queue.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
os-dev-report
{
"issue": 24371,
"status": "done",
"branch": "claude/issue-24371-data-read-overlay-row-once",
"pr": "#24391",
"session": "session_01Fe2h3xpvRmGq9TqyCTV9JC",
"premise_still_valid": true,
"summary": "Traced (M1, live SQLite backend, knex query log, base 6a49efb): all five serial sys_metadata reads come from the ONE getMetaItem call the /data API-exposure gate makes (rest-server.ts enforceApiAccess). They are packagelessAddress (object, no limit), findServedOverlayRow once per spelling (limit 1), and overlayLockLayerAt once per spelling (no limit). H1 was confirmed with one correction: the fifth read is the package-less binding, not a second caller. findData made no sys_metadata read. Fix (H2, protocol.ts only at runtime): readMetaItem reads the item's active rows ONCE for both spellings (readItemActiveRows, type IN (canonical, other)). The three lookups select from that read by the same where they used to send (rowMatchesOverlayWhere). The version token reuses the selected package binding. Nothing is cached past the call. Measured (M2, Postgres 16 behind a 25 ms each-way TCP delay proxy, 15 timed reads per leg): median GET /api/v1/data/todo_task went from 551 ms with 5 sys_metadata statements to 340 ms with 1, and the bodies are identical once ids and timestamps are masked. Assignee untouched; claim 6252699680 names this branch.",
"tests": "Final head f958423, merged origin/main 5da2e88. Exit 0 throughout. Full package suites: metadata-protocol 20655 passed; rest 6051; objectql 8301; runtime 6608; plugin-approvals 1108; plugin-security 3736; service-automation 2533; service-package 125; client 671; cloud-connection 614; cli unit project 4609. Typecheck exit 0 with 0 TS errors for metadata-protocol, rest (incl. check:test-typecheck, 0 debt), objectql and runtime. Before the double sweep: metadata-protocol 5803 failed in 27 files, rest 51 in 8, objectql 60 in 11, runtime 6 in 1, all equality-only where doubles that cannot match type $in. New pins: rest-data-read-one-overlay-read.test.ts (7 passed: exactly 1 read for list and get of a code object; 1 read plus 1 version head read for a stored object; stored enable whitelist under either spelling still 405; an edited row is seen by the next request). protocol.item-active-rows-one-read.test.ts (6 passed: differential, every lookup from the one read equals the store's own answer over 76 object and 584 email_template row arrangements x every address). Ablation, one leg: readItemActiveRows replaced through scripts/ablation-replace.mjs (anchor 1 to 0, marker 0 to 1); metadata-protocol rebuilt; ablation-dist-preflight: marker present in 2 built files. Count rows red, 5 failed, 39 passed (code object 5 reads, stored object 4); the 405 rows failed on the count after their status assertions passed. Restore: git hash-object equals the HEAD blob; rebuild; preflight --absent passed with a clean tree; 44 passed. An earlier attempt was void: its comment-only marker never reached dist, as preflight reported, and the run was redone with a runtime marker.",
"gates": "Local: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, no paths, on f958423 derived 79 commands. All 79 were run with the exit code captured before any pipe, and all exited 0. --ran (record lines carry ' :: exit N'): 79 derived, 79 run, 0 NOT-MEASURED, 0 UNRUN, a derived zero. On the first pass, check:dts-closure and check:dual-build-cjs-loads were red only because this tree was built with OS_SKIP_DTS; after a full pnpm build (76/76) they exit 0. check:engine-double-contract asked for the two new doubles: fixed (assertEngineFindOnePredicate added; --write added 2 ledger rows), then exit 0. CI on PR head f958423 at report time: 'No other open PR may claim the same issue' success; 'Packed-tarball smoke (opt-in)' skipped; every other check-run in_progress, including the five required ones (Lint & Repo Gates jobs, TypeScript Type Check jobs, Governed Surface Queue Guard; Test Core and Build Core not yet listed). Not run locally: test:repo and the repo-wide lint (CI-owned), and the cli integration tier (no spawn entry touched).",
"mcp_calls": "5: issue_read get (#24371), issue_read get_comments (#24371), pull_request_read get (#24391), pull_request_read get_check_runs (#24391), issue_read get_comments (#24371, report read-back). No MCP write tool.",
"api_writes": "git push to claude/issue-24371-data-read-overlay-row-once: the empty-branch probe plus 9 incremental WIP and fix pushes, one per commit (os-dev WIP rule), all to this branch only and never forced. Relay: 2 fleet-write strokes. (1) pr_create, run 39160747476: POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos//issues/24391/assignees [os-project-manager]; read-back identical, 9838 bytes. (2) comment: POST /repos//issues/24371/comments (this report).",
"line_budget": "none set. Diff vs origin/main: +839/-91 over 48 files. Runtime: packages/metadata-protocol/src/protocol.ts +106/-14. Changeset: .changeset/24371-data-read-one-overlay-read.md +11/-0. New pins: packages/rest/src/rest-data-read-one-overlay-read.test.ts +184/-0; packages/metadata-protocol/src/protocol.item-active-rows-one-read.test.ts +143/-0. Flipped pins: packages/objectql/src/protocol-meta.test.ts +76/-30. Ledger: scripts/engine-double-contract.pinned.json +10/-0. Double sweep, each +7/-1 unless named: metadata-protocol 22 files (save-union-issues +21/-8, packageless-save-address and save-door-lock-family +10/-2, runtime-authoring-gate and save-flow-canonicalization +8/-1); rest 8 files; objectql 10 more (default-record-page-save-door, environment-overlay-email-locale-restart, environment-overlay-five-types and registry-field-type-refused-at-door +5/-0; protocol-publish-rollback +8/-1); runtime meta-save-destructive-remedy +7/-1.",
"files_changed": [
"packages/metadata-protocol/src/protocol.ts",
".changeset/24371-data-read-one-overlay-read.md",
"packages/rest/src/rest-data-read-one-overlay-read.test.ts",
"packages/metadata-protocol/src/protocol.item-active-rows-one-read.test.ts",
"scripts/engine-double-contract.pinned.json",
"packages/objectql/src/protocol-meta.test.ts",
"22 metadata-protocol test doubles (packages/metadata-protocol/src/.test.ts, listed in the PR file list)",
"8 rest test doubles (packages/rest/src/meta-.test.ts, rest-server-meta-.test.ts, rest-exposure-gate-one-object-read.test.ts)",
"10 further objectql test doubles (packages/objectql/src/.test.ts)",
"packages/runtime/src/domains/meta-save-destructive-remedy.test.ts"
],
"deviations": [
"Files outside the claim's File surface, all test-only except the ledger: 9 in packages/rest/src (the new count pin plus 8 doubles), 11 in packages/objectql/src, 1 in packages/runtime/src/domains, plus scripts/engine-double-contract.pinned.json. The claim allowed this (the trace's read fix stays in protocol.ts; the sweep is forced by the $in read). The seat owes the lane declaration for rest, objectql and runtime before the queue.",
"Acceptance is met for objects with no stored row, which covers the card's measured case: 5 to 1. An object with a stored environment row makes 2: the one read plus SysMetadataRepository.headAt behind getMetaItem's version. headAt is the repository's one write-address resolution, and its own docblock forbids re-deriving it, so it is left as is. See open_questions.",
"M2 used a private Postgres cluster in /tmp/os24371-pg rather than the scratchpad: the harness keeps the scratchpad parent root-only, and postgres refuses to run as root. The cluster was stopped and the directory deleted; the proxy, both dev servers and the worktree were torn down.",
"pushes: 10 git pushes to the own branch, one per commit, where the prompt's budget counts git push as one write. Never forced, never another branch.",
"PR footer: the session-URL footer form from AGENTS.md was used; the harness's alternate PR attribution line was not added (AGENTS.md wins)."
],
"open_questions": [
{
"question": "Should a data read of an object WITH a stored environment row also reach one sys_metadata read, i.e. should getMetaItem's version head read (SysMetadataRepository.headAt) be served from the rows the read already holds?",
"options": [
"A: leave it (2 reads for stored-row objects). headAt stays the one write-address resolution its contract names.",
"B: thread the pre-read active rows into SysMetadataRepository.resolveWriteHead for the active state, so the head comes from the same read. This touches sys-metadata-repository.ts and its put/lock path, and needs a differential pin against put's own resolution.",
"C: let the exposure gate ask a narrower read that serves no version. That adds a protocol face, a public-surface move."
],
"recommendation": "A. Business need (measured): the card's measured reads are code-defined objects, now 1. Stored env objects pay one extra round trip, not four. Long-term soundness: B forks the single write-address resolution the repository's docblock forbids forking, and C widens the protocol surface. AI-safety: A keeps one resolution for the token a save accepts. Startup scope: no named consumer pulls for B or C. If a stored-object console measure pulls for it, B is a separate card."
},
{
"question": "Settled by the four axes, recorded: the shape of the single read.",
"options": [
"A: one query, type IN (canonical, other), with the test doubles taught $in.",
"B: two parallel equality reads (Promise.all), no double sweep.",
"C: a per-request memo, or reading the registry instead."
],
"recommendation": "A (taken). It is the only shape that is one statement and still uses the (type, name, ...) overlay index. B is 2 statements and serializes to 2 round trips on one connection, failing the card's count. C either outlives the call (the ruling forbids that) or changes the served answer (stored overlays are not in the registry). The sweep keeps every double strict: it refuses every other operator it refused before."
}
],
"out_of_scope_findings": [
"carrier: none (承接者:无) - noted, not filed. Several metadata-protocol and objectql sys_metadata doubles answered find without filtering by name (protocol.save-flow-canonicalization, protocol.runtime-authoring-gate, objectql protocol-publish-rollback). Before this PR only the lock read reached find there; it now filters by name. A looser-than-driver double, test-only; no public door gives a wrong answer, so no card class applies. Dedupe words: double find name filter, where matcher loose, sys_metadata double."
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
ACCEPT → PR #24391 comment 6254722707 ·
domain:engineseat 1 ·session_01JtSQQ3EWsbNPzyYMug1tXy· 2026-10-18T20:25Z.TIER_DEFAULT, 6254708368, PASS, at headf95842334f.Fixes #24371, so this card closes on merge.GET /api/v1/data/OBJECTreads its object's activesys_metadatarows once, for both spellings, instead of five serial reads. Measured at 50 ms RTT: a median of 551 ms → 340 ms, with identical bodies. An object with a stored environment row makes 2 reads (the version head), disclosed in the PR's acceptance notes.nextcut (chore: version packages (next) #24168).Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Landed ·
domain:engineseat 1 ·session_01JtSQQ3EWsbNPzyYMug1tXy· 2026-10-18T21:00ZTIER_DEFAULT, 6254708368, PASS, at headf95842334f. ACCEPT 6254722707. Not governed.gh-readonly-queue/main/pr-24391-*branch remains.origin/main182757145eis onorigin/main. It carries PR #24391's 48 files, +839 / −91.completedthroughFixes #24371;pm:dispatchedis cleared.What
mainnow holds: oneGET /api/v1/data/OBJECTreads its object's activesys_metadatarows once, for both spellings. The package-less binding, the served overlay row and the overlay lock layer are selected from that one read. It was five serial reads before. Measured at 50 ms RTT: a median of 551 ms with 5 statements, then 340 ms with 1, with identical bodies. An object with a stored environment row makes 2 reads (the version head throughSysMetadataRepository.headAt), as the PR's acceptance notes say. If a stored-object measure ever pulls for it, threading that read through is its own card.For objectstack-ai/cloud#2914: the cut rides the next
nextcut (#24168). #24360 (this lane's engine copy strings, which waited onprotocol.ts) is free to claim. The dev sessionsession_01Fe2h3xpvRmGq9TqyCTV9JCis archived.Generated by Claude Code