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: a multi-user field keeps a deleted user's id, so a later update carrying the field unchanged is refused reference_not_found #29062
Filing gate: ① out-of-scope finding carried from #28881 (order ruling 4; the dev's os-dev-report 6570817610 on #28881, PR #29060). Filed by domain:server seat 1. ⛔ Not a claim. Graded by this seat p3 under 「定级你可以自己决定」 (Leehom, 2026-11-01): a stored dangling id the write path already refuses at write time; nothing is granted or disclosed.
Reach: a record with a multi_user (or multi-lookup) field naming a user who is later deleted.
What happens (named by the #28881 dev, not measured on this card)
The write-time reference check (assertReferencesResolve, packages/objectql/src/engine-referential-integrity.ts) refuses a write that names a missing user. But when a user referenced from a multi-user field is deleted afterwards, the delete walk (cascade / set-null) does not reach the array member, so the stored array keeps the deleted user's id. The next update of that record that carries the field unchanged is then refused reference_not_found for a value the editor never touched.
Wanted
Measure first: say what the delete walk does today for a single user / lookup field and for its multi-valued sibling, on main. Then the multi-valued field follows its single sibling's delete rule (drop the member on set-null, or refuse the delete on restrict), or the write check judges only members the write changes. The route is the dispatching seat's call.
Dedupe: searched multi_user deleted user id, set null multi lookup array member. Related: #28881 / PR #29060. New.
Claim: PM loop round 1 · card thread · 2026-11-02T06:56Z
Session: session_01FEkQ5kE5BrPUPNMMyv6oCH (the seat); the card thread is session_01FEkQ5nPQ5s2XpToMJtoBDm
Account: os-project-manager (the seat's linked user; the card's assignee)
Branch: claude/project-thread-cljmpx (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
Worktree: the card thread's own container checkout
Domain: domain:server
Seat: domain:server#3
Ruling-ref: the maintainer, 2026-11-01T03:46Z: 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」, with 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」, and 2026-11-01T06:39Z 「server 队列还是很多任务,再开一个席位吧」. Seat 1 handed this card to seat 3 at 2026-11-02T06:48Z under the maintainer's 2026-11-02T05:15Z 「队列进了很多任务,抓紧派发」.
File surface: packages/objectql/src/engine-referential-integrity.ts and/or packages/objectql/src/engine-delete-cascade.ts (route by measurement), objectql tests, a changeset
Container & model: card thread cmsg_01TenzsRopZj4jMbvjX8uz1XFBxyXPUfh7hY87x8YuedEk (same thread as #28946), mode:thread, model: opus (--tier names no path mandate: PM's call, default), effort: high
Clause-②: no (expected for route A) — route B widens a write door's accept set and the dev then declares yes (widening) with a TIER_DEFAULT review
Metadata-protocol: none (expected; the dev measures and declares)
Responsibility: domain:server seat 3 owns the PR to merge
Thread-read: none
Premise: named by the #28881 dev (os-dev-report 6570817610), not measured on the card; measure-first in the order
Serial constraints cleared: PR #29060 (#28881, seat 1, same check) in review: the dev merges origin/main once if it lands first
Resume-from: none — the branch restarts from origin/main at 4df62925 or later (its PR #28999 is merged)
State: → pm:dispatched, assigned to os-project-manager, recorded by this claim. Order: pm-orders/server/order-29062.md (project folder).
domain:server#3 · os-project-manager · session_01FEkQ5nPQ5s2XpToMJtoBDm
os-dev-report
{
"issue": 29062,
"status": "blocked",
"branch": "claude/project-thread-cljmpx",
"pr": null,
"session": "session_01FEkQ5nPQ5s2XpToMJtoBDm",
"premise_still_valid": true,
"summary": "Measurement only; stopped before any fix on the seat instruction that new work is paused (maintainer short on tokens). Nothing pushed, no PR, no build of a fix. The premise holds with one correction: the delete walk excludes the user TYPE altogether, single and multi alike (engine.ts DELETE_SEMANTICS_REFERENCE_TYPES = master_detail, lookup, tree; its docblock says a user field never joined the walk and moving it is a spec change). Lookups follow their rule for single and multi alike (set_null drops only the deleted member, restrict refuses 409, cascade deletes). Every user-delete door that deletes reaches engine.delete (admin remove-user via the better-auth ObjectQL adapter), and the walk leaves the user id in place. A later update that carries the field unchanged is refused 400 VALIDATION_FAILED reference_not_found, on a single user field as well as a multi-user one; an update that omits the field passes. So route A does not apply (no multi/single divergence) and route B does: the write check judges every value the payload carries, including members already stored. Route B would also fix the single user field, which the card did not name.",
"measurement": {
"base": "6b3ba82c (origin/main at restart)",
"real_sqlite_engine": "ObjectQL + SqlDriver(better-sqlite3), non-system caller, holder row naming A (single) or [A,B] (multi), then engine.delete(A). lookup single default/set_null: deleted, stored null. lookup single restrict: 409 DELETE_RESTRICTED, stored A. lookup single cascade: holder row deleted. lookup multi default/set_null: deleted, stored [B]. lookup multi restrict: 409 DELETE_RESTRICTED, stored [A,B]. lookup multi cascade: holder row deleted. Every surviving lookup row: update carrying the field unchanged 200, omitting it 200, adding a ghost id 400 VALIDATION_FAILED reference_not_found. user rows on a minimal sys_user rig: engine.delete(sys_user) 400 INVALID_FILTER (the minimal rig lacks columns the user delete reads), so the user rows were measured on the showcase instead.",
"showcase_doors": "objectstack dev --seed-admin --fresh, records: showcase_field_zoo f_user=X and f_users=[ADMIN,X]; showcase_project team_members=[ADMIN,X]. (1) POST /api/v1/auth/admin/remove-user: 403 YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS for the stock dev admin (its legacy role scalar is not admin; the data door refuses editing role); after setting role=admin in the dev database: 200, sys_user row 404 afterwards, f_user still X, f_users still [ADMIN,X], team_members still [ADMIN,X]. PATCH carrying both fields unchanged: 400 VALIDATION_FAILED [f_user reference_not_found, f_users reference_not_found]; carrying f_users only: 400 [f_users reference_not_found]; carrying f_user only: 400 [f_user reference_not_found]; project carrying team_members: 400 [team_members reference_not_found]; PATCH omitting the fields: 200. (2) DELETE /api/v1/data/sys_user/ID: 405 OBJECT_API_METHOD_NOT_ALLOWED, user kept. (3) POST /api/v1/auth/delete-user: 404 (deliberately unconfigured, ledger disposition disabled), user kept.",
"fix_shape_if_resumed": "Route B: pass the stored row to assertReferencesResolve at the by-id update site (engine.ts, priorRecord is always read there) and skip a value, or a multi member, the stored row already holds; added dangling members keep being refused as today. The bulk update site has per-row priors only on its rows-read branch; without them it keeps today behaviour. Clause-② yes (widening). Red first is the showcase PATCH above."
},
"tests": "Probe only, deleted after the run (no file left in the tree): a scratch real-SQLite vitest under packages/runtime/src, 16 configs (user/lookup x single/multi x default/set_null/restrict/cascade), exit 0; showcase HTTP script against a fresh dev boot. No fix, so no ablation.",
"gates": "none run: no diff (git status empty at 6b3ba82).",
"dogfood_battery": "n/a: no PR",
"mcp_calls": "0",
"api_writes": "1: this os-dev-report comment via post-stamped. No push, no pr_create. Write budget spent: 1.",
"deviations": "Stopped at measurement on the seat instruction (work paused). To measure remove-user the dev admin role scalar was set to admin directly in the fresh dev database, because the stock dev admin is refused by better-auth and the data door refuses role edits.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: public door, POST /api/v1/auth/admin/remove-user answers 403 YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS for the showcase dev platform admin (role scalar User), so a stock dev boot cannot remove a user through the admin surface · evidence: showcase fresh boot at 6b3ba82, platform admin token · dedupe words: remove-user dev admin role 403, platform admin better-auth role scalar",
"class: c · reach: named producer needed · a user field may author deleteBehavior (any type parses it verbatim) but the walk never reads it on a user field, so restrict or cascade on a user field is declared and not enforced · evidence: probe rows user single/multi restrict and cascade keep the id unchanged · dedupe words: user field deleteBehavior ignored, deleteBehavior on user type"
]
}
Release: claim 6571089500 (session_01FEkQ5kE5BrPUPNMMyv6oCH, card thread session_01FEkQ5nPQ5s2XpToMJtoBDm, branch claude/project-thread-cljmpx) · 2026-11-02T07:37Z · seat 3 shift close (maintainer 2026-11-02T07:10Z 「尽快提交代码,然后收工」, 2026-11-02T07:33Z 「pm-dispatch 中车道下班的流程,都要走完」); handed back, no stay-behind
Produce: none (measurement only; os-dev-report 6571331581, status blocked)
Resume-from: none — fresh from origin/main
Next: build route B of pm-orders/server/order-29062.md (the write-time reference check judges only reference values the write adds). Measured: the delete walk leaves out the user type by declaration (DELETE_SEMANTICS_REFERENCE_TYPES), single and multi alike; admin remove-user leaves the id; a later PATCH carrying the field unchanged is refused reference_not_found. Out-of-scope findings in the report (dev admin 403 on remove-user; deleteBehavior on a user field declared but not enforced).
Claim: PM loop round 1 · card thread · 2026-11-02T08:20Z
Session: session_01ADHkmQJdgarUjPLNgFBY67 (the seat); the card thread is session_01ADHkmSDzgkXapyAsvrXkrV
Account: os-steve (the seat's linked user; the card's assignee)
Branch: claude/project-thread-8t26yj
Worktree: the card thread's own container checkout
Domain: domain:server
Seat: domain:server#3
Ruling-ref: the maintainer, verbatim, in the project thread: 「开 server 2、3、5 号席位, 前任已经下班,接手所有开发,包括跟进之前未完成的开发落地。」
File surface: packages/objectql/src/engine.ts (by-id update reference check), packages/objectql/src/engine-referential-integrity.ts, objectql tests, a changeset
Container & model: card thread cmsg_01FdL1XeUyvVNxLreh3F4Qi93jDJpyZ4T5x8DmbvSnfEjZ, mode:thread, model: opus (--tier names no path mandate: PM's call, default), effort: high
Clause-②: yes (widening)
Metadata-protocol: none
Responsibility: packages/objectql write path (engine reference check); no platform path covers it; any editor of a record holding a deleted user id hits it today
Thread-read: 6571710050
Premise: holds (measured by the previous card thread, re-measured red first per the delta)
Serial constraints cleared: PR #29060 (#28881) edits the same two files: the dev merges origin/main before the PR and again if it lands first, keeping both intents
Resume-from: none — fresh from origin/main
State: pm:queue → pm:dispatched, assigned to os-steve, recorded by this claim. Order: project file server-seat3/standard-order.md + server-seat3/29062-delta.md.
{
"issue": 29062,
"status": "done",
"branch": "claude/project-thread-8t26yj",
"pr": "https://github.com/objectstack-ai/objectstack/pull/29245",
"head": "49f236e7",
"session": "session_01ADHkmSDzgkXapyAsvrXkrV",
"premise_still_valid": true,
"summary": "Route B. assertReferencesResolve takes an optional storedRows reader; after the probes, a refused value or multi-valued member that every stored row already holds is dropped, so only references a write adds are judged. By-id update passes its priorRecord (always read there); predicate update passes the readPriorRows memo, read only after a refusal, and forgives only when every matched row holds the value (zero matched rows forgive nothing). Insert and the insert-mode dry run pass nothing and are unchanged; the engine has no upsert door. Covers single user, multi user, lookup and master_detail alike. Delete walk untouched. origin/main merged twice; the second brings #29060 (flow-write reference check), auto-merged clean, both intents kept and its suites green.",
"tests": "Red first on base 2af0ae2a: engine-lookup-referential-integrity.test.ts Tests 4 failed | 17 passed (21), cause ValidationError 'no sys_user record has id u_gone'. Ablation 1 (stored-row read replaced by empty, via ablation-replace, blob dbd8047a -> 7daaf379 -> restored == HEAD): 4 failed | 17 passed. Ablation 2 (every -> some): 1 failed (bulk) | 20 passed; restored == HEAD. Final head 49f236e7: objectql test Test Files 527/527, Tests 10145/10145; objectql typecheck exit 0 (lock VERDICT command-exit 0); service-automation runas-system-reference-check.integration + builtin/crud-runas: 2/2 files, 44/44 tests.",
"gates": "dispatch-gates --commands (no paths) at 49f236e7: 63 families; all run, exit codes recorded; --ran: 62 run, 1 NOT MEASURED = node scripts/check-plugin-teardown-shape.mjs --self-test exit 3 PREREQUISITE NOT MET (positive control pinned to a commit outside this shallow clone), declared to CI Lint & Repo Gates. Every other family exit 0. pnpm lint exit 0 (also at 0299a25b). CI check-runs: in_progress at report time (not waited on).",
"line_budget": "5 files, +201/-16 (217 changed lines vs 3000 threshold); no governed surface",
"files_changed": [
".changeset/29062-stored-reference-not-rejudged.md",
"packages/objectql/src/engine-referential-integrity.ts",
"packages/objectql/src/engine.ts",
"packages/objectql/src/engine-lookup-referential-integrity.test.ts",
"packages/objectql/src/engine-unreadable-master-detail-header.test.ts"
],
"dogfood_battery": "n/a — no ! in the PR title and Clause-② is yes (widening), no (narrowing) arm",
"deviations": "(1) One forced test edit, named in the PR body: engine-unreadable-master-detail-header.test.ts repoint-by-id / bulk-repoint door wrote the header line l1 already stores, now names h_mine_locked. (2) The first merged-head rebuild accidentally also built objectql's dependents (filter spelled objectql...); output discarded, no effect on the tree. (3) check-plugin-teardown-shape --self-test NOT MEASURED locally (needs unshallow clone).",
"mcp_calls": "0",
"api_writes": "2 — relay pr_create (#29245, run 41427163731) and this os-dev-report comment via post-stamped. git push is not counted.",
"open_questions": [
{
"question": "Route B is applied by type-agnostic rule, so a master_detail header the caller cannot read, carried back unchanged, now passes (as omitting it already did); a repoint to it is still refused. Keep master_detail in the rule?",
"options": [
"A keep it: the stored value is not the write's reference; the #22519 existence oracle is not reopened because the forgiveness does not depend on whether the header exists",
"B exclude master_detail: keep judging a carried master-detail header every time"
],
"recommendation": "A, because forgiving depends only on the stored value, never on the header, so it reveals nothing about the header, and it fixes the same edit-form refusal for a header whose sharing changed"
}
],
"out_of_scope_findings": [
"class: a · reach: public door, POST /api/v1/auth/admin/remove-user answers 403 YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS for the showcase dev platform admin (role scalar is not admin) · evidence: previous card thread measurement 6571331581 on showcase fresh boot at 6b3ba82c; not re-measured here · dedupe words: remove-user dev admin role 403, platform admin better-auth role scalar",
"class: c · reach: named producer needed · deleteBehavior on a user field parses and is never read by the delete walk (DELETE_SEMANTICS_REFERENCE_TYPES excludes user), so restrict/cascade on a user field is declared and not enforced · evidence: 6571331581 probe rows user single/multi restrict and cascade keep the id · dedupe words: user field deleteBehavior ignored, deleteBehavior on user type"
]
}
Pointer · 2026-11-02T10:22Z: PR #29245 seat ACCEPT is on the PR. The dev's two out-of-scope findings are filed: #29253 (deleteBehavior on a user field not enforced) and #29254 (dev admin 403 on admin remove-user).
git grep -n "\[#29062\] a reference value already stored" origin/main -- packages/objectql/src/engine-lookup-referential-integrity.test.ts
line 418
Landed: merged as 344a16d7. A write now judges only the reference values it adds. A stored reference that is carried back unchanged is not refused because it stopped resolving, and that includes a single member of a multi-user field. A new id that does not exist is still refused, and inserts still check each value.
Seat ACCEPT 6574694888 (with the new-head addendum 6575062643) is on the PR (Clause-②: yes (widening), TIER_DEFAULT declaration review inside the ACCEPT). Fixes #29062 closed this card on merge.
State: pm:dispatched and the assignee removed in the same round.
Activity
objectstack-fleet commentedon Nov 2, 2026
domain:server#3 · os-project-manager · session_01FEkQ5kE5BrPUPNMMyv6oCH
Claim: PM loop round 1 · card thread · 2026-11-02T06:56Z
Session:
session_01FEkQ5kE5BrPUPNMMyv6oCH(the seat); the card thread issession_01FEkQ5nPQ5s2XpToMJtoBDmAccount:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-cljmpx(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#3Ruling-ref: the maintainer, 2026-11-01T03:46Z: 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」, with 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」, and 2026-11-01T06:39Z 「server 队列还是很多任务,再开一个席位吧」. Seat 1 handed this card to seat 3 at 2026-11-02T06:48Z under the maintainer's 2026-11-02T05:15Z 「队列进了很多任务,抓紧派发」.
File surface:
packages/objectql/src/engine-referential-integrity.tsand/orpackages/objectql/src/engine-delete-cascade.ts(route by measurement), objectql tests, a changesetContainer & model: card thread
cmsg_01TenzsRopZj4jMbvjX8uz1XFBxyXPUfh7hY87x8YuedEk(same thread as #28946),mode:thread,model: opus(--tiernames no path mandate: PM's call, default),effort: highClause-②: no (expected for route A) — route B widens a write door's accept set and the dev then declares yes (widening) with a
TIER_DEFAULTreviewMetadata-protocol: none (expected; the dev measures and declares)
Responsibility:
domain:serverseat 3 owns the PR to mergeThread-read: none
Premise: named by the #28881 dev (os-dev-report
6570817610), not measured on the card; measure-first in the orderSerial constraints cleared: PR #29060 (#28881, seat 1, same check) in review: the dev merges
origin/mainonce if it lands firstResume-from: none — the branch restarts from
origin/mainat4df62925or later (its PR #28999 is merged)State: →
pm:dispatched, assigned toos-project-manager, recorded by this claim. Order:pm-orders/server/order-29062.md(project folder).objectstack-fleet commentedon Nov 2, 2026
domain:server#3 · os-project-manager · session_01FEkQ5nPQ5s2XpToMJtoBDm
os-dev-report
{
"issue": 29062,
"status": "blocked",
"branch": "claude/project-thread-cljmpx",
"pr": null,
"session": "session_01FEkQ5nPQ5s2XpToMJtoBDm",
"premise_still_valid": true,
"summary": "Measurement only; stopped before any fix on the seat instruction that new work is paused (maintainer short on tokens). Nothing pushed, no PR, no build of a fix. The premise holds with one correction: the delete walk excludes the user TYPE altogether, single and multi alike (engine.ts DELETE_SEMANTICS_REFERENCE_TYPES = master_detail, lookup, tree; its docblock says a user field never joined the walk and moving it is a spec change). Lookups follow their rule for single and multi alike (set_null drops only the deleted member, restrict refuses 409, cascade deletes). Every user-delete door that deletes reaches engine.delete (admin remove-user via the better-auth ObjectQL adapter), and the walk leaves the user id in place. A later update that carries the field unchanged is refused 400 VALIDATION_FAILED reference_not_found, on a single user field as well as a multi-user one; an update that omits the field passes. So route A does not apply (no multi/single divergence) and route B does: the write check judges every value the payload carries, including members already stored. Route B would also fix the single user field, which the card did not name.",
"measurement": {
"base": "6b3ba82c (origin/main at restart)",
"real_sqlite_engine": "ObjectQL + SqlDriver(better-sqlite3), non-system caller, holder row naming A (single) or [A,B] (multi), then engine.delete(A). lookup single default/set_null: deleted, stored null. lookup single restrict: 409 DELETE_RESTRICTED, stored A. lookup single cascade: holder row deleted. lookup multi default/set_null: deleted, stored [B]. lookup multi restrict: 409 DELETE_RESTRICTED, stored [A,B]. lookup multi cascade: holder row deleted. Every surviving lookup row: update carrying the field unchanged 200, omitting it 200, adding a ghost id 400 VALIDATION_FAILED reference_not_found. user rows on a minimal sys_user rig: engine.delete(sys_user) 400 INVALID_FILTER (the minimal rig lacks columns the user delete reads), so the user rows were measured on the showcase instead.",
"showcase_doors": "objectstack dev --seed-admin --fresh, records: showcase_field_zoo f_user=X and f_users=[ADMIN,X]; showcase_project team_members=[ADMIN,X]. (1) POST /api/v1/auth/admin/remove-user: 403 YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS for the stock dev admin (its legacy role scalar is not admin; the data door refuses editing role); after setting role=admin in the dev database: 200, sys_user row 404 afterwards, f_user still X, f_users still [ADMIN,X], team_members still [ADMIN,X]. PATCH carrying both fields unchanged: 400 VALIDATION_FAILED [f_user reference_not_found, f_users reference_not_found]; carrying f_users only: 400 [f_users reference_not_found]; carrying f_user only: 400 [f_user reference_not_found]; project carrying team_members: 400 [team_members reference_not_found]; PATCH omitting the fields: 200. (2) DELETE /api/v1/data/sys_user/ID: 405 OBJECT_API_METHOD_NOT_ALLOWED, user kept. (3) POST /api/v1/auth/delete-user: 404 (deliberately unconfigured, ledger disposition disabled), user kept.",
"fix_shape_if_resumed": "Route B: pass the stored row to assertReferencesResolve at the by-id update site (engine.ts, priorRecord is always read there) and skip a value, or a multi member, the stored row already holds; added dangling members keep being refused as today. The bulk update site has per-row priors only on its rows-read branch; without them it keeps today behaviour. Clause-② yes (widening). Red first is the showcase PATCH above."
},
"tests": "Probe only, deleted after the run (no file left in the tree): a scratch real-SQLite vitest under packages/runtime/src, 16 configs (user/lookup x single/multi x default/set_null/restrict/cascade), exit 0; showcase HTTP script against a fresh dev boot. No fix, so no ablation.",
"gates": "none run: no diff (git status empty at 6b3ba82).",
"dogfood_battery": "n/a: no PR",
"mcp_calls": "0",
"api_writes": "1: this os-dev-report comment via post-stamped. No push, no pr_create. Write budget spent: 1.",
"deviations": "Stopped at measurement on the seat instruction (work paused). To measure remove-user the dev admin role scalar was set to admin directly in the fresh dev database, because the stock dev admin is refused by better-auth and the data door refuses role edits.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: public door, POST /api/v1/auth/admin/remove-user answers 403 YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS for the showcase dev platform admin (role scalar User), so a stock dev boot cannot remove a user through the admin surface · evidence: showcase fresh boot at 6b3ba82, platform admin token · dedupe words: remove-user dev admin role 403, platform admin better-auth role scalar",
"class: c · reach: named producer needed · a user field may author deleteBehavior (any type parses it verbatim) but the walk never reads it on a user field, so restrict or cascade on a user field is declared and not enforced · evidence: probe rows user single/multi restrict and cascade keep the id unchanged · dedupe words: user field deleteBehavior ignored, deleteBehavior on user type"
]
}
objectstack-fleet commentedon Nov 2, 2026
domain:server#3 · os-project-manager · session_01FEkQ5kE5BrPUPNMMyv6oCH
Release: claim
6571089500(session_01FEkQ5kE5BrPUPNMMyv6oCH, card threadsession_01FEkQ5nPQ5s2XpToMJtoBDm, branchclaude/project-thread-cljmpx) · 2026-11-02T07:37Z · seat 3 shift close (maintainer 2026-11-02T07:10Z 「尽快提交代码,然后收工」, 2026-11-02T07:33Z 「pm-dispatch 中车道下班的流程,都要走完」); handed back, no stay-behindProduce: none (measurement only; os-dev-report
6571331581, status blocked)Resume-from: none — fresh from
origin/mainpm-orders/server/order-29062.md(the write-time reference check judges only reference values the write adds). Measured: the delete walk leaves out theusertype by declaration (DELETE_SEMANTICS_REFERENCE_TYPES), single and multi alike; admin remove-user leaves the id; a later PATCH carrying the field unchanged is refusedreference_not_found. Out-of-scope findings in the report (dev admin 403 on remove-user;deleteBehavioron a user field declared but not enforced).pm:dispatched→pm:queue, assignee cleared.objectstack-fleet commentedon Nov 2, 2026
domain:server#3 · os-steve · session_01ADHkmQJdgarUjPLNgFBY67
Claim: PM loop round 1 · card thread · 2026-11-02T08:20Z
Session:
session_01ADHkmQJdgarUjPLNgFBY67(the seat); the card thread issession_01ADHkmSDzgkXapyAsvrXkrVAccount:
os-steve(the seat's linked user; the card's assignee)Branch:
claude/project-thread-8t26yjWorktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#3Ruling-ref: the maintainer, verbatim, in the project thread: 「开 server 2、3、5 号席位, 前任已经下班,接手所有开发,包括跟进之前未完成的开发落地。」
File surface:
packages/objectql/src/engine.ts(by-id update reference check),packages/objectql/src/engine-referential-integrity.ts, objectql tests, a changesetContainer & model: card thread
cmsg_01FdL1XeUyvVNxLreh3F4Qi93jDJpyZ4T5x8DmbvSnfEjZ,mode:thread,model: opus(--tiernames no path mandate: PM's call, default),effort: highClause-②: yes (widening)
Metadata-protocol: none
Responsibility:
packages/objectqlwrite path (engine reference check); no platform path covers it; any editor of a record holding a deleted user id hits it todayThread-read: 6571710050
Premise: holds (measured by the previous card thread, re-measured red first per the delta)
Serial constraints cleared: PR #29060 (#28881) edits the same two files: the dev merges
origin/mainbefore the PR and again if it lands first, keeping both intentsResume-from: none — fresh from
origin/mainState:
pm:queue→pm:dispatched, assigned toos-steve, recorded by this claim. Order: project file server-seat3/standard-order.md + server-seat3/29062-delta.md.fix(objectql): the reference check judges only the values a write add…
test(objectql): a master-detail repoint pin names a header the line d…
objectstack-fleet commentedon Nov 2, 2026
domain:server#3 · os-steve · session_01ADHkmSDzgkXapyAsvrXkrV
os-dev-report
{ "issue": 29062, "status": "done", "branch": "claude/project-thread-8t26yj", "pr": "https://github.com/objectstack-ai/objectstack/pull/29245", "head": "49f236e7", "session": "session_01ADHkmSDzgkXapyAsvrXkrV", "premise_still_valid": true, "summary": "Route B. assertReferencesResolve takes an optional storedRows reader; after the probes, a refused value or multi-valued member that every stored row already holds is dropped, so only references a write adds are judged. By-id update passes its priorRecord (always read there); predicate update passes the readPriorRows memo, read only after a refusal, and forgives only when every matched row holds the value (zero matched rows forgive nothing). Insert and the insert-mode dry run pass nothing and are unchanged; the engine has no upsert door. Covers single user, multi user, lookup and master_detail alike. Delete walk untouched. origin/main merged twice; the second brings #29060 (flow-write reference check), auto-merged clean, both intents kept and its suites green.", "tests": "Red first on base 2af0ae2a: engine-lookup-referential-integrity.test.ts Tests 4 failed | 17 passed (21), cause ValidationError 'no sys_user record has id u_gone'. Ablation 1 (stored-row read replaced by empty, via ablation-replace, blob dbd8047a -> 7daaf379 -> restored == HEAD): 4 failed | 17 passed. Ablation 2 (every -> some): 1 failed (bulk) | 20 passed; restored == HEAD. Final head 49f236e7: objectql test Test Files 527/527, Tests 10145/10145; objectql typecheck exit 0 (lock VERDICT command-exit 0); service-automation runas-system-reference-check.integration + builtin/crud-runas: 2/2 files, 44/44 tests.", "gates": "dispatch-gates --commands (no paths) at 49f236e7: 63 families; all run, exit codes recorded; --ran: 62 run, 1 NOT MEASURED = node scripts/check-plugin-teardown-shape.mjs --self-test exit 3 PREREQUISITE NOT MET (positive control pinned to a commit outside this shallow clone), declared to CI Lint & Repo Gates. Every other family exit 0. pnpm lint exit 0 (also at 0299a25b). CI check-runs: in_progress at report time (not waited on).", "line_budget": "5 files, +201/-16 (217 changed lines vs 3000 threshold); no governed surface", "files_changed": [ ".changeset/29062-stored-reference-not-rejudged.md", "packages/objectql/src/engine-referential-integrity.ts", "packages/objectql/src/engine.ts", "packages/objectql/src/engine-lookup-referential-integrity.test.ts", "packages/objectql/src/engine-unreadable-master-detail-header.test.ts" ], "dogfood_battery": "n/a — no ! in the PR title and Clause-② is yes (widening), no (narrowing) arm", "deviations": "(1) One forced test edit, named in the PR body: engine-unreadable-master-detail-header.test.ts repoint-by-id / bulk-repoint door wrote the header line l1 already stores, now names h_mine_locked. (2) The first merged-head rebuild accidentally also built objectql's dependents (filter spelled objectql...); output discarded, no effect on the tree. (3) check-plugin-teardown-shape --self-test NOT MEASURED locally (needs unshallow clone).", "mcp_calls": "0", "api_writes": "2 — relay pr_create (#29245, run 41427163731) and this os-dev-report comment via post-stamped. git push is not counted.", "open_questions": [ { "question": "Route B is applied by type-agnostic rule, so a master_detail header the caller cannot read, carried back unchanged, now passes (as omitting it already did); a repoint to it is still refused. Keep master_detail in the rule?", "options": [ "A keep it: the stored value is not the write's reference; the #22519 existence oracle is not reopened because the forgiveness does not depend on whether the header exists", "B exclude master_detail: keep judging a carried master-detail header every time" ], "recommendation": "A, because forgiving depends only on the stored value, never on the header, so it reveals nothing about the header, and it fixes the same edit-form refusal for a header whose sharing changed" } ], "out_of_scope_findings": [ "class: a · reach: public door, POST /api/v1/auth/admin/remove-user answers 403 YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS for the showcase dev platform admin (role scalar is not admin) · evidence: previous card thread measurement 6571331581 on showcase fresh boot at 6b3ba82c; not re-measured here · dedupe words: remove-user dev admin role 403, platform admin better-auth role scalar", "class: c · reach: named producer needed · deleteBehavior on a user field parses and is never read by the delete walk (DELETE_SEMANTICS_REFERENCE_TYPES excludes user), so restrict/cascade on a user field is declared and not enforced · evidence: 6571331581 probe rows user single/multi restrict and cascade keep the id · dedupe words: user field deleteBehavior ignored, deleteBehavior on user type" ] }Stamped 2026-11-02T10:15Z.
Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:server#3 · os-steve · session_01ADHkmQJdgarUjPLNgFBY67
Pointer · 2026-11-02T10:22Z: PR #29245 seat ACCEPT is on the PR. The dev's two out-of-scope findings are filed: #29253 (
deleteBehavioron auserfield not enforced) and #29254 (dev admin 403 on admin remove-user).docs: the reference check judges only what an update adds — error cat…
objectstack-fleet commentedon Nov 2, 2026
domain:server#3 · os-steve · session_01ADHkmQJdgarUjPLNgFBY67
Landing record · #29062 · PR #29245
domain:serverseat 3 ·session_01ADHkmQJdgarUjPLNgFBY67· 2026-11-02T11:42Zgit ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-29245-*'git merge-base --is-ancestor 344a16d7ab origin/maingit grep -n "\[#29062\] a reference value already stored" origin/main -- packages/objectql/src/engine-lookup-referential-integrity.test.ts344a16d7. A write now judges only the reference values it adds. A stored reference that is carried back unchanged is not refused because it stopped resolving, and that includes a single member of a multi-user field. A new id that does not exist is still refused, and inserts still check each value.6574694888(with the new-head addendum6575062643) is on the PR (Clause-②: yes (widening), TIER_DEFAULT declaration review inside the ACCEPT).Fixes #29062closed this card on merge.pm:dispatchedand the assignee removed in the same round.