Back to notes

Project history

The second operator is the test

sprintctlThe CLI and schema that own sprint work, dependencies, reservations, and handoffs. and actionqThe PostgreSQL-backed queue that owns actions, sessions, claims, and outcomes. enforce useful execution discipline, but five current invariants rely on one operator remaining the only source of identity, authority, and audit judgment.

Agent workflow / Published Jul 19, 2026 / Revised Jul 22, 2026

Vuoro
The public label for this family of small, separately owned agent-workflow tools.
sprintctl
The CLI and schema that own sprint work, dependencies, reservations, and handoffs.
actionq
The PostgreSQL-backed queue that owns actions, sessions, claims, and outcomes.
On this page

No second human operator, no coordination system. Only disciplined self-management.

Within the Vuoro project, sprintctl owns work items, claims, dependencies, and handoffs; actionq owns queued machine actions and their execution lifecycle. Both can arbitrate concurrent worker sessions. That is not the same as arbitrating between people. Their current contracts assume one human authority behind every actor string, claim token, queue worker, and completion decision. Here N=2 means two independent human authorities, not two agents operating for the same person. The single-operator assumption is load-bearing in five places.

The claimant can certify completion

An active exclusive execute claim is sufficient completion authority. item done-from-claim checks the claim type, exclusivity, expiry, ID, and token, then moves the work item to done and normally releases the claim. The work_item schema has one status and no submission, review, or acceptance relation. With one operator, execution proof and the decision that the work is sufficient can be the same act. At N=2, the implementer can certify their own result and the system cannot express a reviewer disagreeing without rewriting the item state by convention.

Names stand in for principals

Actor labels are descriptive text, not authenticated identity. The sprintctl schema stores work-item assignee, event actor, and claim agent as text, while its own operating guide says actor, instance, host, and process metadata are advisory only. Actionq likewise stores created_by, claimed_by, and event actor as TEXT, and its CLI accepts the worker and actor labels from the caller. With one operator, a name is a useful note because the reader already knows who caused the event. At N=2, either operator can write the other’s name, assignment cannot grant permission, and the audit log records a claim about identity rather than identity established by the system.

Expired ownership is disposable

Update, 2026-07-19 — partially fixed. PostgreSQL expiry now marks active claims expired and retains their rows. Reacquisition preserves the old row before creating its replacement. The current verification note also states the remaining boundary: SQLite expiry still deletes, and proof-bearing release still deliberately deletes an active claim. The original paragraph below records the behavior at the audited commit; it is no longer the complete PostgreSQL behavior.

Expired sprintctl claims are deleted, not retained as claim history. The SQLite maintenance path executes DELETE FROM claim, and the PostgreSQL path does the same for rows past backend time. The documented remote-mode checklist still tells an operator to schedule that purge. No retained claim-history model exists in these paths: claim creation and expiry purge do not append matching lifecycle events. With one operator, deletion is tolerable because the person reconstructing the incident is the person who held the claim. At N=2, expiry erases who occupied the work, when their authority lapsed, and which later attempt replaced it; the remaining item state cannot reconstruct the deleted lease.

Expiry disciplines the holder but does not fence them

Update, 2026-07-19 — schema landed, fencing did not. Sprintctl now stores a lease_epoch: remote token rotation increments it, and remote reacquisition advances it across retained claim rows. The completion note says there is no expected_epoch command input and no downstream enforcement. The old “no epoch” statement is now historical. The invariant in this section remains: expiry still cannot fence a stale holder’s external work, and actionq still does not bind terminal transitions to its current worker.

A claim deadline is a coordination cue, not a fencing epoch. Sprintctl’s claim ownership protocol states that expiry cannot reject stale downstream work, and its retained direct backend admits a stale heartbeat after another owner has claimed the item; the opt-in remote arbiter repairs that renewal history but still makes no downstream fencing claim. The claim schema has tokens and expiry but no epoch. Actionq is looser: its complete, fail, and reject transitions check only that the action is currently claimed, not that the caller is the current claimed_by worker, and its protocol names this limitation explicitly. With one operator, TTL and sweep are self-discipline. At N=2, a timed-out worker can continue external writes or win a terminal transition after reassignment, so the newer claim does not make the older execution harmless.

Repository identity carries project authority

The repository is both storage scope and authority scope. PgStore carries a repo_id that is prepended to every query as a tenant discriminator, and backend resolution derives that ID from the repository directory or marker. Actionq’s dispatch server requires one concrete repo_id and writes it directly into the action’s project field. There is no separate project, membership, or principal boundary in these schemas. With one operator, project equals repository because both names point to the same personal scope. At N=2, a project spanning repositories has split authority, two projects sharing a repository cannot have separate policy, and database access to a repository tenant is indistinguishable from permission to operate all of it.

Five things, not one

The conceptual repair is small even if an implementation would not be: assignment names the accountable party; a claim grants temporary execution rights; an attempt records one session and its artifacts; a submission presents an attempt for judgment; acceptance is the authorized decision that the work item is complete. None implies the next. This is what a multi-operator fix would have to express, not a statement of build intent.

Verification boundary

This inventory was checked against sprintctl commit 11519c4 and actionq commit 85b72bd on 2026-07-19. Code, current schema, tests, and protocol documents outrank the project narrative. The claims above are limited to those two revisions. The N=2 consequences and five-part resolution are analysis, not implemented state. The dated updates were checked against sprintctl commit 80aaa97; actionq remained at the audited revision.

Coordination infrastructure without a second operator is design theater. The correct output is the analysis.

Reference:MarkdownDownload

Optional exploration template

Portable task language, not part of the note.

A post-hoc prompt for applying and extending this note. It is not a reconstruction of how the note was written.

This note is superseded by "Design internal pipelines for succession, not just operation", which absorbs its inventory into a succession model with the operating drill it motivated; read that first. This note remains the project-history record of the audited findings. The transferable question: which of your system's invariants quietly depend on one person being the only source of identity, authority, and audit judgment? The worked case audited two coordination tools against specific commits and found five such invariants, then made a small conceptual repair: assignment names the accountable party, a claim grants temporary execution rights, an attempt records one session and its artifacts, a submission presents that attempt for judgment, and acceptance is the authorized decision that the item is complete -- and none of those implies the next. The note is careful about its own standing: the inventory is verified against named revisions, while the consequences and the five-part resolution are analysis rather than implemented state. Its conclusion is that coordination infrastructure without a second operator is design theatre, and the correct output was the analysis rather than a build. Apply the question to a system you operate alone. Audit it against a specific revision, list what collapses if a second principal appears, and say which of the five your model conflates. Then read the successor for what to do about it.