Skip to content

fix(sdk): stamp writer metadata on every store mutation#477

Merged
peteski22 merged 3 commits into
mainfrom
fix/stale-writer-metadata
Jun 26, 2026
Merged

fix(sdk): stamp writer metadata on every store mutation#477
peteski22 merged 3 commits into
mainfrom
fix/stale-writer-metadata

Conversation

@peteski22

@peteski22 peteski22 commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

What

The store metadata (last_writer, last_write_at) was stamped only at store construction, during ensureSchema/ensureMetadata. After the first connection, every insert/update/delete left the metadata stale — lying about the most recent write. This affected all persistent store implementations across both SDKs (SQLite and PostgreSQL).

For a shared multi-agent PostgreSQL store, operators want this metadata to reflect which agent actually wrote last. This PR stamps the writer on every mutation, inside the same transaction as the mutation itself, so the metadata stays accurate and the stamp is atomic with the write.

How

  • Go SDK: stampWriter becomes a free function taking a small execer interface, satisfied by both a connection/pool and a transaction, so it can run within a mutation's transaction. The PostgreSQL Delete is now wrapped in a transaction so the stamp is atomic with the delete.
  • Python SDK: _stamp_writer no longer commits on its own and runs within the caller's transaction. The PostgreSQL delete is wrapped in a transaction for the same atomicity.
  • In-memory stores are unaffected (they hold no metadata table).

Testing

  • Added Go and Python tests asserting the writer timestamp is refreshed on insert, update, and delete.
  • make lint: clean (0 issues)
  • make test: Go 456, Python 308 (+11 skipped), frontend 11 — all pass

Fixes #470

Summary by CodeRabbit

  • New Features

    • Write operations now refresh “last writer” metadata across the SQLite, PostgreSQL and Python stores.
    • Insert, update and delete now record the latest write activity more consistently.
  • Bug Fixes

    • Metadata updates are applied in the same transaction as the data change, improving consistency after successful writes.
    • Delete and update no longer advance write timestamps when no rows are affected.
  • Tests

    • Added coverage to verify writer-stamp metadata is updated after insert, update and delete operations.

The last_writer/last_write_at metadata was stamped only at store
construction, so after the first connection it stopped reflecting the
most recent write. Stamp it inside the same transaction as every
Insert, Update, and Delete in the SQLite and PostgreSQL stores so a
shared multi-agent store can identify which SDK wrote last.

stampWriter becomes a free function taking an execer interface,
satisfied by both a connection/pool and a transaction, so it can run
within a mutation's transaction. The PostgreSQL Delete is wrapped in a
transaction so the stamp is atomic with the delete.

Refs #470
The last_writer/last_write_at metadata was stamped only at store
construction, so after the first connection it stopped reflecting the
most recent write. Stamp it inside the same transaction as every
insert, update, and delete in the SQLite and PostgreSQL stores so a
shared multi-agent store can identify which SDK wrote last.

_stamp_writer no longer commits on its own and runs within the caller's
transaction; the PostgreSQL delete is wrapped in a transaction so the
stamp is atomic with the delete.

Fixes #470
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ac6d02cc-cb58-4438-8946-68f44001ce4e

📥 Commits

Reviewing files that changed from the base of the PR and between f0bcc8b and e495208.

📒 Files selected for processing (3)
  • sdk/go/store_sqlite.go
  • sdk/go/store_sqlite_test.go
  • sdk/python/tests/test_store.py

Walkthrough

Writer metadata stamping now runs during SQLite and PostgreSQL insert, update, and delete operations in the Go and Python SDKs. Schema initialisation also writes the metadata within transactional scope, and tests verify the writer keys change after mutations.

Changes

Writer metadata stamping across SDK stores

Layer / File(s) Summary
Go SQLite stamping
sdk/go/store_sqlite.go, sdk/go/store_sqlite_test.go
stampWriter accepts transaction-capable executors, newSQLiteStore stamps metadata on open, and Delete, Insert, and Update call it before commit.
Go PostgreSQL helper
sdk/go/stores/postgres/postgres.go
execer and stampWriter are split into a standalone helper that writes last_writer and last_write_at with Exec, and ensureSchema calls it on the pool.
Go PostgreSQL writes
sdk/go/stores/postgres/postgres.go
Delete now runs in an explicit transaction, and Insert and Update stamp writer metadata inside their transaction scopes before commit.
Python SQLite schema stamping
sdk/python/src/cq/store.py
_ensure_schema() stamps inside with self._conn:, and the extra schema commit is removed.
Python SQLite writes
sdk/python/src/cq/store.py, sdk/python/tests/test_store.py
insert, delete, and update call _stamp_writer() inside the transactional connection scope, and the metadata tests assert both writer keys are restamped after each mutation.
Python PostgreSQL writes
sdk/python/src/cq/stores/postgres.py
insert, update, and delete call _stamp_writer() within their transactional write paths after the data and domain-index changes.

