Repository navigation
Bind push authorization to Git's ref transaction; scope enforcement and full schema contract - #10
Closed
Deep-Sixed wants to merge 10 commits into
Closed
Deep-Sixed wants to merge 10 commits into
Deep-Sixed wants to merge 10 commits into
Conversation
- Approvals: the agent that consumes an approval (pusher or takeover claimant) never counts toward its threshold, and a takeover's beneficiary cannot vote yes. Approved-but-unused instances still accept votes, so an independent approver can replace a self-vote and a deny revokes it before use. - Claims: a reclaim of an expired lease or an approved takeover on the incumbent's branch inherits its managed worktree instead of failing because Git will not check the branch out twice. - Scopes: globs are normalized (./, //, leading /, backslashes, ..) and overlap is compared case-insensitively; scopes escaping the repository are refused. - Hook: pushes to a claimed branch are rejected when the new commits touch paths outside the claim's scopes (merge commits count only paths the merge itself changed). - CLI: commit specs resolve to full OIDs (short SHAs, refs, uppercase); duplicate agent/protected-ref registration and malformed operation JSON are clean errors; task and agent creation take the write reservation. - handoff create honors an explicit --last-commit and no longer crashes when the claim's worktree has been removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8BpCkArA34hB1aSyVcnZp
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8BpCkArA34hB1aSyVcnZp
Pre-receive used to consume approvals and commit before Git updated the ref, so an update Git later refused still spent its approval, and a claim release, takeover, or handoff between pre-receive and the ref update let a push land on authority that no longer held. - pre-receive now validates and records each exact ref update (ref_updates, migration 003) without consuming anything. - A managed reference-transaction hook re-validates every recorded update at the `prepared` stage, while Git holds the ref locks and can still abort, and consumes approvals there. Failure aborts the update. `committed`/`aborted` record the outcome, and an abort after `prepared` restores the consumed approval. - pre-receive refuses pushes when the transaction hook is missing or modified; hook install writes both hooks and inspects both before writing either. Ref transactions no push validated pass through. - Repository identity lookups no longer inherit the hook's GIT_DIR, which resolved every registered path to the invoking repository. verify_contract() now replays the bundled migrations in memory and requires every table, index, and trigger to match, rejecting missing or altered objects (append-only audit triggers, single-owner and single live approval unique indexes, identity triggers), unexpected indexes or triggers, and stores migrated by a newer version. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8BpCkArA34hB1aSyVcnZp
A governed ref update whose committed/aborted hook never ran (killed process, unreachable store) stayed `prepared` forever, with its approval consumed whether or not Git applied it. `quorumgit doctor` now reports updates prepared for longer than the validation window and reads the outcome from the ref: at the new value (or gone, for a deletion) is committed; still at the old value is aborted, and --repair restores the consumed approval. A ref that has moved elsewhere or is still locked is reported, never guessed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8BpCkArA34hB1aSyVcnZp
Deep-Sixed
marked this pull request as ready for review
September 29, 2026 08:57
Resolves README conflicts with the docs-alignment change on main, keeping its added detail (identity over local remotes, hub handoff accept, command flags, destroy semantics) while updating the statements this branch makes untrue: an agent's own vote never counts toward an approval it uses, and handoff create honors an explicit --last-commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8BpCkArA34hB1aSyVcnZp
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cc88d14851
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Addresses review findings on the reference-transaction boundary: - pre-receive also requires the transaction hook to be an executable regular file; Git silently skips a non-executable hook, which would have skipped re-validation and approval consumption. - An identified transaction (every push carries QUORUMGIT_AGENT) whose pre-receive validation expired or is missing is rejected instead of passing through as local maintenance. Only unidentified transactions pass through. Classification happens before taking the write reservation, and symbolic refs such as HEAD, which Git lists beside the ref they point to, are left to that ref's own line. - With the store missing, an identified `prepared` transaction fails closed instead of exiting 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8BpCkArA34hB1aSyVcnZp
Resolves conflicts with the move from libSQL to Python's sqlite3 module: the in-memory schema replay behind verify_contract() now uses sqlite3, and restoring an approval catches sqlite3.IntegrityError, which is what the standard library raises for the one-live-approval unique index. The README keeps main's SQLite 3 requirements and adds this branch's Git 2.28+ requirement for the reference-transaction hook. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8BpCkArA34hB1aSyVcnZp
When Git aborts a transaction after `prepared`, the consumed approval is restored unless the same operation was re-requested meanwhile. Restoring then would leave two live approvals for one operation, so the old one stays consumed and `approval.restore_skipped` is recorded. The test covers that path, which depends on catching sqlite3.IntegrityError: with the libSQL-era ValueError catch the constraint error escapes and the abort is never recorded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8BpCkArA34hB1aSyVcnZp
…nce fixes Resolves conflicts with #13 and the ruff cleanup on main: - checkpoint, handoff create, and worktree inheritance use main's trees.active_worktree_for_claim(); the branch's duplicate CLI helper is dropped. handoff create keeps this branch's rule that an explicit --last-commit wins over the worktree's HEAD, and the README says so. - Keeps main's explicit check= on subprocess calls and adds it to this branch's remaining call, so `ruff check` reports no findings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8BpCkArA34hB1aSyVcnZp
This was referenced Sep 29, 2026
main's #12 now owns two things this branch also fixed, more completely: - Approval authority: agent roles and per-repository policy, requesters and takeover beneficiaries barred from voting, and approvers barred from consuming. This branch's independent-vote rule, approve-after- approved voting, and their tests are dropped in favour of it. The push path's re-validation at the reference transaction's `prepared` stage now authorizes through main's approved_instance/approval_refusal with the pusher as consumer. - Retained-worktree continuation: main's continue_or_create_worktree replaces this branch's inherit_worktree and its duplicate tests. Everything else in this branch is kept: the reference-transaction boundary, push-time scope enforcement, the full schema contract, doctor reconciliation, scope normalization, commit resolution, and handoff --last-commit precedence. CLI commands use main's store.session, and migration 003_ref_updates sits between 002 and main's 004. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8BpCkArA34hB1aSyVcnZp
Owner
Author
|
Closing this in favour of smaller PRs.
The remaining pieces land separately:
This branch is left in place as the reference for that follow-up. Generated by Claude Code |
Deep-Sixed
pushed a commit
that referenced
this pull request
Sep 30, 2026
From the session that rebuilt this boundary on #10's branch (44a6b5f): - A force push refused by receive.denyNonFastForwards, which Git checks after pre-receive, keeps its approval and the retry uses it. On main the approval is consumed and the branch does not move. - An update needing two approvals (protected ref and protected path) consumes both at `prepared`, records both in approval_ids, and restores both on `aborted`. - A non-atomic push of main and a claimed branch whose commits include main's still matches the out-of-scope operation that was approved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q8BpCkArA34hB1aSyVcnZp
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 findings from a code review of
mainatd73bb83, plus two raised afterwards: the pre-receive timing gap andverify_contractnot checking indexes and triggers. Each fix comes with regression tests (tests/test_governance_fixes.py,tests/test_transaction_boundary.py). I ran the key tests against the previous code, and they fail there for the intended reason. Supersedes #9, which is now closed.Changes
main's Move approval authority to the repository and continue retained worktrees #12: agent roles, a per-repository approval policy, and a rule that an agent who approved an operation can't be the one to carry it out. This PR's original fix and its tests were removed in favour of it. The push path below authorizes through Move approval authority to the repository and continue retained worktrees #12's rules, with the pusher as the agent using the approval.continue_or_create_worktree). This PR's duplicate code was removed../,//, a leading/, backslashes,..). Overlap is compared case-insensitively, and scopes that point outside the repository are refused.handoff create --last-commitis honored even when the claim has a worktree.ref_updatestable (migration003_ref_updates.sql, between002andmain's004). It no longer consumes anything.preparedstage: Git'sreference-transactionhook re-checks every recorded update against current state while Git holds the ref locks and can still abort. Approvals are consumed only here, and any failure aborts the update.committed/abortedstages: these record the outcome. An abort afterpreparedreturns the consumed approval to usable. If the same operation was requested again in the meantime, the old approval stays used up so there are never two live approvals for one operation.hook installinspects both hook paths before writing either. It refuses to overwrite a hook QuorumGit didn't write and requires Git 2.28 or newer.verify_contractchecks the whole schema. It replays the bundled migrations into an in-memory database and requires every table, index and trigger in the store to match (ignoring formatting). It rejects missing or altered objects, indexes or triggers that shouldn't be there, and stores migrated by a newer version.GIT_DIR. With an absoluteGIT_DIR, every registered path resolved to the hub.doctorsettles ref updates stuck inprepared. It reads the outcome from the ref itself: at the new value → committed; still at the old value → aborted, and--repairrestores the approval; moved elsewhere or still locked → reported, never guessed.Notes for reviewers
quorumgit hook install --repo <name>. Until then, pushes are rejected with a message saying the transaction hook is missing.main:sqlite3): the schema replay usessqlite3. Restoring an approval catchessqlite3.IntegrityError, the error raised when the one-live-approval index is hit.active_worktree_for_claimand follows Fix pre-existing ruff lint findings #15's explicitcheck=convention.Validation
pytest tests -q: 185 passedpyright: 0 errorsruff check(the pinned 0.16.9): no findings🤖 Generated with Claude Code
https://claude.ai/code/session_01Q8BpCkArA34hB1aSyVcnZp