Refuse a second SQLite service before migration #183

Closed
weichen wants to merge 5 commits from feature/u8-sqlite-instance-guard into main
5 Commits
Author SHA1 Message Date
weichenandCursor 167609b3db Merge origin/main into the SQLite instance guard
ci / vet-and-test (pull_request) Successful in 44s
Why: main now rejects unknown subcommands and registers schema compatibility, so the guard branch could not merge until migrate stayed in that switch and v28 was registered.
Evidence: go test ./internal/instanceguard ./internal/app ./internal/store ./cmd/agent-system ./cmd/storage-migrate passed locally after the merge.
Impact: the activity lock and durability checks stay in front of open. v28 does not raise the old-reader floor because instance_guard is a new table.
Refs: cmd/agent-system/main.go, internal/store/migrate_compat.go
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-12 15:46:22 +00:00
weichenandCursor ef66bfb331 Reject a rebuilt lock even when its inode is reused
ci / vet-and-test (pull_request) Successful in 51s
Why: deleting the lock and creating a new file can recycle the same inode, so an inode-only check treated the rebuilt lock as the original.
Evidence: SELF go test ./internal/instanceguard ./internal/store ./internal/app, including TestClearedLockRecordRefusedWhenInodeStays.
Impact: instance_guard and the lock file now share a random lock_gen. A missing or different generation refuses Bind unless adopt.
Refs: docs/recovery/u8-sqlite-instance-guard.md
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-12 15:07:29 +00:00
weichenandCursor 15e7ec5f1f Register instance_guard for copy and avoid recycled inodes
ci / vet-and-test (pull_request) Failing after 54s
Why: CI rejected the new table as missing from the storage-migrate copy list, and the replacement test passed on filesystems that reused the deleted inode.
Evidence: SELF go test TestCopyOrderCoversAllFixtureTables and TestDatabaseReplacementRefusedBeforeOpen.
Impact: SQLite to PostgreSQL copy keeps the guard row. The replacement test burns inodes until the new file is distinct.
Refs: docs/recovery/u8-sqlite-instance-guard.md
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-12 14:57:03 +00:00
weichenandCursor 1f7bcac6e0 Keep the activity lock from creating or aliasing the database
ci / vet-and-test (pull_request) Failing after 44s
Why: Review found that a missing data directory failed before open, adopt recreated a missing database, a lock symlink could be written as the database, and a failed identity check stored the wrong instance id.
Evidence: SELF go test TestMissingDataDirIsCreatedBeforeLock, TestLockSymlinkCannotAliasDatabase, TestAdoptDoesNotCreateMissingDatabase, TestIdentityFailureDoesNotStick.
Impact: First start creates the data directory before taking the lock. Adopt still refuses a missing database. The lock file is opened with O_NOFOLLOW and rejected when it is the database. Identity is checked before the guard row is written.
Refs: docs/recovery/u8-sqlite-instance-guard.md
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-12 14:16:55 +00:00
weichenandCursor 89acd16ca5 Refuse a second SQLite service before migration
Why: Two processes opening the same SQLite file both migrate and reconcile, so the second one seals the first one's live runs. The activity lock is taken before open, and durability settings are read back on the application connection.
Evidence: SELF go test ./internal/instanceguard ./internal/store ./internal/app (filtered T8 cases) on feature/u8-sqlite-instance-guard. PG durability readback is NOT_RUN locally; Dev CI test-backend-pg is the GATE.
Impact: SQLite serve and migrate share one non-blocking flock. A replaced database or rebuilt lock file refuses start unless --adopt-instance-guard. PostgreSQL serve refuses when fsync, full_page_writes, or synchronous_commit is off.
Refs: docs/recovery/u8-sqlite-instance-guard.md
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-12 13:53:42 +00:00