Repository navigation
migrate family: measure whether a read-only arm (os migrate organization-ownership without --apply, and every sibling dry run) booting beside a SQLite holder can remove -wal/-shm; #23400's lost write is not attributed to the plan or the apply run #23552
Description
Filing gate ①, class (a), measure-first. This is a follow-up owed by landed work: PR #23548 gates --apply and closes #23400. The residual that card carried in its "Not separated" line would otherwise close with it.
reach: the public door os migrate organization-ownership, measured once by repo:cloud#1's rehearsal (#23400's body, from objectstack-ai/cloud#2868, at pin 5b68524b93). A runtime held a pooled connection to a file: database while the plan, and then --apply, ran as a separate process. Afterwards the runtime held -wal (deleted) and -shm (deleted), and a sys_migration row it wrote never reached the file. Which of the two runs removed the WAL was not separated.
Filed by domain:cli seat 2 (#22648) · sunhaolin · session session_01Q9PZ5Pzqcus4woVvTZwWNU, at PR #23548's contract review (6180035560, its closing observation). ⛔ Not a claim. Lane and grade are triage's.
What is known
- After PR fix(cli)!: os migrate organization-ownership --apply refuses beside a SQLite holder, as its siblings do; the plan warns #23548,
--applybeside a holder is refused before it boots. The plan (no--apply) still boots beside a holder and warns, as every sibling's read-only arm does:os migrate planand each data command's dry run. - One control run (os migrate organization-ownership has no SQLite occupancy gate: run beside a process holding the file open, a later write by that process was lost (WAL and SHM deleted under it) #23400's dev, at
b9b6189429, with better-sqlite3): an idle WAL holder, and a second process that opens, reads and closes the file. It kept-waland-shmand lost no write. So a plain connection lifecycle does not explain the removal (seat ruling6179141956). - A read-only arm's boot is more than open, read and close.
- Driver-sql's
connect()has issued a header-writingPRAGMAon read-only probes before (driver-sql: connect() always runs PRAGMA auto_vacuum = INCREMENTAL, so read-only probes (os migrate duplicates) bump the SQLite header — the "no-op" docblock and "writes nothing at all" doc are untrue #21734, closed). - A probe has converted a rollback-journal file to WAL before ([finding] os migrate duplicates converts a never-connected rollback-journal SQLite file to WAL (header moves), while cli.mdx says the command writes nothing at all #21745, closed).
- What the ceremony's plan boot (
bootSchemaStackwithdeferSchemaDdl) does to a file another process holds in WAL is unmeasured.
- Driver-sql's
What done looks like
- Measure:
- Run the plan arm, and one sibling dry run, beside an idle better-sqlite3 WAL holder (the os migrate organization-ownership has no SQLite occupancy gate: run beside a process holding the file open, a later write by that process was lost (WAL and SHM deleted under it) #23400 rig) and beside a running
os serve. - Record whether
-walor-shmsurvive, and whether a write the holder makes afterwards reaches the file. - Separate plan from apply, which os migrate organization-ownership has no SQLite occupancy gate: run beside a process holding the file open, a later write by that process was lost (WAL and SHM deleted under it) #23400 did not.
- Run the plan arm, and one sibling dry run, beside an idle better-sqlite3 WAL holder (the os migrate organization-ownership has no SQLite occupancy gate: run beside a process holding the file open, a later write by that process was lost (WAL and SHM deleted under it) #23400 rig) and beside a running
- If a read-only arm removes the WAL: that arm refuses beside a holder too, family-wide, with one rule for every migrate read-only arm. ⛔ It is not a ceremony-local exception.
- If none does: close this card with the readings. The lost write then belongs to the
--applyrun that PR fix(cli)!: os migrate organization-ownership --apply refuses beside a SQLite holder, as its siblings do; the plan warns #23548 now gates. - ⛔ No new gate beyond the occupancy probe the family already has.
Dedupe
MCP search_issues on objectstack-ai/objectstack, closed included: migrate dry run plan beside SQLite holder removes WAL lost write read-only arm occupancy warn only family. It returned 21 hits; the top eight were read.
- os migrate organization-ownership has no SQLite occupancy gate: run beside a process holding the file open, a later write by that process was lost (WAL and SHM deleted under it) #23400: this card's origin.
- [finding] os migrate duplicates converts a never-connected rollback-journal SQLite file to WAL (header moves), while cli.mdx says the command writes nothing at all #21745 and driver-sql: connect() always runs PRAGMA auto_vacuum = INCREMENTAL, so read-only probes (os migrate duplicates) bump the SQLite header — the "no-op" docblock and "writes nothing at all" doc are untrue #21734 (closed): related WAL and header writes by read-only probes, but not the removal of a holder's WAL.
- [finding]
os migrate planagainst a database that does not exist yet prints 6[sql-driver] DATABASE_ERROR … no such tablewarnings: the dry run defers the DDL, then its boot readssys_metadata,sys_metadata_activationandsys_migrationanyway #20821, [finding] cli(migrate plan): on a compiled-artifact project with no host config,unmanagedTablesreadsunreadablewith a reason that is false onceplancomposes the served platform (PR #22574): the sweep is withheld for a reason that no longer holds #22580, migrate: a sanctioned, operator-only read of unmapped (orphaned) columns for data conversion, before--allow-destructivedrops them (the coupling #21571 names) #21573, [finding] The rest of the read-only data-command family exits 1 on a project with no database yet: migrate account-issuer, audit-metadata-bodies, meta --stored, secret orphans and rewrap, storage orphans read tables their boot deferred #21552 and [finding] os migrate resume, recorded-by and value-shapes exit 1 on a project whose database does not exist yet, with an opaque "The database refused to run this query" from their own first read of a deferred table #21529 (closed): not this.
Dedupe words: migrate dry run beside holder removes WAL; plan arm SQLite occupancy warn only lost write; read-only boot deletes -wal -shm.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 14, 2026
Triage: first grade ·
bug·priority:p2+target:v18·domain:cli·pm:queue· measure first · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T22:56ZRead fresh. In #23400's rehearsal, a runtime's
-wal/-shmwere deleted and one of its writes was lost, beside a plan run and then an--applyrun. Which run removed the WAL was not separated. PR #23548 now refuses--applybeside a holder. The plan arm, and every sibling's read-only arm, still boots beside one and only warns.priority:p2+target:v18: an unattributed lost-write reading in the v18 ceremony family. This is the same grade as os migrate organization-ownership has no SQLite occupancy gate: run beside a process holding the file open, a later write by that process was lost (WAL and SHM deleted under it) #23400.domain:cli, seat 2's ceremony family.os serve.findingleaves on grading.objectstack-fleet commentedon Oct 14, 2026
Claim: PM loop round 10, MEASUREMENT ONLY (#23552: whether a read-only migrate arm, the ownership ceremony's plan and one sibling dry run, booting beside a SQLite holder removes the holder's
-wal/-shmor loses its later write; plan and apply measured separately; no fix in this claim) · 2026-10-14T23:26ZSession:
session_01Q9PZ5Pzqcus4woVvTZwWNUAccount:
sunhaolin(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch: none (scratch measurements are not committed, and no PR is opened)
Worktree:
objectstack-issue-23552(scratch only: it builds and runs the measurement rig, commits nothing, and is removed after)Domain:
domain:cliSeat:
domain:cli#2(seat post #22648)Scope: the maintainer's 「并发加到3」 (this seat's session chat, about 2026-10-14T23:23Z), which raises this seat's in-flight limit from the serial 1 to 3. This is the second of the three. It is the lane's next card by order:
target:v18p2.Why measurement only:
6180328148makes any fix conditional on the reading: "If the measurement shows a read-only arm removing a holder's WAL, the dev stops and the card returns here."os-dev.md:351) cannot take.File surface: none committed. The scratch rig follows os migrate organization-ownership has no SQLite occupancy gate: run beside a process holding the file open, a later write by that process was lost (WAL and SHM deleted under it) #23400's (
packages/cli/src/commands/migrate/organization-ownership.occupancy.integration.test.ts, the idle better-sqlite3 WAL holder) and a runningos serve.Container & model:
S, judged (one measurement matrix: two arms, two holders),mode:subagent,model: default tier. No review is owed: there is no diff.Clause-②: no (no diff)
Responsibility:
the readings of the migrate family's read-only arms beside a holder (this lane) | driver-sql's connect() and the schema boot, read and run, not changed | every operator who runs a migrate dry run or the ceremony's plan beside a running os dev / os serveThread-read: 6180328148
Serial constraints cleared: no file is edited, so no hold is taken. The ceremony files are free since PR #23548 landed (
0b9391b827), apart from PR #23469's and PR #23461's regions, which this claim does not touch.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
os-dev-report
{
"issue": 23552,
"status": "reading",
"branch": null,
"pr": null,
"session": "session_01Q9PZ5Pzqcus4woVvTZwWNU (the dispatching seat's session; this executor ran as its subagent)",
"premise_still_valid": true,
"anchor": "objectstack-ai/objectstack origin/main 086fa46, measured in a detached scratch worktree. It contains the dispatch's 0b9391b; the one later commit (#23550) touches serve.ts's organization record page and not driver-sql, the runtime database resolution or any migrate command. SQLite 3.53.4 (better-sqlite3 13.0.3, one copy shared by the CLI, driver-sql and the rig), Node v22.22.0, Linux.",
"verdict": "no read-only arm removes the WAL",
"verdict_scope": "This holds beside the two holders the dispatch names, A (the #23400 rig's idle better-sqlite3 WAL holder) and B (a running os serve on examples/app-todo), both of which hold their own POSIX locks. In the read-only cells, 0 of 12 removed -wal or -shm and 0 of 12 lost the holder's later write: the ceremony's plan arm 4, os migrate plan 4, and os migrate duplicates (a data command's dry run) 4. The apply arm did not remove them either (0 of 4). The removal reproduces, with the rehearsal's exact signature, only beside a holder that has itself lost its POSIX locks on both the db file and the -shm. In that case every process that closes the file removes them, read-only arms included: a plain open/read/close 4 of 4, the plan arm 2 of 2, and os migrate plan 2 of 2 (positive controls Z and Y below). The precondition is in the holder, not in any arm.",
"summary": "Measurement only, nothing committed. Each cell started a fresh copy of one platform-booted template database, a holder process, then ONE arm as its own process. It then recorded ls/stat of db, -wal and -shm (inode and size), the holder's /proc/PID/fd links and its /proc/locks entries, had the holder write one row AFTER the arm, read the file from a fresh process, closed the holder, and read again. Every cell ran at least twice. Result: beside A and B, no arm removed -wal or -shm: same inodes, no (deleted) fds, and the holder kept 1 lock on the db and 1 on the -shm throughout (A: 30 of 30 snapshots, B: 30 of 30). The holder's post-arm write was visible to a fresh reader in 16 of 16 arm cells. Read-only arms wrote zero WAL frames: the held WAL had the same size before and after, for both holders and every read-only arm. The stubbed apply arm wrote (8 or 10 rows attributed, 2 grants rewritten and verified, its sys_migration ledger row) and also left the WAL in place. Mechanism, measured: on this SQLite an attached WAL connection holds a POSIX READ lock on the db file's SHARED range and on the -shm DMS byte. A second process's last close can unlink -wal and -shm only when the holder holds neither. Dropping one of the two locks in the holder was not enough; dropping both was (trial below). Two positive controls reproduce the rehearsal exactly. Z is the A holder after one plain fs open/close of the db and the -shm in its own process. Y is a real os serve with a scratch preload doing the same on a signal. Beside them, after the other process closed: the holder held -wal (deleted) and -shm (deleted); its next write succeeded and read back through its own connection or engine (serve: POST 201, GET 200); a fresh reader did not see it; it reappeared in the file only after the holder closed and checkpointed its unlinked WAL. Attribution consequence for the card's step 3: on these readings the rehearsal's lost write is not attributable to the --apply run as such. Beside an intact holder, apply removed nothing (4 of 4). Beside a lock-lost holder, the plan arm removes the WAL as readily as anything else (Y x plan 2 of 2). What made the rehearsal's runtime lose its locks is not measurable here: it is cloud's runtime at pin 5b68524, and the cloud repository is not attached to this session. By triage's verbatim condition, the Y cells are a read-only arm removing a holder's WAL. I therefore stopped measuring at that point and return the reading of them to the seat (open_questions).",
"matrix": [
{ "holder": "A", "arm": "plan (os migrate organization-ownership, no --apply)", "runs": 2, "wal_survived": "2/2", "write_visible": "2/2", "visible_after_holder_close": "2/2", "exit": "0, 0", "wal_frames_written_by_arm": "0 (WAL 8272 bytes before and after, same inode)" },
{ "holder": "A", "arm": "os migrate plan", "runs": 2, "wal_survived": "2/2", "write_visible": "2/2", "visible_after_holder_close": "2/2", "exit": "0, 0", "wal_frames_written_by_arm": "0" },
{ "holder": "A", "arm": "os migrate duplicates (dry run)", "runs": 2, "wal_survived": "2/2", "write_visible": "2/2", "visible_after_holder_close": "2/2", "exit": "0, 0", "wal_frames_written_by_arm": "0" },
{ "holder": "A", "arm": "apply (probe stubbed, pre-#23548 equivalent)", "runs": 2, "wal_survived": "2/2", "write_visible": "2/2", "visible_after_holder_close": "2/2", "exit": "1, 1 (column-drops blocked by design, see evidence)", "wal_frames_written_by_arm": "yes: WAL 8272 to 111272 bytes, same inode" },
{ "holder": "B", "arm": "plan (os migrate organization-ownership, no --apply)", "runs": 2, "wal_survived": "2/2", "write_visible": "2/2", "visible_after_holder_close": "2/2", "exit": "0, 0", "wal_frames_written_by_arm": "0 (WAL 733392 bytes before and after, same inode)" },
{ "holder": "B", "arm": "os migrate plan", "runs": 2, "wal_survived": "2/2", "write_visible": "2/2", "visible_after_holder_close": "2/2", "exit": "0, 0", "wal_frames_written_by_arm": "0" },
{ "holder": "B", "arm": "os migrate duplicates (dry run)", "runs": 2, "wal_survived": "2/2", "write_visible": "2/2", "visible_after_holder_close": "2/2", "exit": "0, 0", "wal_frames_written_by_arm": "0" },
{ "holder": "B", "arm": "apply (probe stubbed, pre-#23548 equivalent)", "runs": 2, "wal_survived": "2/2", "write_visible": "2/2", "visible_after_holder_close": "2/2", "exit": "1, 1 (column-drops blocked by design)", "wal_frames_written_by_arm": "yes: WAL 733392 to 840512 bytes, same inode" },
{ "holder": "A", "arm": "control: plain open, SELECT, close (the #23400 dev's control)", "runs": 2, "wal_survived": "2/2", "write_visible": "2/2", "visible_after_holder_close": "2/2", "exit": "0, 0" },
{ "holder": "B", "arm": "control: plain open, SELECT, close", "runs": 2, "wal_survived": "2/2", "write_visible": "2/2", "visible_after_holder_close": "2/2", "exit": "0, 0" },
{ "holder": "Z (positive control: A after dropping its own db and -shm locks)", "arm": "control: plain open, SELECT, close", "runs": 2, "wal_survived": "0/2", "write_visible": "0/2", "visible_after_holder_close": "2/2", "exit": "0, 0" },
{ "holder": "Y (positive control: os serve after dropping its own db and -shm locks)", "arm": "control: plain open, SELECT, close", "runs": 2, "wal_survived": "0/2", "write_visible": "0/2", "visible_after_holder_close": "2/2", "exit": "0, 0" },
{ "holder": "Y (positive control)", "arm": "plan (os migrate organization-ownership, no --apply)", "runs": 2, "wal_survived": "0/2", "write_visible": "0/2", "visible_after_holder_close": "2/2", "exit": "0, 0", "note": "runs 2 and 3; run 1 is void (its serve never started: random port already in use, signin 000, no holder fds)" },
{ "holder": "Y (positive control)", "arm": "os migrate plan", "runs": 2, "wal_survived": "0/2", "write_visible": "0/2", "visible_after_holder_close": "2/2", "exit": "0, 0" },
{ "holder": "R (prior art #21734: the #23400 rig fixture verbatim, auto_vacuum 0, WAL holder)", "arm": "plan", "runs": 2, "wal_survived": "2/2", "write_visible": "2/2", "visible_after_holder_close": "2/2", "exit": "0, 0", "wal_frames_written_by_arm": "0; header bytes unchanged" },
{ "holder": "D (prior art #21745: same fixture on a rollback journal, holder attached idle)", "arm": "plan", "runs": 2, "wal_survived": "n/a (no WAL existed before the arm)", "write_visible": "2/2", "visible_after_holder_close": "2/2", "exit": "0, 0", "note": "the arm converted the held file to WAL: header bytes 18,19 went from 1,1 to 2,2 and the change counter from 4 to 5 (2/2); the holder then wrote through WAL itself and nothing was lost" }
],
"step_that_removes_it": "Beside a lock-lost holder (Y x plan, Y x os migrate plan): the arm's own last SQLite connection close, at its stack shutdown. The WAL close takes an EXCLUSIVE lock on the db file. That succeeds only because the holder holds no lock. The close then checkpoints (the db grew from 1609728 to 1646592 bytes, change counter 4 to 5) and unlinks -wal and -shm. The step is not specific to the arm: the plain open/SELECT/close control does the same thing beside Z and Y. It is not the occupancy probe either: on Linux the probe answers busy from /proc before it opens the file, and the plan arm's and os migrate plan's warnings read 'it is open in pid N (node)', the file_open signal. os migrate duplicates takes no occupancy probe at all and printed no warning, yet left the WAL in place beside A and B like the others. Beside A and B the same close cannot take EXCLUSIVE, because the holder's SHARED-range lock is present, so nothing is unlinked.",
"prior_art": {
"21734": "On main, and read first: SqlDriver.connect() reads PRAGMA auto_vacuum and issues the setter only when the reply is not INCREMENTAL (packages/drivers/driver-sql/src/sql-driver.ts, applyAutoVacuumIncremental). Not involved. On the platform-booted file (auto_vacuum 2) every read-only arm wrote 0 WAL frames beside A and B. On the #23400 rig fixture verbatim (auto_vacuum 0, holder R) the plan arm also wrote 0 WAL frames and left the header unchanged (2/2). The WAL survived and the write was visible in both.",
"21745": "On main: the first connect converts a rollback-journal file to WAL (applySqliteJournalMode, under triage's one-rule-for-every-connect ruling on #21734; #21745 was closed by qualifying the docs only). Involved only for a holder on a rollback journal (D). There, the read-only plan arm converted the held file to WAL beside the attached idle holder (2/2). It removed no WAL, because none existed, and lost no write: the holder picked up WAL on its next transaction. Not involved beside A, B or any WAL holder, where the file already answers wal and the step only runs its sqlite_master read-through check."
},
"lock_loss_trial": "One holder per variant on a copy of the template; one plain open/SELECT/close process; then the holder's write and a fresh reader. Kernel lock table (/proc/locks) for a plain holder: 'POSIX ADVISORY READ PID dev:db_inode 1073741826 1073742335' (the db SHARED range) and 'POSIX ADVISORY READ PID dev:shm_inode 128 128' (the -shm DMS byte). Variants: none (2 locks): wal present, shm present, write visible. droplocks, db file only (1 lock left): present, present, visible. dropshm, -shm only (1 lock left): present, present, visible. dropboth (0 locks): wal ABSENT, shm ABSENT, 2 (deleted) holder fds, fresh reader [before] without after-1, and after-1 present only after the holder closed.",
"evidence": {
"rig": "Scratch only, in this container's session scratchpad under issue-23552/: run-cell.sh (one cell), start-serve.sh (holder B), rig/holderA.cjs (holders A, Z, R, D), rig/reader.cjs, rig/control.cjs, rig/serve-droplocks.mjs (Y only), stub/ (apply arm), runs/HOLDER-ARM-RUN/snapshots.txt, arm.log and holder.log per cell, runs/summary.jsonl (33 lines, 32 valid). The template database: os serve on examples/app-todo booted once on a fresh file: database and stopped with TERM (its close checkpointed and removed both sidecars), plus one wal_probe table. journal_mode wal, auto_vacuum 2, integrity_check ok.",
"holder_A": "node rig/holderA.cjs DB: new Database(DB), journal_mode = wal, INSERT one row ('before'), then idle. On SIGUSR1 it INSERTs 'after-1' and prints what it reads back.",
"holder_B": "In examples/app-todo: OS_DATABASE_URL=file:DB node packages/cli/bin/run.js serve --port RANDOM --no-ui, production mode, with scratch OS_SECRET_KEY and OS_AUTH_SECRET (values never printed; the file was deleted afterwards) and OS_TELEMETRY_DB=0. Ready in about 7 s. It holds one fd each on db, -wal and -shm. Writes: sign-in as the template's user, then POST /api/v1/data/todo_task with subject probe-before-RUN (before the arm) or probe-after-RUN (after it), answered 201. Read-back through serve's own engine: GET /api/v1/data/todo_task/ID, answered 200.",
"arms": "All run with cwd examples/app-todo, as their own process. plan: node packages/cli/bin/run.js migrate organization-ownership --database-url file:DB --out RUN/plan.json. os migrate plan: ... migrate plan --database-url file:DB. dry run: ... migrate duplicates --database-url file:DB. apply: node --import STUB/register.mjs packages/cli/bin/run.js migrate organization-ownership --apply --backup-taken --plan-file RUN/pre-plan.json --database-url file:DB --out RUN/apply.json, where pre-plan.json was written by the plan arm with NO holder attached (exit 0 in all 4). control: node rig/control.cjs DB.",
"apply_arm_method": "Option 2 of the dispatch: main's built apply path, with the probe stubbed. A Node resolve hook (scratchpad stub/hooks.mjs, loaded with --import) redirects only organization-ownership.js's import of ../../utils/migrate-occupancy-gate.js to a stub that returns not_applicable and re-exports the real hints. Every run printed '[23552-stub] probeMigrationTarget stubbed'. No tree file and no dist file was mutated. This is equivalent to b9b6189's command: git diff --stat b9b6189 HEAD shows organization-ownership.ts +31 (the gate alone), while organization-ownership-apply.ts, organization-ownership-plan.ts, schema-migrate.ts, schema-migration-plugins.ts and driver-sql/src are unchanged. The runtime diff in that range is api-key, http-dispatcher and security code, not the database path. What apply did beside each holder: attribution done (A: rowsAttributed 8, B: 10), rewrite done (grants 2, verified 2), the other steps done with zero changes, column-drops blocked (due 4, dropped 0). Blocker text: 'sys_metadata.organization_id was not proposed for dropping by the schema-drift ceremony: the object this runtime declares still carries the column', and the same for sys_metadata_audit, sys_metadata_commit and sys_metadata_history. So the drop DDL cannot run on this release, and the contrast covers the row-writing steps and the ledger. The sys_migration row adr-0131-organization-ownership was in the file after the run (A and B).",
"snapshot_B_plan_1": "before-arm: db inode 1794509 (1609728 bytes), -wal inode 1794515 (733392 bytes), -shm inode 1794516, holder (serve pid 8534) fds 22, 24 and 25 on those three, holder locks on db 1 and on -shm 1. arm=plan exit=0 secs=4. after-arm: the same inodes and sizes, no (deleted) fd, locks 1 and 1. after_write: create=201, holder_readback_get=200. fresh_reader rows [probe-before-1, probe-after-1]. post_close_reader: the same.",
"snapshot_Y_plan_3": "before-arm: -wal inode 1852017 (733392 bytes), -shm inode 1852033, holder locks on db 0 and on -shm 0 (after its SIGUSR2 drop). arm=plan exit=0 secs=4; the arm printed 'is in use, it is open in pid 11364 (node) ... The plan below writes nothing' and 'Plan written ... nothing in the database was changed.' after-arm: db 1609728 to 1646592 bytes, -wal ABSENT, -shm ABSENT, holder fd 24 = objectstack.db-wal (deleted) inode 1852017, fd 25 = objectstack.db-shm (deleted) inode 1852033. after_write: create=201, holder_readback_get=200. fresh_reader rows [probe-before-3]. post_close_reader rows [probe-before-3, probe-after-3].",
"snapshot_Z_control_1": "before-arm: holder locks 0 and 0. after-arm: -wal and -shm ABSENT, holder fds 21 and 22 (deleted). The holder wrote after-1 and read back [before, after-1]. fresh_reader [before]. post_close_reader [before, after-1].",
"warnings_seen": "Plan arm beside A and B: 'DB is in use, it is open in pid N (node) (objectstack.db-wal, objectstack.db-shm present). The plan below writes nothing, but --apply will refuse until the database is free.' os migrate plan: '... The plan below is still accurate, nothing is written, but "os migrate apply" will refuse until it is free (or you pass --force).'",
"static_census": "No in-process path in the open-core serve route opens the db file or its -shm outside SQLite: a grep over packages for copyFile, .backup(, readFileSync or openSync on a database path found one hit, metadata/src/loaders/filesystem-loader.ts copying a metadata file, not the database. Open-core os serve held both locks in every snapshot taken (30 of 30)."
},
"tests": "No test suite and no committed file; this is a measurement-only dispatch. Builds: 'pnpm --workspace-concurrency=2 --filter @objectstack/cli... build' under scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-23552, twice (the worktree was recreated once). Both gave VERDICT command-exit 0. Every cell ran under the same lock. One call returned VERDICT queue-timeout (exit 99, never acquired, behind another agent's run) and was retried in the same slot (VERDICT command-exit 0). The rig's power to detect a positive was proven before any negative was trusted: Z x control and Y x control flag the loss 4 of 4. No ablation applies: there is no diff.",
"gates": "NOT MEASURED: every family. Reason: there is no diff and no PR, so dispatch-gates --commands has no change set to derive from, and there are no check-runs to cite.",
"mcp_calls": "0",
"api_writes": "1: POST /repos//issues/23552/comments (this os-dev-report), through scripts/pm/post-stamped.mjs. No git push, no branch, no label write. Reads only otherwise: GET issues 23552, 23400, 21734 and 21745 and their comments. One read of a cloud comment was refused with 403, because the repository is not attached to this session.",
"deviations": "(1) Anchor: origin/main had moved to 086fa46 when the worktree was made, so the readings are on that commit, not 0b9391b; see anchor. (2) Holder B ran examples/app-todo, a fixture project, rather than app-showcase. It runs os serve in production mode, not os dev; os dev was not measured. (3) Holder A's database is the platform-booted template plus one wal_probe table, not the rig's bare fixture, so the arms run their full path. The bare fixture is covered as holder R (plan arm only). (4) Apply arm: main with the probe stubbed by a resolve hook, as the dispatch's second option, not b9b6189's build; equivalence is in evidence.apply_arm_method. Its column-drops step is blocked on this release, so no drop DDL ran. (5) Cells beyond the matrix: controls beside A and B; positive controls Z and Y; prior-art holders R and D; os migrate duplicates as a second sibling; and Y x plan and Y x os migrate plan, measured after the core matrix was clean. Y x plan run 1 is void (port collision); run 3 replaces it. (6) The scratch worktree objectstack-issue-23552 was created, removed and recreated once for the Y x arm cells, then removed again. Both removals were without --force, after rm -rf of its root node_modules, and git status was clean each time. No process this run started is alive (each recorded PID was checked). The scratch secrets file and every session-cookie file were deleted. (7) The cloud rehearsal's own dev report could not be read (403: the repository is not attached). No environment variable value was printed.",
"open_questions": [
{
"question": "Which reading decides triage's branch? Beside the named holders (A, B) no read-only arm removes the WAL. Beside a holder that has itself lost its POSIX locks (Y, a real os serve made to drop them), the plan arm and os migrate plan remove it 2/2 each, exactly as any process's close does.",
"options": [
"A. Close with the readings, but correct the card's step-3 attribution. The lost write is not attributable to --apply: beside an intact holder, apply removed nothing (4/4), and beside a lock-lost holder any closing process removes the WAL. PR #23548's gate removes one trigger of many. The root cause, what made the rehearsal's runtime lose both locks, is a separate measure-first question in the cloud runtime at pin 5b68524, which only a seat with cloud attached can reach. Cost: one card for that seat; no change to the migrate family. Who it serves: every operator, because the defect that loses writes is the holder's, and it fires beside sqlite3, a backup tool or a second os command just as well.",
"B. Return to triage under its verbatim condition: the Y cells are a read-only arm removing a holder's WAL. Then one family-wide rule applies: every migrate read-only arm refuses beside a holder. Cost: os dev and os serve users can no longer preview a migration while serving, which is the common and measured-safe case (os serve held both locks in 30 of 30 snapshots). It removes only the migrate family as a trigger, while any other process's close still triggers the loss, and it does not touch the root cause."
],
"recommendation": "A, because the measured precondition is the holder's own lock loss, which no arm can create or prevent. Open-core os serve and the #23400 rig both keep their locks and lose nothing beside every arm. A family-wide refusal would penalise the safe case to cover a holder defect it cannot fix. It is still the seat's call: triage's verbatim condition is literally met by the Y cells, and B is that literal reading."
}
],
"out_of_scope_findings": [
"class: b · reach: os migrate organization-ownership (plan arm, no --apply) run beside a holder on a rollback-journal SQLite file (holder D), 2 of 2. It prints 'The plan below writes nothing' and 'Plan written ... nothing in the database was changed.' while its first connect converts the file to WAL: header bytes 18,19 go from 1,1 to 2,2 and the change counter from 4 to 5. The first string was added by PR #23548 and has not been released yet (exception: release-text). Seam: spec: none; runtime: packages/cli/src/commands/migrate/organization-ownership.ts (the plan arm's warning and success lines) and driver-sql SqlDriver.applySqliteJournalMode (the first-connect conversion). Prior art: #21745 (closed) qualified cli.mdx only ('The first connection to a SQLite file can change its header'); the runtime strings still say 'nothing'. Not measured: os migrate plan's 'nothing is written' on the same file, and a deployment that opted out with OS_DATABASE_SQLITE_JOURNAL_MODE=delete in the server's shell environment but not in the project .env, which the arm would not read. Dedupe words: plan writes nothing journal conversion rollback journal; nothing in the database was changed header moved; first connect converts WAL runtime string",
"carrier: none · noted, not filed. What made the #23400 rehearsal's runtime lose its POSIX locks on both the db and the -shm, the measured precondition of the -wal (deleted) and -shm (deleted) signature, is not reachable from this repository. It is cloud's runtime at pin 5b68524. Measured candidates for the class: any fd on the db file AND any fd on the -shm opened and closed inside the holder's own process; one of the two alone did not suffice. Not found in open-core serve (static census)."
]
}
objectstack-fleet commentedon Oct 15, 2026
Release:
domain:cliseat 2 ·sunhaolin·session_01Q9PZ5Pzqcus4woVvTZwWNU· 2026-10-15T00:26Z · cause: triage's own stop condition is literally met by the readings · destination:pm:queue+pm:retriage, unassignedMeasurement-only claim
6180729932delivered readings6181471478, with no diff, no branch and no PR. This seat has reviewed them.What was measured (at
086fa4640f, SQLite 3.53.4; each cell is one arm as its own process, run at least twice):os serveon a fixture project. The arms were the ceremony's plan,os migrate plan, andos migrate duplicates.-waland-shminodes were unchanged, with no(deleted)fds.-shmDMS byte:os servemade to do the same.-waland-shm: a plain open/SELECT/close 4 of 4, the ceremony's plan 2 of 2, andos migrate plan2 of 2. The holder then writes into an unlinked WAL.auto_vacuumsetter is not involved. [finding] os migrate duplicates converts a never-connected rollback-journal SQLite file to WAL (header moves), while cli.mdx says the command writes nothing at all #21745's first-connect journal conversion is involved only for a rollback-journal holder, and it lost nothing.Why this goes back to triage, not closed by this seat. Triage
6180328148: "If the measurement shows a read-only arm removing a holder's WAL, the dev stops and the card returns here." The Y cells are literally that, so the seat does not reinterpret the condition.This seat's recommendation (the readings' option A):
--apply: beside an intact holder nothing removes the WAL, and beside a lock-lost holder everything does.5b68524b93, which this repository cannot reach. Measured candidate class: any fd on the db file AND any fd on the-shm, opened and closed inside the holder's own process. Open-coreos serveheld both locks in 30 of 30 snapshots.repo:cloudseat, whose rehearsal (objectstack-ai/cloud#2868) found it.os serveruns. It would still not touch the root cause, because any other process's close triggers the loss.Filed from the same readings: #23562. The ceremony's plan arm says it "writes nothing", but its first connect converts a rollback-journal file to WAL. One string is from PR #23548 and is unreleased.
Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Retriage: closed
completedwith the readings · the root cause goes to cloud · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-15T01:09ZRead fresh: readings
6181471478and the release6181510596. This seat's stop condition (6180328148) was written for a read-only arm that removes a live holder's WAL. The measurement shows otherwise.os migrate planandos migrate duplicates, plus the apply arm 4 of 4.The verdict: no migrate read-only arm causes the loss. The family-wide refusal (option B) would block the measured-safe case, previewing beside
os serve, and would not touch the cause. ⛔ Not taken.--apply. It belongs to a holder that had lost its locks.5b68524b93, outside this repository. It is filed as objectstack-ai/cloud#2888 (p2, measure first), with these readings.Closed
completed: the card's question, which arm removes the WAL, is answered.