Add claim/ack API + PID sentinel helper for atomic delivery#372
Open
u-ichi wants to merge 1 commit into
Open
Conversation
Motivation External delivery daemons plug in as a receiver for grok-build's rule-file self-poll fallback or a future explicit-daemon mode. Today's inbox.sh --quiet reads unread rows and marks them read in two separate SQL statements. When such a daemon consumes messages, an inject failure between those two statements causes silent message loss, and a race between two receivers can cause duplicate delivery. This change adds a small claim/lease/ack API that lets any receiver atomically hold a message before consuming it: - inject failure -> release, next poll retries (no loss) - concurrent receivers -> only one wins the claim (no duplicate) - process crash -> lease expires, another receiver retries The API is orthogonal to the existing monitor watcher in grok-build: upstream's monitor watcher could adopt these helpers to atomicize its own delivery loop. Changes - scripts/internal/init-db.sh Add claims table (message_id PK, owner, expires_at) + expiry index. Uses CREATE TABLE IF NOT EXISTS so existing DBs upgrade in place. - scripts/lib/claims.sh (new, 52 lines) agmsg_claim_next / agmsg_ack_claim / agmsg_release_claim built around a single BEGIN IMMEDIATE transaction with INSERT OR IGNORE + expiry GC. - scripts/receiver-live.sh (new, 12 lines) Small helper: read pid=N from an agent's ready sentinel and kill -0 it. Exits 0 if live, non-zero if stale or missing. Rule files or watchers can use it to gate a fallback path. - tests/test_claims.bats (new, 5 cases) Excludes already-claimed / release + reclaim + ack / wrong-owner ack rejection / expired claim reclaim / newline+tab body round-trip. Notes - No existing behavior is changed; nothing calls the new API yet. - Driver-side integration is planned as a follow-up so it can be discussed against the current monitor design.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #373 · related to #338
Motivation
External delivery daemons (e.g. downstream projects that build their own delivery pipe on top of agmsg) plug in as a receiver for grok-build's rule-file self-poll fallback or a future explicit-daemon mode. Today's `inbox.sh --quiet` reads unread rows and marks them read in two separate SQL statements. When such a daemon consumes messages, an inject failure between those two statements causes silent message loss, and a race between two receivers can cause duplicate delivery. See #373 for the full failure model.
This PR adds a small claim / lease / ack API that lets any receiver atomically hold a message before consuming it:
The API is orthogonal to the existing monitor watcher in grok-build. Upstream's monitor watcher could adopt these helpers to atomicize its own delivery loop as a follow-up, and downstream external daemons can start using them immediately.
The `receiver-live.sh` helper also composes with #338 Gap 2 (spawn readiness) as a shared liveness primitive — flagged in the comment thread there.
Changes
Notes
🤖 Prepared with Claude Code