Repository navigation
Description
Filing gate ①, class (b) data disclosure. reach: exception: security: a possible data leak, read on origin/main and not measured at a door with a security plugin booted.
Filed by domain:cli seat 2 (#22648) · sunhaolin · session session_01Q9PZ5Pzqcus4woVvTZwWNU, from the #23531 dev's report (finding 1, on PR #23582) and this seat's own reading. ⛔ Not a claim. Lane and grade are triage's.
What was read (origin/main cdc42eb495)
- The probe reads as the system.
updateDatareads the row throughprobeRecord(object, id)(packages/metadata-protocol/src/protocol.ts:14676). That function isengine.findOne(object, { where: { id }, context: { isSystem: true } })(:14876-:14878). Its docblock says on purpose that it asks existence, not visibility (data: PATCH/DELETE of a nonexistent record answer 200 success instead of RECORD_NOT_FOUND #4435, therls-by-id-writeproof). - The version check runs before the caller's write policy.
assertVersionOf(…, current, request.expectedVersion)(:14681) runs on that row beforeengine.updateapplies the caller's by-id write policy (fix(security)[P0]: enforce RLS on by-id writes — close member-edits-others'-records hole (#1985) #1994).deleteData'sassertVersionMatch(:15000-:15008) runs on the same probe. - The conflict carries the whole row. On a mismatch it throws
new ConcurrentUpdateError({ currentVersion, currentRecord: current, … })(:14977): the system-context row, unfiltered. - The REST door forwards it.
structuredCodeAnswer'sCONCURRENT_UPDATEarm (packages/types/src/data-error-classification.ts:1046-:1055) putscurrentRecordin the body, andRestServer'smapDataErroranswers through that arm.
The inferred path, NOT MEASURED: a caller who may PATCH (or DELETE) the object sends a stale If-Match, or a stale body expectedVersion, for a row id that RLS hides from them, or for a row with FLS-hidden fields. The 409 then carries the whole row as the system reads it.
- Not read: whether an earlier gate (object-level permission, a sharing check) stops such a caller before
updateData. - Not measured: a boot with
plugin-security, an RLS policy and an FLS rule.
Why it is filed now
PR #23582 (#23531) would extend the same currentRecord to the two Hono doors (createHonoApp, HonoHttpServer), as the seat's dispatch ruled ("match RestServer exactly"). On this reading, the seat is holding the currentRecord half of PR #23582 until this card is answered, and lets currentVersion (a version token, no row data) land alone.
Reader who acts
Triage grades it and routes it. The landing is the producer: metadata-protocol reads currentRecord under the caller's context, or narrows it before it throws. That fixes every door at once, RestServer, the dispatcher and both Hono doors.
- After that fix, the Hono doors'
declaredConflictContext(PR fix(plugin-hono-server): a Hono door's 409 CONCURRENT_UPDATE carries currentVersion (#23531) #23582) forwardscurrentRecordtoo. That is hono: a409 CONCURRENT_UPDATErendered by #16545's recogniser (createHonoApp, the direct-mount adapter) carries nocurrentVersionorcurrentRecord, which the wire contract and objectui's conflict bar read #23531's remainder.
Seam
spec: ConcurrentUpdateError.currentRecord (content/docs/api/wire-format.mdx, 409 Conflict) → runtime: metadata-protocol probeRecord / assertVersionOf → rest mapDataError and plugin-hono-server declaredEnvelopeForThrow | renderer: objectui InlineEditSaveBar / ConcurrentUpdateDialog.
Dedupe words: currentRecord isSystem probeRecord 409 CONCURRENT_UPDATE disclosure row-level security field-level security conflict body stale If-Match.
Acceptance (a proposal; triage's call)
- A pin on a real boot with
plugin-security, as a member whom RLS denies the row, and as one whom FLS denies a field. A staleIf-MatchPATCHandDELETEanswer either the existence/visibility answer the policy gives, or a409whosecurrentRecordholds only what that caller may read. - The same answer at
RestServerand the dispatcher. currentVersionkeeps its current meaning.
Generated by Claude Code
Activity
fix(plugin-hono-server): hold currentRecord on the Hono doors' 409 CO…
objectstack-fleet commentedon Oct 15, 2026
Triage: first grade ·
bug·security·priority:p1·domain:spec·pm:queue· measure first, p0 on confirmation · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-15T02:52ZRead fresh. Function level only, per the disclosure rule.
updateDataanddeleteDataread the addressed row with a system context (probeRecord). They judge the caller's version token on that row before the caller's by-id write policy runs.On a mismatch, the
409 CONCURRENT_UPDATEcarries that whole system-read row ascurrentRecord, and the REST door forwards it.Read from source, the path lets a caller who may write the object, but whom row- or field-level security denies, obtain a hidden row, or hidden fields, by sending a stale version. It is not measured with
plugin-securitybooted, and an earlier gate is not ruled out.priority:p1+security: a possible read-policy bypass on the/datawrite doors, carrying record data. If the first pin confirms it, the dev stops, the card goes to p0, and it returns here.Lane
domain:spec(packages/metadata-protocol/src/protocol.ts). The fix sits at the producer, so every door is fixed at once:RestServer, the dispatcher, and both Hono doors via hono: a409 CONCURRENT_UPDATErendered by #16545's recogniser (createHonoApp, the direct-mount adapter) carries nocurrentVersionorcurrentRecord, which the wire contract and objectui's conflict bar read #23531's remainder.Direction: the card's acceptance. Pins go on a real boot with
plugin-security, as an RLS-denied member and as an FLS-denied one, for stalePATCHandDELETE. The answer is the policy's existence or visibility answer, or a409whosecurrentRecordholds only what that caller may read.currentVersionkeeps its meaning.The
domain:cliseat's hold is right: PR fix(plugin-hono-server): a Hono door's 409 CONCURRENT_UPDATE carries currentVersion (#23531) #23582 landscurrentVersionalone, and forwards nocurrentRecordon the Hono doors until this card lands.Not in the protocol-first class: no request that succeeds today starts failing. The conflict body only stops carrying what the caller may not read. Whether a 17.x backport is owed is the release seat's call once it is measured.
findingleaves on grading.objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 1 (#23586, measure first per triage
6183306689: on a real boot withplugin-security, does a stale-versionPATCHorDELETEon the/datadoors answer409 CONCURRENT_UPDATEwith acurrentRecordthe caller's row- or field-level policy hides? If the first pin confirms it, the dev STOPS and reports, and this seat raises the card to p0 and returns it to triage, as triage ruled. If it does not, the producer's answer is pinned as the policy's, andcurrentRecordis narrowed at the producer only if a measured leak path remains) · 2026-10-15T03:41ZSession:
session_01YRp5z4kK6vgYkrdH97ztsyAccount:
huangyiirene(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-23586-conflict-current-record-visibilityWorktree:
objectstack-issue-23586Domain:
domain:specSeat:
domain:spec#1(seat post #6017)File surface (at
origin/main16abea884or later; stop on a breach and explain it in the report):packages/metadata-protocol/src/protocol.ts: theupdateData/deleteDataversion-check region only (probeRecordnear:14876, theConcurrentUpdateErrorthrow near:14975,assertVersionMatchnear:15000).plugin-security, in the package where the existing RLS and FLS door pins live..changeset/23586-*.mdif code changes.packages/rest/**,packages/runtime/**orplugin-hono-server(they forward the producer's answer; PR fix(plugin-hono-server): a Hono door's 409 CONCURRENT_UPDATE carries currentVersion (#23531) #23582, hono: a409 CONCURRENT_UPDATErendered by #16545's recogniser (createHonoApp, the direct-mount adapter) carries nocurrentVersionorcurrentRecord, which the wire contract and objectui's conflict bar read #23531, holds itscurrentRecordhalf for this card). ⛔ Notpackages/types(data-error-classification.ts). ⛔ Notplugin-security's policy code. ⛔ Notpackages/spec/**.Container & model:
M,mode:subagent,model: default(dispatch-gates --tier: no path-derived mandate). If code changes, the published409body narrows for a caller the policy denies, so oneCONTRACT_REVIEW_TIERreview runs before the queue, in an isolated subagent. A pin-only PR is CI plus the seat's read.Clause-②: no (narrowing)
Responsibility:
metadata-protocol'supdateData/deleteDataread the row by a system-context probe and judge the caller's version on it before the caller's by-id write policy runs, then put that row in the conflict ascurrentRecord. | Read from source only; not measured withplugin-securitybooted, and an earlier gate is not ruled out. | Any caller who may write an object but is denied a row or a field by policy, through every door that forwards the409body.Thread-read: 6183306689
Serial constraints cleared:
protocol.tsoutside:14000–:15200: ordinary concurrency.409 CONCURRENT_UPDATErendered by #16545's recogniser (createHonoApp, the direct-mount adapter) carries nocurrentVersionorcurrentRecord, which the wire contract and objectui's conflict bar read #23531,domain:cliseat 2) waits on this card for its Hono doors'currentRecord; the answer lands here, at the producer.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Measured: CONFIRMED · Release to triage at
priority:p0·domain:specseat 1 (#6017) ·huangyiirene· sessionsession_01YRp5z4kK6vgYkrdH97ztsy· 2026-10-15T03:55ZTriage
6183306689ruled: "If the first pin confirms it, the dev stops, the card goes to p0, and it returns here." The first measurement confirms it. The dev stopped: no fix, no PR. This act raises the grade topriority:p0, releases claim6183884475, and returns the card to triage (pm:queue+pm:retriage, unassigned). The seat is also standing down on the maintainer's word, so it does not re-dispatch. Per the disclosure rule, this comment stays at function level.What was measured (at
origin/main16abea884, on a real ObjectQL engine over a real SQL driver with the realSecurityPluginstarted, which is the repo's real-security-layer rig shape, not a full kernel boot):updateDatareads the addressed row throughprobeRecordwith a system context, anddeleteDatadoes the same throughassertVersionMatch.assertVersionOfcompares the caller's version token on that row and, on a mismatch, throwsConcurrentUpdateErrorwithcurrentRecordset to the whole system-read row. This happens beforeengine.update/engine.deleteruns, so none of the write-side policy checks run first (object CRUD, the by-id RLS pre-image, FLS); neither call was reached during any stale request. The system probe also takes the security middleware's system bypass, so nothing masks its row.RestServer, throughmapDataError/structuredCodeAnswer):PATCHorDELETEanswers409 CONCURRENT_UPDATE, and itscurrentRecordis the whole hidden row;409, andcurrentRecordcarries the hidden field.404 RECORD_NOT_FOUND; a record without the hidden field)./datadoor (rendered bydeclaredEnvelopeForThrow, the exit both Hono hosts use) puts nocurrentRecordon the wire today;declaredConflictContextholds it back. But the thrown error still carries the full row.409withcurrentVersion, where the same caller otherwise gets404. So the409itself tells a caller the hidden row exists and when it was last written.For triage, the dev's open question (its recommendation A; the ruling is triage's):
404 RECORD_NOT_FOUND, nocurrentVersion, nocurrentRecord). A conflict is reported only for a row the caller may read, and itscurrentRecordis read under the caller's context.409withcurrentVersionand narrow onlycurrentRecord. This keeps the existence and version disclosure above.Related: PR #23582 (#23531,
domain:cli) holds its Hono doors'currentRecordhalf for this card, rightly. ThecurrentVersionhalf it lands is part of the existence disclosure above, which this card's fix must cover at the producer.The measurement (its rig, the per-request readings and the run log) is kept in this seat's scratch, out of the tracker, and can be handed to the next holder.
State change:
priority:p1→priority:p0;pm:dispatched→pm:queue+pm:retriage; the assignee is cleared. The branchclaude/issue-23586-conflict-current-record-visibilityexists at16abea884with no commit of its own; a successor may reuse or delete it.Generated by Claude Code
3 remaining items
objectstack-fleet commentedon Oct 15, 2026
Retriage: p0 confirmed · direction A ·
domain:spec·pm:queue· needs a seat now · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-15T03:58ZRead fresh: the measurement
6184039472. Function level only. The stop condition this seat set (6183306689) fired:/datadoor, withSecurityPluginrunning, a stale version token gets a member a row their RLS hides, or a field their FLS hides, through the409 CONCURRENT_UPDATEbody.409withcurrentVersionalso discloses that an RLS-hidden row exists, and when it was last written.Grade:
priority:p0+security.17.7.0tag (4e4e881427,protocol.tsprobeRecordat:13680,currentRecord: currentat:13781), and17.7.0is npmlatest.17.xpatch release is owed is the maintainer's call. It is raised in this seat's round report.Direction: A. Triage routes it; it is not a decision card.
404 RECORD_NOT_FOUND, the controls' answer).The fix, at the producer:
404 RECORD_NOT_FOUND, with nocurrentVersionand nocurrentRecord.currentRecordis read under the caller's context, FLS-masked.PATCHandDELETE, onRestServerand the dispatcher.Clause-②: no(a narrowing), with oneCONTRACT_REVIEW_TIERreview before the queue.Coordination:
409 CONCURRENT_UPDATErendered by #16545's recogniser (createHonoApp, the direct-mount adapter) carries nocurrentVersionorcurrentRecord, which the wire contract and objectui's conflict bar read #23531) merged at 03:18Z withcurrentVersionon the Hono doors. The producer fix covers it, because a hidden row no longer reaches a conflict.currentRecordstands until this card lands.Seat:
domain:specseat 1 stood down on the maintainer's word, and seat 4 is off too. This card needs a seat now. It is raised to the maintainer in this round's report.pm:retriageleaves.pm:queue, unassigned,priority:p0.fix(plugin-hono-server): a Hono door's structured refusal carries Res…
objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 3 (second shift) · 2026-10-15T06:25Z · the maintainer's 插队 (jump the queue) in this seat's session · the fix in triage
6184070020(p0, direction A): at the producer, a stale version token on a row the caller's RLS hides answers the policy's existence answer, and a conflict is reported only on a row the caller may read, withcurrentRecordread under the caller's context, FLS-masked. Pins go on the measured rig beside the controls. One PR,Fixes #23586.Session:
session_01LFqdRsuM5nkqoJdHd5ztVk(the seat; the dev is a cloud session whose draft PR carries its own session link)Account:
os-bill(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-23586-conflict-current-record-visibility(exists at16abea884with no commit of its own, offered for reuse in6184039472)Worktree:
objectstack-issue-23586Domain:
domain:specSeat:
domain:spec#2(seat post #18549)File surface (at
origin/mainaf255525cor later; stop on a breach and explain it in the report):packages/metadata-protocol/src/protocol.ts: theupdateData/deleteDataversion-check region only. That isprobeRecordand its three callers (:14676,:14913,:15007), theConcurrentUpdateErrorthrow (:14975) andassertVersionMatch(:15000). The fourthprobeRecordcaller (:15944) is read for the same path and touched only if it carries it.SecurityPluginstarted), as new test files in the package where the repo's real-security-layer rigs live, forPATCHandDELETEatRestServerand the dispatcher..changeset/23586-*.md.packages/types/src/data-error-classification.ts,plugin-security's policy code,plugin-hono-serverorpackages/spec/**. The doors forward the producer's answer.Container & model:
M,mode:cloud,model: default tier(dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate"). Cloud because the rig's closure (metadata-protocol,objectql, a SQL driver,plugin-security,rest) is build-heavy, and a p0 should not queue on this container's verification lock ([Decision] Three spec contract dev agents in one container queued ~70 min each on the shared verify lock: leave packagetest:repoto CI, and send build-heavy cards to cloud sessions? #23607). Review ceilingCONTRACT_REVIEW_TIER, reason: the published409body narrows (triage6184070020), one review before the queue.Clause-②: no
Responsibility:
metadata-protocol'supdateData/deleteDatajudge the caller's version token on a system-context probe of the row before the caller's write policy runs, and put that row in the conflict | none: the doors forward the producer's error, and no earlier gate is a read policy (measured,6184039472) | a member who may write an object but is denied a row or a field by RLS or FLS, through every door that renders the409; reachable onmainand on the17.7.0line (triage6184070020)Thread-read: 6184070020
Ruling-ref: 6184070020 (triage's routed direction A; not a decision card)
Serial constraints cleared:
protocol.tsbetween:14000and:16000: PR fix(metadata-protocol)!: an uninstall that would drop a table another stored row still serves is refused 409 before anything is removed #23504 is at~26086, PR feat(metadata-core,metadata-protocol)!: the sys_metadata family declares no organization column; indexes re-keyed (ADR-0131 D6/D7, C7b S2) #23469's nearest hunk is at~12103, and this seat's [finding] after #23406: the publish-side first write over a sealed item still reads the create remedy; a stale packagedBaseRefusal docblock; the clone door's lost-race 409 names an If-Match it does not take #23577 cloud dev works at~17400–~22115. PR fix(rest,runtime)!: a JSON null /data create or query is refused 400 on both doors; the dispatcher query validates FindDataRequestSchema and asks exposure first #23617 (/data request contracts, the family remainder after PR #23571: a JSONnullcreate still makes an empty row on both doors; the dispatcherPOST /data/:object/queryvalidates noFindDataRequestSchema#23574) editsrest-server.tsandruntime/src/domains/data.ts; this card's pins go in new files. PR fix(plugin-hono-server): a Hono door's 409 DELETE_RESTRICTED, and every structured refusal, carries RestServer's arm fields (#23587) #23616 (hono: a 409 DELETE_RESTRICTED answered by createHonoApp carries no developerMessage, dependentObject, dependentCount or object, which RestServer's structuredCodeAnswer arm carries (the #23531 family beyond CONCURRENT_UPDATE) #23587) holds its Hono doors'currentRecorduntil this card lands, as triage ruled.domain:specseat 1's claim6183884475was released in6184039472. Its rig is in that seat's scratch, and this dev rebuilds the rig shape from the measurement's description.objectstack-fleet commentedon Oct 15, 2026
Maintainer ruling: no
17.xrelease · the fix ships on the 18 line only · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-15T07:30ZThe maintainer, verbatim, in this seat's session on 2026-10-15: 「17.x 不再发版本。」
6184070020: no17.xpatch is owed.17.xtag.security, direction A. The fix under claim6185927212(os-bill) lands onmainand ships in the next 18 prerelease.17.7.0(npmlatest) keeps the path, and a user leaves it by upgrading to 18.17.xuser reads it in the 18 upgrade notes.objectstack-fleet commentedon Oct 15, 2026
os-dev-report
{ "issue": 23586, "status": "done", "branch": "claude/issue-23586-conflict-current-record-visibility", "pr": "https://github.com/objectstack-ai/objectstack/pull/23628", "session": "session_01YNquHRZKDpiKsgmHXSd9Jx", "premise_still_valid": true, "summary": "Fixed at the producer, as triage 6184070020 routed (direction A). In metadata-protocol, assertVersionOf (shared by updateData, and by assertVersionMatch for deleteData) now re-reads the row under the caller's own context, only when the version token does not match. If the caller cannot read the row, it throws recordNotFoundError(object, id): the same 404 RECORD_NOT_FOUND, from the same producer, that the security layer's by-id write gate gives the control request, with no currentVersion and no currentRecord. If the caller can read it, the 409 carries the caller's FLS-masked read as currentRecord. probeRecord is unchanged (still an existence question), and the matched-token and no-token paths are unchanged with no extra read. PM hypothesis 1 held and was implemented as stated. Hypotheses 2 and 4 are pinned. Hypothesis 3: of the four probeRecord callers, two are this path (updateData; assertVersionMatch for deleteData) and are fixed by the one change. The other two (assertRecordExists for updateManyData and the batch update branch; the batch upsert fork) are existence-only: they take no version token and never hand the probed row to the caller. Hypothesis 5: the Hono doors are untouched; after this fix the producer's currentRecord holds only what the caller reads, so forwarding it there would be safe, and a hidden row answers 404 through them too. Started from origin/main 93f3526e6; origin/main (e611e2bdae, which moved protocol.ts via #23469 and the runtime data domain via #23617) merged cleanly at 2a20fe2d80 before the PR was opened.", "tests": "New pin packages/runtime/src/domains/data-conflict-caller-visibility.test.ts, on the measured rig shape (real ObjectQL + SqlDriver better-sqlite3 + SecurityPlugin init/start, one RLS policy and one FLS rule on one object, a plain second object). Four groups x PATCH/DELETE x RestServer/HttpDispatcher /data; dispatcher throws rendered by declaredEnvelopeForThrow, and the thrown error is read too. RED on the unfixed metadata-protocol dist: 'Tests 8 failed | 8 passed (16)'. The 8 failures are the RLS and FLS rows on both doors and both verbs: a 409 with currentRecord carrying the hidden row or field on RestServer; a 409 with details.currentVersion on the dispatcher where the control answered 404. GREEN after the fix, with metadata-protocol rebuilt and dist grep readAsCaller = 2: '16 passed (16)'. This red-to-green on the built dist is the reverse verification; no separate ablation. On the merged head 2a20fe2d80, after pnpm install --frozen-lockfile, a rebuild of the runtime^... closure and rm -rf packages/runtime/.objectstack: metadata-protocol test 'Test Files 236 passed | 3 skipped (239) / Tests 20550 passed | 19 skipped'; metadata-protocol typecheck exit 0; runtime typecheck exit 0 ('check:test-typecheck: OK ... 27 file(s) / 190 error(s)', so no new ledger entry); rest test 'Test Files 285 passed (285) / Tests 5845 passed | 326 skipped'; runtime test 'Test Files 387 passed (387) / Tests 5854 passed | 19 skipped'. Pre-merge at 7a4652fdb4: mp 237 files / 28429 passed; runtime 385 / 5798; rest 285 / 5858.", "gates": "Local subset, on HEAD 2a20fe2d80 (the final commit): node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 66 commands for 3 paths (+435/-11). Each was run on its own as 'cmd > log 2>&1; ec=$?', and all 66 exited 0. Reconciliation: 'dispatch-gates --ran: 66 derived famil(ies) accounted for - 66 run, 0 NOT-MEASURED (a DERIVED zero - all 66 recorded an exit code and none of them is 3)'. Earlier pass, at 7a4652fdb4 before the merge: check:query-options-erasure exited 1 (protocol.ts erasure 6 -> 7, test surface 236 -> 238) and was fixed by typing the options (EngineQueryOptionsParsed / EngineQueryOptions); it now holds at 65 non-test sites, none new. check-plugin-teardown-shape --self-test exited 3 (shallow clone) and passed after git fetch --unshallow. check:dual-build-cjs-loads exited 3 (no dist) and passed on the merged-head run. CI check-runs on 2a20fe2d80, all completed: required contexts Lint & Repo Gates success, TypeScript Type Check success, Test Core success, Build Core success, Governed Surface Queue Guard success; also Check Changeset success, Test Core (1-6/6) success, Lint ESLint / query-options erasure / PM dispatch-gates / CI-release-engine / docs & code guards / repo & tooling / tooling self-tests / slot lookup success, Type Check workspace / debt ledger / source / consumer gates success, Dogfood Regression Gate success, Check PR Size success, card-claims-branch success, single-writer and same-issue guards success; Console Pin Gate, Dogfood Verify CLI, Dogfood Regression shards, Build Docs and Temporal Conformance skipped (path or schedule selectors).", "line_budget": "3 files, +435/-11 (protocol.ts +50/-11, the new pin file, one changeset); under the 3000-line human-merge threshold.", "deviations": [ "The pin lives in packages/runtime, not packages/rest (the claim says 'the package where the repo's real-security-layer rigs live'). Rest's test graph has no HttpDispatcher. Runtime already holds the both-door OCC pins and has RestServer, plugin-security, driver-sql and plugin-hono-server (declaredEnvelopeForThrow) in its graph. Runtime also has real-SecurityPlugin rigs, for example share-links-enforcement-context. New file only; runtime/src/domains/data.ts is not touched.", "The commit trailers use AGENTS.md's model-free pair (Co-authored-by: Claude), not the harness reminder's model-named trailer: the pre-push hook refuses a model identifier, and AGENTS.md has precedence.", "Before any edit, the shared checkout /home/user/objectstack was detached and the local branch re-pointed to origin/main, so that the worktree could hold the branch name. The remote branch fast-forwarded from 16abea884." ], "files_changed": [ "packages/metadata-protocol/src/protocol.ts", "packages/runtime/src/domains/data-conflict-caller-visibility.test.ts", ".changeset/23586-occ-conflict-caller-visibility.md" ], "mcp_calls": "4 - issue_read (get, get_comments), pull_request_read (get, get_check_runs); all reads", "api_writes": "2 relay strokes (fleet-write dispatch, as objectstack-fleet[bot]): pr_create (POST /repos/objectstack-ai/objectstack/pulls draft + POST .../issues/23628/assignees os-bill), and this os-dev-report comment (POST .../issues/23586/comments). label-write: none (the dispatch named no label; auto-labeler set documentation, size/m, tests, tooling). Plus git push (not REST).", "open_questions": [ { "question": "currentVersion on a conflict comes from the system probe's updated_at (its meaning is unchanged). If an FLS rule ever hid updated_at itself, a stale token on a readable row would still be told the current version. Should the conflict then omit it?", "options": ["A: keep it as is. The token is the comparison's own subject, and a git grep finds no FLS entry keyed on .updated_at (0 hits).", "B: take currentVersion from the caller's read, and answer null when FLS hides updated_at."], "recommendation": "A. Four axes: no real business scenario hides updated_at (0 hits), so B serves a speculative surface. A keeps one meaning for currentVersion, which clients echo as the next If-Match, while B gives it a second, null meaning that AI-written clients could mishandle. Startup focus says not to grow a case nobody pulls." }, { "question": "Should the Hono doors' hold on currentRecord (declaredConflictContext, #23587 / PR #23616) now be lifted?", "options": ["A: lift it on that card. The producer's record is now the caller's read.", "B: keep holding it."], "recommendation": "A, on #23587's own PR. It is outside this card's file surface, and this PR does not touch plugin-hono-server." }, { "question": "Is a 17.x backport owed? The same producer path is in the 17.7.0 tag.", "options": ["A: backport", "B: no"], "recommendation": "This is the maintainer's call, as dispatched. No backport PR was opened and no release branch was touched." } ], "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
ACCEPT → PR #23628 comment 6187592340 ·
domain:specseat 2 · sessionsession_01LFqdRsuM5nkqoJdHd5ztVk· 2026-10-15T08:12Z · on2a20fe2d80; the fix at the producer, red-to-green on the measured rig, CI green; contract review atCONTRACT_REVIEW_TIERbefore ready.objectstack-fleet commentedon Oct 15, 2026
Landing record ·
domain:specseat 2 · sessionsession_01LFqdRsuM5nkqoJdHd5ztVk· 2026-10-15T09:01Z · claim6185927212ends here:pm:dispatchedis removed in this act, and the card is closed (completed, by PR #23628'sFixes)origin/main7dc7d2213"fix(metadata-protocol): an optimistic-concurrency conflict reports only what the caller may read" (git merge-base --is-ancestor→ onorigin/main)gh-readonly-queue/main/*ref names it. One earlier queue build failed in a test that PR #23504 adds, stacked ahead of it (queue anchor #23641); the rebuild onmainpassed and merged82daa759dequals the clean merge of its parent with the reviewed head2a20fe2d8(ACCEPT6187592340; contract review PASS6187787956on that head)What closed, at function level: a stale version token on a row the caller's row-level policy hides now gets the same
404as the request without a token. A conflict on a readable row carries only the caller's own field-masked read. It ships on the 18 line only (the maintainer's no-17.xruling, relayed in6186913121).Handed on: the Hono doors'
currentRecordhold (#23587, its PR #23616 merged with the hold) may now lift, because the producer's record is the caller's read. That is noted on #23587.