Possibly related PRs

  • mozilla-ai/cq#476: This PR’s writer-metadata changes build on the earlier context propagation refactor used by the Go SQLite and PostgreSQL store paths.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR fixes mutation-time stamping in SQLite and PostgreSQL, but it does not address the in-memory store mentioned in issue #470. Add equivalent writer-stamp handling for the in-memory store, or document that issue #470 is limited to persistent stores.
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: stamping writer metadata on every store mutation.
Out of Scope Changes check ✅ Passed All code changes stay within writer-metadata stamping and related tests across the affected SDK stores.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/stale-writer-metadata

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@peteski22 peteski22 requested a review from Copilot June 26, 2026 10:06
@peteski22 peteski22 added sdk sdk-go For issues or PRs related to the Go SDK sdk-python For issues or PRs related to the Python SDK labels Jun 26, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@sdk/go/store_sqlite_test.go`:
- Around line 914-937: The mutation metadata test only verifies keyLastWriteAt,
but it should also confirm keyLastWriter is restamped after each mutation. In
the store_sqlite_test.go test around the Insert, Update, and Delete paths, reuse
the existing overwrite-and-read pattern for keyLastWriter alongside
keyLastWriteAt, then assert that both values change after s.Insert, s.Update,
and s.Delete. Use the existing readTimestamp helper pattern as a guide and
locate the relevant logic by the keyLastWriteAt/keyLastWriter constants and the
s.Insert, s.Update, and s.Delete calls.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: cb4f7650-cd97-49fe-bfe0-e480d8442665

📥 Commits

Reviewing files that changed from the base of the PR and between 1117429 and f0bcc8b.

📒 Files selected for processing (6)
  • sdk/go/store_sqlite.go
  • sdk/go/store_sqlite_test.go
  • sdk/go/stores/postgres/postgres.go
  • sdk/python/src/cq/store.py
  • sdk/python/src/cq/stores/postgres.py
  • sdk/python/tests/test_store.py

Comment thread sdk/go/store_sqlite_test.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes stale store metadata (last_writer, last_write_at) by stamping writer/timestamp on every persistent-store mutation (insert/update/delete) and ensuring the stamp is performed in the same transaction as the mutation for atomicity (notably for PostgreSQL deletes).

Changes:

  • Python: Move writer stamping into the mutation transactions for both SQLite (SqliteStore) and PostgreSQL (PostgresStore), and stop _stamp_writer from committing independently.
  • Go: Make PostgreSQL writer stamping transaction-friendly via a small execer interface and stamp on every mutation; ensure deletes are wrapped in a transaction for atomic stamping.
  • Tests: Add mutation-level writer-stamp refresh coverage for Python SQLite and Go SQLite.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
sdk/python/tests/test_store.py Adds tests asserting last_write_at is refreshed on insert/update/delete for the local (SQLite) store.
sdk/python/src/cq/stores/postgres.py Stamps writer metadata on insert/update/delete and wraps delete in a transaction for atomicity.
sdk/python/src/cq/store.py Makes _stamp_writer transaction-scoped (no internal commit) and calls it on each SQLite mutation.
sdk/go/stores/postgres/postgres.go Refactors writer stamping to work with both pool/tx and stamps within mutation transactions (including delete).
sdk/go/store_sqlite.go Stamps metadata within SQLite mutation transactions by making stampWriter accept either DB or tx executors.
sdk/go/store_sqlite_test.go Adds a regression test ensuring last_write_at changes on insert/update/delete.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread sdk/go/store_sqlite.go
The SQLite stampWriter was the only statement in the mutation
transactions still using a context-less Exec, so the metadata write
ignored the caller's cancellation while every other statement in the
same transaction honored it. Switch the execer interface to
ExecContext and thread the caller's context through Insert, Update,
and Delete.

Inline the single-caller ensureMetadata into newSQLiteStore so the
construction-time stamp, which has no caller context, lives at the
constructor with an explicit background context rather than fabricating
one inside a helper.

Also assert in the Go and Python tests that both last_writer and
last_write_at are restamped on every mutation, so a regression that
drops either write is caught.

Refs #470
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

sdk sdk-go For issues or PRs related to the Go SDK sdk-python For issues or PRs related to the Python SDK

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Store metadata stamp becomes stale after initial connection

2 participants