Task 7: SQLite last-synced state store #7
Labels
No labels
data-integrity
engine
platform
procurement
remote
scaffold
ui
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: Cordy/cairn-desktop#7
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Depends on Task 6.
Goal
Persist what we recorded after the last successful sync of every path. This is the third leg of every three-way comparison — without it the engine can only do two-way diffing, which is how naive sync clients delete data.
Files
internal/state/state.go,internal/state/state_test.goProduces
Driver — do not substitute
Use
modernc.org/sqlite(pure Go). Notmattn/go-sqlite3, which requires cgo and would breakCGO_ENABLED=0, cross-compilation, and the arm64 CI runner. This is a global constraint inCLAUDE.md.Import it for its side effect and open with driver name
"sqlite":Steps
PutthenGetround-trips every field exactlyGeton a missing path returns(_, false, nil)— missing is not an errorPuttwice on the same path upserts, leaving one row with the newer valuesDeleteremoves the rowAllis sorted by pathpathis the primary key. UseINSERT … ON CONFLICT(path) DO UPDATE SET …for the upsert. Create the schema inOpenwithCREATE TABLE IF NOT EXISTS.git commit -s -m "feat(state): SQLite last-synced state store (pure Go driver)"Acceptance criteria
IsDiracross the int/bool boundary.go buildsucceeds withCGO_ENABLED=0.Done
What was built
internal/statepackage:Entrystruct,Open/Get/Put/Delete/All/Closeon*Store, backed bymodernc.org/sqlite(pure Go, no cgo).Open(CREATE TABLE IF NOT EXISTS),pathas primary key, upsert viaINSERT … ON CONFLICT(path) DO UPDATE SET …covering all 8 non-key columns.SetMaxOpenConns(1)) so:memory:DSNs see one consistent database (R9).modernc.org/sqlite@v1.46.1pinned (not@latest) to keepgo.mod's directive at exactlygo 1.25(R1).TestPutUpsertReplacesEveryField, a full-field upsert probe (all 9 fields,IsDirtrue→false) that the issue's ownTestPutIsUpsert(checking onlyRemoteETag) did not guard.Tests
internal/state/state_test.go: round-trip (+IsDir), missing-is-not-error, upsert (issue's verbatim test plus the new full-field test), delete, sortedAll.go vet ./...andgo test -count=1 ./...pass forinternal/remote,internal/state,internal/sync,internal/vfs.Acceptance criteria
IsDiracross the int/bool boundary" — met:TestPutGetRoundTrip/TestPutGetRoundTripIsDircover every field;TestPutUpsertReplacesEveryFieldcovers the upsert path too.go buildsucceeds withCGO_ENABLED=0" — met: verified locally, cross-compiled for linux/amd64 and linux/arm64, and reconfirmed by the green CI run above.Rulings
go 1.25, notoolchainline; newest dependency whose own directive is ≤ 1.25 (sqlite pinned to v1.46.1).Openpins the pool to one connection (SetMaxOpenConns(1)) at least for:memory:DSNs.git commit -s, with theCo-Authored-By: Claude Opus 5trailer.Deferred
1.25/1.25.0"numerically identical"); the fix itself (pinning to v1.46.1) is correct.Implemented and reviewed by Claude (subagent-driven), landed on main after review and green CI.