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
objectql write path: an insert or update response leaves a declared column the driver did not echo as an absent key on mongo/memory, while the next read answers it null (#29408) #29538
立卡门类别: ① 产品缺陷 — 具名落点 + 复现. This follow-up is owed by work under review: open_question 2 of the #29408 dev report (issuecomment-6584231973), answered B by contract review issuecomment-6584622486 on PR #29528 and ruled B by this seat.
reach: 公开入口实测 on PR #29528's base aca58cdb6, through the engine. The #29408 card thread measured that SQLite answers an unwritten declared column as null in both the read and the write response. Memory and mongo return no key in either. PR #29528 fixes the read half only: find and findOne now answer every stored declared column the read selected as null. The insert and update answers on mongo and memory stay sparse.
业务理由: a client that creates or edits a record and renders the response shows a different record body than the very next GET of the same row. Once #29528 lands, the write response is the only record body left that differs by driver. The house rule is that the write response is the record as read (read-your-write; #5504 holds it for materialized formulas). A UI form that diffs the write answer against its own state sees fields appear on the next read that the save never mentioned.
动手的读者: domain:server (objectql). The write-answer seam in packages/objectql/src/engine.ts (the insert, update and upsert answer paths), beside selectedStoredColumns and withDeclaredColumnsOnly in packages/objectql/src/declared-read-columns.ts as #29528 adds them. Triage grades and routes it.
查重 (includes closed issues): insert response unwritten column null → 3, write echo sparse mongo null → 4, read-your-write response null column → 6. The only one on this face is #29408, the read half. None covers the write answer.
What happens
SQLite: the insert or update answer carries every declared column, unwritten ones as null.
Memory and mongo: the answer carries only the keys the driver echoed, so an unwritten declared column is an absent key.
Claim: PM loop round 13 · 2026-11-02T22:41Z
Session: session_01ADHkmQjPiXgNoSaEpqeGvY (seat); the dev is the card thread, and its first report names its session
Account: os-steve
Branch: the card thread's harness-pinned branch, named in its first report (it names no issue, so this claim is the identity record)
Worktree: the card thread's own
Domain: domain:server
Seat: domain:server#1
Ruling-ref: 6584622486 (contract review on PR #29528, open_question 2 answered B: this follow-up)
File surface: the write-answer paths in packages/objectql/src/engine.ts, reusing packages/objectql/src/declared-read-columns.ts helpers, tests, and an @objectstack/objectql patch changeset. ⛔ No packages/spec/**, no new error code, no change to what a write stores or to the read path #29528 landed.
Container & model: S, mode:thread, model as the thread was opened, effort: medium
Clause-②: no (expected: only a write answer gains null keys; the dev measures)
Metadata-protocol: none
Responsibility: the code is the engine's write answer · a client sees two bodies for one record, the write answer and the next read · reached by every mongo- or memory-backed write of a row that leaves a declared field unwritten
Thread-read: 6584622486
Premise: unblocked; #29408 landed via PR #29528 (de708b35bc)
Serial constraints cleared: #29562 (this seat, in flight) edits validation/record-validator.ts only. #29068 (PR #29628) is in service-messaging, runtime and client.
Orders: the seat's standard order plus the card delta, in the project's shared folder (server-seat1/standard-order.md, server-seat1/29538-delta.md).
Claim: PM loop round 13 (revised: branch and dev session named) · 2026-11-02T22:43Z (supersedes 6590331437)
Session: session_01ADHkmQjPiXgNoSaEpqeGvY (seat); the dev is the card thread, session_01ADHkmQvHYgu6y4y7zJqHpN
Account: os-steve
Branch: claude/project-thread-2polz0 (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
Worktree: the card thread's own
Domain: domain:server
Seat: domain:server#1
Ruling-ref: 6584622486 (contract review on PR #29528, open_question 2 answered B: this follow-up)
File surface: the write-answer paths in packages/objectql/src/engine.ts, reusing packages/objectql/src/declared-read-columns.ts helpers, tests, and an @objectstack/objectql patch changeset. ⛔ No packages/spec/**, no new error code, no change to what a write stores or to the read path #29528 landed.
Container & model: S, mode:thread, model as the thread was opened, effort: medium
Clause-②: no (expected: only a write answer gains null keys; the dev measures)
Metadata-protocol: none
Responsibility: the code is the engine's write answer · a client sees two bodies for one record, the write answer and the next read · reached by every mongo- or memory-backed write of a row that leaves a declared field unwritten
Thread-read: 6584622486
Premise: unblocked; #29408 landed via PR #29528 (de708b35bc)
Serial constraints cleared: #29562 (this seat, in flight) edits validation/record-validator.ts only. #29068 (PR #29628) is in service-messaging, runtime and client.
Orders: the seat's standard order plus the card delta, in the project's shared folder (server-seat1/standard-order.md, server-seat1/29538-delta.md).
Hand-off: #29538 stopped before PR, by the maintainer's order
「还在开发、没提 PR 的卡,要求立刻提交代码到分支,也交接」 (Steve)
Branch:claude/project-thread-2polz0, head 2fc0141e63d4518281b5ff1677927245696fa51f, pushed. Base mainfa07650b. Working tree clean. No PR is open.
Status: the fix and its pins are done and green. The consumer sweep, the gates, dogfood, the PR and the os-dev-report are NOT done.
Done
Step 0, measured on fa07650b through the engine and the protocol doors (insert, batch insert, insertMany, update by id, createData, updateData, createManyData, updateManyData, batchData create / update / upsert both arms). SQLite: every write answer equals the next findOne. driver-memory (real driver, probe not kept) and driver-mongodb (fake Db; real mongod NOT MEASURED): every one of those 12 paths answered note, qty and the registry-injected system columns as missing keys that the next read answered null, and the formula doubled as null (a fault) against -1 on the read. Predicate update answers a count on all three; PUT is not served. Full table: project folder server-seat1/29538-step0.md.
Fix (commit e47a9f82): packages/objectql/src/engine.ts applies selectedStoredColumns(schema, undefined) + withSelectedColumnsAnswered / rowsWithSelectedColumnsAnswered right after the existing withDeclaredColumnsOnly at the insert result site and the by-id update readback site, before the formulas and the after-hooks. One docblock bullet in declared-read-columns.ts; the helper is reused, not copied, and the read path is unchanged. Re-measured on head: all 36 driver × path cells identical, and every write accepted on base and head.
Pins (commit 1cb3f6a3): objectql/src/write-answer-declared-columns.test.ts (sparse driver, the 12 paths: write answer toStrictEqual next findOne, plus a formula control); runtime/src/declared-column-null-on-write.test.ts (SQLite and a sparse stub, with SecurityPlugin: FLS-refused secret absent from the member's insert, batch and update answers; system control answers it null); driver-mongodb/src/mongodb-declared-column-null-on-write.test.ts (fake Db: insert, bulkCreate, update). Changeset .changeset/29538-write-answer-declared-column-null.md: objectql patch, Clause-②: no, Metadata-protocol: none.
Ablations, objectql rebuilt and the marker proven in dist/ on every leg: A1, insert fill removed → 14 objectql, 2 mongo and 2 runtime reds. A2, update fill removed → 11, 1 and 2 reds. A3, insert fill moved after the security mask → the FLS pin reds on both runtime drivers. A4, the same for update → the FLS pin reds again. Restore leg: blob equals HEAD, git diff HEAD empty, every marker --absent, pins green (28/3/8).
Forced test edits (commit 2fc0141e), all in objectql: five files read "left unset" off the write answer on a sparse stub. Each now asserts the answer is null AND the stored row has no key (engine-default-value-tokens, engine-lookup-referential-integrity, engine-select-option-default ×3, engine-write-formula-hydration (now also toStrictEqual the next read)). In protocol-save-notice-answer the hooks that read ctx.result treat null as "not said". The five files pass (93/93).
Consumer sweep (on head 1cb3f6a3, before the test edits), stopped part-way
objectql was red 10 in 5 files, which the edits above fix. These were green: rest 378 files, runtime 530, driver-memory 74, driver-mongodb 40, plugin-audit 68, plugin-security 241. Not run: metadata-protocol (stopped mid-run), service-analytics, plugin-auth, plugin-approvals, service-messaging, service-storage, service-settings, plugin-sharing, service-automation, service-datasource, plugin-webhooks, client, organizations, example-showcase, plugin-email, service-mailbox, driver-odata, plugin-pinyin-search, connector-mcp, cloud-connection, example-crm and cli. This is every package whose tests boot a real ObjectQL.
Next steps for whoever picks it up
Run the full objectql suite on 2fc0141e, then the unrun sweep packages above. Expect reds only where a test reads "unset" off a write answer, or tells a missing key from null on a write result. Fix them the same way: the stored row for "unset", != null for readers.
Run node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, each listed command, --ran reconcile, and pnpm lint for objectql, runtime and driver-mongodb. Watch the federated-injected-column-readers census: engine.ts makes no new direct call, so I expect no new row, but this is not measured. Run the dogfood battery too (the delta lists it).
Open a draft PR to main (Fixes #29538) through the relay pr_create. Copy the changeset's two declaration lines into the body and add ## 维护者速读. Name the six forced test edits and the four ablations. Then post the os-dev-report.
Acceptance notes to carry: engine-level write results keep internal: true fields whole (A-prime), so an engine-level write answer still differs from an engine findOne there on every driver. That is pre-existing and out of scope. The REST doors strip them.
Release: session_01ADHkmQjPiXgNoSaEpqeGvY · reason: seat 1 off duty on Steve's order (quoted below), work pushed before its PR · next: pm:queue, the next domain:server seat resumes from the branch
Resume-from: claude/project-thread-2polz0@2fc0141e63d4518281b5ff1677927245696fa51f
State: no PR yet. The branch is pushed and its tree is clean. The dev's hand-off is 6591637215, just above, and it lists what is done and what is left. The seat's orders are server-seat1/standard-order.md and server-seat1/29538-delta.md in the project's shared folder. Claim 6590385670 and its revision stand as the record of the file surface.
What the next seat does:
Re-claim with your own session, then open a card thread on the branch above.
Finish the consumer sweep (metadata-protocol plus the 21 packages not yet run), dispatch-gates --ran, lint and the dogfood battery. Merge origin/main first.
Open the draft PR, post the os-dev-report, then review it on the declaration face at TIER_DEFAULT. The expected level is Clause-②: no. Check it against the measured table.
Read the 6 forced objectql test edits closely. Each one moved a test that read "unset" off the write answer to read the stored row. Confirm every one is a test of the old answer and not of a behaviour a caller relies on.
Claim: PM loop round 1 · resume after the seat 1 release 6591650672 · 2026-11-03T00:24Z
Session: session_01JQwxbZUR3xvHyDPCjNL5P4 (the seat); the dev is the card thread session_01JQwxbXRug3R6WZAjtFuAoS
Account: os-warren
Branch: claude/project-thread-2polz0 (continued, not restarted; no PR yet)
Worktree: the card thread's own
Domain: domain:server
Seat: domain:server#5
Ruling-ref: 6584622486 (contract review on PR #29528, open_question 2 answered B: this follow-up); the maintainer, verbatim, in this account's project (2026-11-03T00:09:14Z): 「开 server 座位 2 和 5,前任已下班,接手他的所有开发,都是并发5」
File surface: unchanged from claim 6590385670: the write-answer paths in packages/objectql/src/engine.ts, packages/objectql/src/declared-read-columns.ts helpers, tests (incl. the runtime and driver-mongodb pins already on the branch), one @objectstack/objectql patch changeset. ⛔ No packages/spec/**, no new error code, no change to what a write stores or to the read path.
Container & model: M, mode:thread, model: default (--tier: no path mandate), effort: high
Clause-②: no (expected: only a write answer gains null keys; the dev measures)
Metadata-protocol: none
Responsibility: the code is the engine's write answer · a client sees two bodies for one record, the write answer and the next read · every mongo- or memory-backed write of a row that leaves a declared field unwritten
Thread-read: 6591650672
Premise: holds (hand-off 6591637215; the dev re-merges origin/main first)
Serial constraints cleared: no other seat-5 card touches packages/objectql/src/engine.ts; #29414 (this seat) is in metadata-protocol.
Resume-from: claude/project-thread-2polz0@2fc0141e63d4518281b5ff1677927245696fa51f
Takeover record: Release 6591650672 (os-steve, session_01ADHkmQjPiXgNoSaEpqeGvY) names this card back to pm:queue; last pushed sha 2fc0141e, fix and pins green, consumer sweep, gates, dogfood and PR owed. Review at TIER_DEFAULT on the declaration face, plus a read of the 6 forced objectql test edits.
Seat progress note · 2026-11-03T05:01Z · answering triage's idle reading (mailbox #29658, comment 6595859453). Not idle: the claim 6592031968 stands and the card thread is building on claude/project-thread-2polz0.
Head 94b04574 (pushed 2026-11-03T04:20Z). Merged origin/main earlier as 3597f58.
Consumer sweep found one red outside the first 21 packages: trigger-record-change, multi-lookup. It was fixed in-surface in 8bf733d, and an ablation proves the fix.
Final pass so far: 31 package suites green and typecheck ×3 green. check:engine-double-contract is green after the mongo pins' fakes were split per arm.
Still to run: the query-options-erasure recheck, pnpm lint, the dogfood battery and --ran. Then a draft PR and the os-dev-report on this card, followed by the seat's TIER_DEFAULT review.
{
"issue": 29538,
"status": "done",
"branch": "claude/project-thread-2polz0",
"pr": "https://github.com/objectstack-ai/objectstack/pull/29858",
"session": "session_01JQwxbXRug3R6WZAjtFuAoS (resumed from session_01ADHkmQvHYgu6y4y7zJqHpN at 2fc0141e, Resume-from per Release 6591650672)",
"premise_still_valid": true,
"summary": "Resumed the pushed fix (insert result and by-id update readback answer every stored declared column the driver did not echo as null, reusing #29408's helpers). Merged origin/main twice (3597f584, 143b91915; no protocol.ts diff vs main). The consumer sweep found one real reader of the old answer outside the original 21: trigger-record-change multilookup-context (#1872), where a driver that stores a column without echoing it got that column answered null and the flow record overlay lost the written value. Fixed in-surface (8bf733db): new storedColumnsLeftUnwritten in declared-read-columns.ts drops from the fill any column the insert payload, or the PATCH then the raw prior row, carries a non-null value for; a PATCH null stays answerable; no change on driver-sql, driver-memory, driver-mongodb, whose echo is the stored row. Gate reds on the new test code fixed in the tests only (mongo pin split into two native-only Collection fakes per seat call; three stored-row reads typed without as any; sparse fake refusal text). The 6 forced objectql test edits each test the old answer: no production caller reads those keys off a write answer (per file in the PR body). Clause-2 no and Metadata-protocol none confirmed from the diff: only a response body gains null keys, nothing stored or accepted moves, no packages/spec.",
"tests": "Head 77b6b65dd: objectql 545/545 files, 10792 passed; driver-mongodb 35+6 skipped files, 728 passed/194 skipped; runtime 535/535, 8562 passed/19 skipped; trigger-record-change 12/12, 121 passed; typecheck exit 0 for objectql (incl. check:test-typecheck), runtime, driver-mongodb. Consumer sweep on f847848c, prefix direction (downstream packages whose tests boot a real ObjectQL), all exit 0: objectql, runtime, driver-mongodb, driver-memory, rest, plugin-security, plugin-audit, trigger-record-change, metadata-protocol, service-analytics, plugin-auth, plugin-approvals, service-messaging, service-storage, service-settings, plugin-sharing, service-automation, service-datasource, plugin-webhooks, client, organizations, example-showcase, plugin-email, service-mailbox, driver-odata, plugin-pinyin-search, connector-mcp, cloud-connection, example-crm, cli (31). First pass on 3597f584: trigger-record-change red 1 (expected null to be 'ch_1'), fixed by 8bf733db. Ablations via ablation-replace.mjs + ablation-dist-preflight.mjs, objectql rebuilt each leg: A5 storedColumnsLeftUnwritten no-op -> 3 of 4 new non-echo pins red + multilookup-context red, PATCH-null control green; A6 write fill emptied -> 3/3 driver-mongodb arms red (first A6 attempt failed the DTS build, measured nothing, re-run typed); restore legs blob == HEAD, marker absent from dist, rebuild exit 0. A1-A4 by the previous dev on 1cb3f6a3 (hand-off 6591637215).",
"gates": "Head 77b6b65dd: dispatch-gates --commands --repo objectstack-ai/objectstack derived 63; all 63 run, exit codes captured before any pipe; --ran: 63 derived famil(ies) accounted for, 63 run, 0 NOT-MEASURED. pnpm lint exit 0. Earlier reds on the way, each fixed in the tests, no gate or ledger touched: check:engine-double-contract (mongo Collection fake counted as engine double), check:query-options-erasure (test surface 236 -> 239 from three as-any driver reads), check:where-matcher (sparse fake matcher unjudged), check:test-typecheck (an object key DriverQuery lacks, from my first typing attempt). Prereq misses fixed: check-plugin-teardown-shape --self-test needed 621a4876 fetched; check:dual-build-cjs-loads needed 7 packages built. PR check-runs: in_progress at report time, not waited on.",
"dogfood_battery": "94b04574b (later commits: test-only edits and a main merge) · VERDICT command-exit 0 · Test Files 399 passed + 3 skipped (402) · Tests 3297 passed + 28 skipped (3325) · no failures. Ran because the dispatch order owes it; os-dev.md scopes the full battery to a ! title or a (narrowing) arm, neither applies here.",
"mcp_calls": "0",
"api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/pulls, run 41600961497, read-back 11321 bytes identical) and this os-dev-report comment via post-stamped.mjs (POST /repos/objectstack-ai/objectstack/issues/29538/comments). Reads: REST GET of issue 29538 and its comments. git push is not a REST write.",
"deviations": [
"Card text says fill every stored declared column the driver did not return; the engine now also skips a column the write itself carried a non-null value for (payload, or PATCH then prior row). Same result on every in-repo driver; it only differs on a driver that stores without echoing, where it avoids answering a null the row does not hold. In-surface (engine.ts write-answer sites + declared-read-columns.ts helper).",
"trigger-record-change added to the sweep beyond the 21 named (it reads ctx.result); it was the one red.",
"Seat-1 test files edited beyond 2fc0141e: mongodb-declared-column-null-on-write.test.ts restructured (batch arm's next read is engine.find); write-answer-declared-columns.test.ts gained 4 non-echo cases and its refusal text changed."
],
"body_amendments": "none",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"class: a · reach: check:engine-double-contract reason line 'x PINNED [findOne]: packages/drivers/driver-mongodb/src/mongodb-declared-column-null-on-write.test.ts declares 1 engine double(s) whose findOne() does not route through assertEngineFindOnePredicate (line 57)' on 94b04574 parent · evidence: instrument false red, a native Mongo Collection fake declaring insertOne/insertMany/updateOne/findOne/find was counted as a findOne engine double because ENGINE_SIBLINGS (scripts/check-engine-double-contract.mjs:441) holds find and insertMany and ENGINE_ONLY_MEMBERS (:481) takes insertMany as positive engine evidence; counting at :1235-1243 · dedupe words: engine-double-contract insertMany Collection fake, ENGINE_ONLY_MEMBERS insertMany, findOne engine double misattribution · seat files at ACCEPT",
"class: a · reach: check:where-matcher 'packages/objectql/src/write-answer-declared-columns.test.ts: NEW silently-wrong WHERE matcher (0 silent, 1 unjudged). line 46 matches: could not lift: ReferenceError: name is not defined' · evidence: instrument false unjudged, the lifter (scripts/check-where-matcher-conformance.mjs buildCallable, identifiersIn) reads identifiers out of template-literal text, so a refusal message saying 'sparse driver' pulled the sibling driver literal and its name shorthand into the lift; renaming the message text made it CONFORMING · dedupe words: where-matcher lift template literal identifiers, could not lift name is not defined, unjudged matcher refusal text"
]
}
Seat records: claim 6592031968, progress note 6595884048, ACCEPT 6599143511 on head 77b6b65d (TIER_DEFAULT, not governed). pm:dispatched comes off in the same round. Follow-ups: #29859, #29860 (filed).
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p2 ·
pm:queue· 2026-11-02T19:05Z⛔ Not a claim.
null. A client sees two different bodies for one record. The write-side twin of objectql read path: a declared column the driver did not return stays an absent key, so mongo/memory rows are sparse where SQL rows are not #29408, p2.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:server#1 · os-steve · session_01ADHkmQjPiXgNoSaEpqeGvY
Claim: PM loop round 13 · 2026-11-02T22:41Z
Session:
session_01ADHkmQjPiXgNoSaEpqeGvY(seat); the dev is the card thread, and its first report names its sessionAccount:
os-steveBranch: the card thread's harness-pinned branch, named in its first report (it names no issue, so this claim is the identity record)
Worktree: the card thread's own
Domain:
domain:serverSeat:
domain:server#1Ruling-ref: 6584622486 (contract review on PR #29528, open_question 2 answered B: this follow-up)
File surface: the write-answer paths in
packages/objectql/src/engine.ts, reusingpackages/objectql/src/declared-read-columns.tshelpers, tests, and an@objectstack/objectqlpatch changeset. ⛔ Nopackages/spec/**, no new error code, no change to what a write stores or to the read path #29528 landed.Container & model:
S,mode:thread, model as the thread was opened,effort: mediumClause-②: no (expected: only a write answer gains
nullkeys; the dev measures)Metadata-protocol: none
Responsibility: the code is the engine's write answer · a client sees two bodies for one record, the write answer and the next read · reached by every mongo- or memory-backed write of a row that leaves a declared field unwritten
Thread-read: 6584622486
Premise: unblocked; #29408 landed via PR #29528 (
de708b35bc)Serial constraints cleared: #29562 (this seat, in flight) edits
validation/record-validator.tsonly. #29068 (PR #29628) is in service-messaging, runtime and client.Orders: the seat's standard order plus the card delta, in the project's shared folder (server-seat1/standard-order.md, server-seat1/29538-delta.md).
objectstack-fleet commentedon Nov 2, 2026
domain:server#1 · os-steve · session_01ADHkmQjPiXgNoSaEpqeGvY
Claim: PM loop round 13 (revised: branch and dev session named) · 2026-11-02T22:43Z (supersedes 6590331437)
Session:
session_01ADHkmQjPiXgNoSaEpqeGvY(seat); the dev is the card thread,session_01ADHkmQvHYgu6y4y7zJqHpNAccount:
os-steveBranch:
claude/project-thread-2polz0(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own
Domain:
domain:serverSeat:
domain:server#1Ruling-ref: 6584622486 (contract review on PR #29528, open_question 2 answered B: this follow-up)
File surface: the write-answer paths in
packages/objectql/src/engine.ts, reusingpackages/objectql/src/declared-read-columns.tshelpers, tests, and an@objectstack/objectqlpatch changeset. ⛔ Nopackages/spec/**, no new error code, no change to what a write stores or to the read path #29528 landed.Container & model:
S,mode:thread, model as the thread was opened,effort: mediumClause-②: no (expected: only a write answer gains
nullkeys; the dev measures)Metadata-protocol: none
Responsibility: the code is the engine's write answer · a client sees two bodies for one record, the write answer and the next read · reached by every mongo- or memory-backed write of a row that leaves a declared field unwritten
Thread-read: 6584622486
Premise: unblocked; #29408 landed via PR #29528 (
de708b35bc)Serial constraints cleared: #29562 (this seat, in flight) edits
validation/record-validator.tsonly. #29068 (PR #29628) is in service-messaging, runtime and client.Orders: the seat's standard order plus the card delta, in the project's shared folder (server-seat1/standard-order.md, server-seat1/29538-delta.md).
test(objectql, runtime, driver-mongodb): pin the write answer to the …
test(objectql): read "left unset" off the stored row, not the write a…
objectstack-fleet commentedon Nov 3, 2026
domain:server#1 · os-steve · session_01ADHkmQjPiXgNoSaEpqeGvY
Hand-off: #29538 stopped before PR, by the maintainer's order
「还在开发、没提 PR 的卡,要求立刻提交代码到分支,也交接」 (Steve)
claude/project-thread-2polz0, head2fc0141e63d4518281b5ff1677927245696fa51f, pushed. Basemainfa07650b. Working tree clean. No PR is open.Done
fa07650bthrough the engine and the protocol doors (insert, batch insert,insertMany, update by id,createData,updateData,createManyData,updateManyData,batchDatacreate / update / upsert both arms). SQLite: every write answer equals the nextfindOne. driver-memory (real driver, probe not kept) and driver-mongodb (fakeDb; real mongod NOT MEASURED): every one of those 12 paths answerednote,qtyand the registry-injected system columns as missing keys that the next read answerednull, and the formuladoubledasnull(a fault) against-1on the read. Predicate update answers a count on all three;PUTis not served. Full table: project folderserver-seat1/29538-step0.md.e47a9f82):packages/objectql/src/engine.tsappliesselectedStoredColumns(schema, undefined)+withSelectedColumnsAnswered/rowsWithSelectedColumnsAnsweredright after the existingwithDeclaredColumnsOnlyat the insert result site and the by-id update readback site, before the formulas and the after-hooks. One docblock bullet indeclared-read-columns.ts; the helper is reused, not copied, and the read path is unchanged. Re-measured on head: all 36 driver × path cells identical, and every write accepted on base and head.1cb3f6a3):objectql/src/write-answer-declared-columns.test.ts(sparse driver, the 12 paths: write answertoStrictEqualnextfindOne, plus a formula control);runtime/src/declared-column-null-on-write.test.ts(SQLite and a sparse stub, withSecurityPlugin: FLS-refusedsecretabsent from the member's insert, batch and update answers; system control answers itnull);driver-mongodb/src/mongodb-declared-column-null-on-write.test.ts(fakeDb: insert, bulkCreate, update). Changeset.changeset/29538-write-answer-declared-column-null.md: objectql patch,Clause-②: no,Metadata-protocol: none.dist/on every leg: A1, insert fill removed → 14 objectql, 2 mongo and 2 runtime reds. A2, update fill removed → 11, 1 and 2 reds. A3, insert fill moved after the security mask → the FLS pin reds on both runtime drivers. A4, the same for update → the FLS pin reds again. Restore leg: blob equals HEAD,git diff HEADempty, every marker--absent, pins green (28/3/8).2fc0141e), all in objectql: five files read "left unset" off the write answer on a sparse stub. Each now asserts the answer isnullAND the stored row has no key (engine-default-value-tokens,engine-lookup-referential-integrity,engine-select-option-default×3,engine-write-formula-hydration(now alsotoStrictEqualthe next read)). Inprotocol-save-notice-answerthe hooks that readctx.resulttreatnullas "not said". The five files pass (93/93).Consumer sweep (on head
1cb3f6a3, before the test edits), stopped part-wayobjectql was red 10 in 5 files, which the edits above fix. These were green: rest 378 files, runtime 530, driver-memory 74, driver-mongodb 40, plugin-audit 68, plugin-security 241. Not run: metadata-protocol (stopped mid-run), service-analytics, plugin-auth, plugin-approvals, service-messaging, service-storage, service-settings, plugin-sharing, service-automation, service-datasource, plugin-webhooks, client, organizations, example-showcase, plugin-email, service-mailbox, driver-odata, plugin-pinyin-search, connector-mcp, cloud-connection, example-crm and cli. This is every package whose tests boot a real
ObjectQL.Next steps for whoever picks it up
2fc0141e, then the unrun sweep packages above. Expect reds only where a test reads "unset" off a write answer, or tells a missing key fromnullon a write result. Fix them the same way: the stored row for "unset",!= nullfor readers.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, each listed command,--ranreconcile, andpnpm lintfor objectql, runtime and driver-mongodb. Watch thefederated-injected-column-readerscensus: engine.ts makes no new direct call, so I expect no new row, but this is not measured. Run the dogfood battery too (the delta lists it).main(Fixes #29538) through the relaypr_create. Copy the changeset's two declaration lines into the body and add## 维护者速读. Name the six forced test edits and the four ablations. Then post the os-dev-report.Acceptance notes to carry: engine-level write results keep
internal: truefields whole (A-prime), so an engine-level write answer still differs from an enginefindOnethere on every driver. That is pre-existing and out of scope. The REST doors strip them.Stamped 2026-11-03T00:00Z.
objectstack-fleet commentedon Nov 3, 2026
domain:server#1 · os-steve · session_01ADHkmQjPiXgNoSaEpqeGvY
Release:
session_01ADHkmQjPiXgNoSaEpqeGvY· reason: seat 1 off duty on Steve's order (quoted below), work pushed before its PR · next:pm:queue, the nextdomain:serverseat resumes from the branchResume-from: claude/project-thread-2polz0@2fc0141e63d4518281b5ff1677927245696fa51f
Hand-off · #29538 · 2026-11-03T00:00Z
Order: 「还在开发、没提 PR 的卡,要求立刻提交代码到分支,也交接」 (Steve, project thread, 2026-11-02T23:57Z).
State: no PR yet. The branch is pushed and its tree is clean. The dev's hand-off is 6591637215, just above, and it lists what is done and what is left. The seat's orders are
server-seat1/standard-order.mdandserver-seat1/29538-delta.mdin the project's shared folder. Claim 6590385670 and its revision stand as the record of the file surface.What the next seat does:
dispatch-gates --ran, lint and the dogfood battery. Mergeorigin/mainfirst.os-dev-report, then review it on the declaration face atTIER_DEFAULT. The expected level isClause-②: no. Check it against the measured table.1 remaining item
objectstack-fleet commentedon Nov 3, 2026
domain:server#5 · os-warren · session_01JQwxbZUR3xvHyDPCjNL5P4
Claim: PM loop round 1 · resume after the seat 1 release 6591650672 · 2026-11-03T00:24Z
Session:
session_01JQwxbZUR3xvHyDPCjNL5P4(the seat); the dev is the card threadsession_01JQwxbXRug3R6WZAjtFuAoSAccount:
os-warrenBranch:
claude/project-thread-2polz0(continued, not restarted; no PR yet)Worktree: the card thread's own
Domain:
domain:serverSeat:
domain:server#5Ruling-ref: 6584622486 (contract review on PR #29528, open_question 2 answered B: this follow-up); the maintainer, verbatim, in this account's project (2026-11-03T00:09:14Z): 「开 server 座位 2 和 5,前任已下班,接手他的所有开发,都是并发5」
File surface: unchanged from claim 6590385670: the write-answer paths in
packages/objectql/src/engine.ts,packages/objectql/src/declared-read-columns.tshelpers, tests (incl. the runtime and driver-mongodb pins already on the branch), one@objectstack/objectqlpatch changeset. ⛔ Nopackages/spec/**, no new error code, no change to what a write stores or to the read path.Container & model:
M,mode:thread,model: default(--tier: no path mandate),effort: highClause-②: no (expected: only a write answer gains
nullkeys; the dev measures)Metadata-protocol: none
Responsibility: the code is the engine's write answer · a client sees two bodies for one record, the write answer and the next read · every mongo- or memory-backed write of a row that leaves a declared field unwritten
Thread-read: 6591650672
Premise: holds (hand-off 6591637215; the dev re-merges
origin/mainfirst)Serial constraints cleared: no other seat-5 card touches
packages/objectql/src/engine.ts; #29414 (this seat) is in metadata-protocol.Resume-from:
claude/project-thread-2polz0@2fc0141e63d4518281b5ff1677927245696fa51fTakeover record: Release 6591650672 (
os-steve,session_01ADHkmQjPiXgNoSaEpqeGvY) names this card back topm:queue; last pushed sha2fc0141e, fix and pins green, consumer sweep, gates, dogfood and PR owed. Review atTIER_DEFAULTon the declaration face, plus a read of the 6 forced objectql test edits.fix(objectql): a write never answers null for a column it carried a v…
chore(changeset): name the carried-value rule of the write-answer fill (
test(objectql, driver-mongodb): give each mongo write arm a fake of o…
objectstack-fleet commentedon Nov 3, 2026
domain:server#5 · os-warren · session_01JQwxbZUR3xvHyDPCjNL5P4
Seat progress note · 2026-11-03T05:01Z · answering triage's idle reading (mailbox #29658, comment 6595859453). Not idle: the claim 6592031968 stands and the card thread is building on
claude/project-thread-2polz0.94b04574(pushed 2026-11-03T04:20Z). Mergedorigin/mainearlier as 3597f58.check:engine-double-contractis green after the mongo pins' fakes were split per arm.pnpm lint, the dogfood battery and--ran. Then a draft PR and the os-dev-report on this card, followed by the seat's TIER_DEFAULT review.test(objectql): the sparse fake's refusals no longer name the driver …
test(objectql): read the stored row with a plain driver query, which …
objectstack-fleet commentedon Nov 3, 2026
domain:server#5 · os-warren · session_01JQwxbXRug3R6WZAjtFuAoS
os-dev-report
{ "issue": 29538, "status": "done", "branch": "claude/project-thread-2polz0", "pr": "https://github.com/objectstack-ai/objectstack/pull/29858", "session": "session_01JQwxbXRug3R6WZAjtFuAoS (resumed from session_01ADHkmQvHYgu6y4y7zJqHpN at 2fc0141e, Resume-from per Release 6591650672)", "premise_still_valid": true, "summary": "Resumed the pushed fix (insert result and by-id update readback answer every stored declared column the driver did not echo as null, reusing #29408's helpers). Merged origin/main twice (3597f584, 143b91915; no protocol.ts diff vs main). The consumer sweep found one real reader of the old answer outside the original 21: trigger-record-change multilookup-context (#1872), where a driver that stores a column without echoing it got that column answered null and the flow record overlay lost the written value. Fixed in-surface (8bf733db): new storedColumnsLeftUnwritten in declared-read-columns.ts drops from the fill any column the insert payload, or the PATCH then the raw prior row, carries a non-null value for; a PATCH null stays answerable; no change on driver-sql, driver-memory, driver-mongodb, whose echo is the stored row. Gate reds on the new test code fixed in the tests only (mongo pin split into two native-only Collection fakes per seat call; three stored-row reads typed without as any; sparse fake refusal text). The 6 forced objectql test edits each test the old answer: no production caller reads those keys off a write answer (per file in the PR body). Clause-2 no and Metadata-protocol none confirmed from the diff: only a response body gains null keys, nothing stored or accepted moves, no packages/spec.", "tests": "Head 77b6b65dd: objectql 545/545 files, 10792 passed; driver-mongodb 35+6 skipped files, 728 passed/194 skipped; runtime 535/535, 8562 passed/19 skipped; trigger-record-change 12/12, 121 passed; typecheck exit 0 for objectql (incl. check:test-typecheck), runtime, driver-mongodb. Consumer sweep on f847848c, prefix direction (downstream packages whose tests boot a real ObjectQL), all exit 0: objectql, runtime, driver-mongodb, driver-memory, rest, plugin-security, plugin-audit, trigger-record-change, metadata-protocol, service-analytics, plugin-auth, plugin-approvals, service-messaging, service-storage, service-settings, plugin-sharing, service-automation, service-datasource, plugin-webhooks, client, organizations, example-showcase, plugin-email, service-mailbox, driver-odata, plugin-pinyin-search, connector-mcp, cloud-connection, example-crm, cli (31). First pass on 3597f584: trigger-record-change red 1 (expected null to be 'ch_1'), fixed by 8bf733db. Ablations via ablation-replace.mjs + ablation-dist-preflight.mjs, objectql rebuilt each leg: A5 storedColumnsLeftUnwritten no-op -> 3 of 4 new non-echo pins red + multilookup-context red, PATCH-null control green; A6 write fill emptied -> 3/3 driver-mongodb arms red (first A6 attempt failed the DTS build, measured nothing, re-run typed); restore legs blob == HEAD, marker absent from dist, rebuild exit 0. A1-A4 by the previous dev on 1cb3f6a3 (hand-off 6591637215).", "gates": "Head 77b6b65dd: dispatch-gates --commands --repo objectstack-ai/objectstack derived 63; all 63 run, exit codes captured before any pipe; --ran: 63 derived famil(ies) accounted for, 63 run, 0 NOT-MEASURED. pnpm lint exit 0. Earlier reds on the way, each fixed in the tests, no gate or ledger touched: check:engine-double-contract (mongo Collection fake counted as engine double), check:query-options-erasure (test surface 236 -> 239 from three as-any driver reads), check:where-matcher (sparse fake matcher unjudged), check:test-typecheck (an object key DriverQuery lacks, from my first typing attempt). Prereq misses fixed: check-plugin-teardown-shape --self-test needed 621a4876 fetched; check:dual-build-cjs-loads needed 7 packages built. PR check-runs: in_progress at report time, not waited on.", "dogfood_battery": "94b04574b (later commits: test-only edits and a main merge) · VERDICT command-exit 0 · Test Files 399 passed + 3 skipped (402) · Tests 3297 passed + 28 skipped (3325) · no failures. Ran because the dispatch order owes it; os-dev.md scopes the full battery to a ! title or a (narrowing) arm, neither applies here.", "mcp_calls": "0", "api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/pulls, run 41600961497, read-back 11321 bytes identical) and this os-dev-report comment via post-stamped.mjs (POST /repos/objectstack-ai/objectstack/issues/29538/comments). Reads: REST GET of issue 29538 and its comments. git push is not a REST write.", "deviations": [ "Card text says fill every stored declared column the driver did not return; the engine now also skips a column the write itself carried a non-null value for (payload, or PATCH then prior row). Same result on every in-repo driver; it only differs on a driver that stores without echoing, where it avoids answering a null the row does not hold. In-surface (engine.ts write-answer sites + declared-read-columns.ts helper).", "trigger-record-change added to the sweep beyond the 21 named (it reads ctx.result); it was the one red.", "Seat-1 test files edited beyond 2fc0141e: mongodb-declared-column-null-on-write.test.ts restructured (batch arm's next read is engine.find); write-answer-declared-columns.test.ts gained 4 non-echo cases and its refusal text changed." ], "body_amendments": "none", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "class: a · reach: check:engine-double-contract reason line 'x PINNED [findOne]: packages/drivers/driver-mongodb/src/mongodb-declared-column-null-on-write.test.ts declares 1 engine double(s) whose findOne() does not route through assertEngineFindOnePredicate (line 57)' on 94b04574 parent · evidence: instrument false red, a native Mongo Collection fake declaring insertOne/insertMany/updateOne/findOne/find was counted as a findOne engine double because ENGINE_SIBLINGS (scripts/check-engine-double-contract.mjs:441) holds find and insertMany and ENGINE_ONLY_MEMBERS (:481) takes insertMany as positive engine evidence; counting at :1235-1243 · dedupe words: engine-double-contract insertMany Collection fake, ENGINE_ONLY_MEMBERS insertMany, findOne engine double misattribution · seat files at ACCEPT", "class: a · reach: check:where-matcher 'packages/objectql/src/write-answer-declared-columns.test.ts: NEW silently-wrong WHERE matcher (0 silent, 1 unjudged). line 46 matches: could not lift: ReferenceError: name is not defined' · evidence: instrument false unjudged, the lifter (scripts/check-where-matcher-conformance.mjs buildCallable, identifiersIn) reads identifiers out of template-literal text, so a refusal message saying 'sparse driver' pulled the sibling driver literal and its name shorthand into the lift; renaming the message text made it CONFORMING · dedupe words: where-matcher lift template literal identifiers, could not lift name is not defined, unjudged matcher refusal text" ] }Generated by Claude Code
objectstack-fleet commentedon Nov 3, 2026
domain:server#5 · os-warren · session_01JQwxbZUR3xvHyDPCjNL5P4
Landed · 2026-11-03T09:09Z
dbe2a93220, through the merge queueFixes #29538Seat records: claim 6592031968, progress note 6595884048, ACCEPT 6599143511 on head
77b6b65d(TIER_DEFAULT, not governed).pm:dispatchedcomes off in the same round. Follow-ups: #29859, #29860 (filed).