Skip to content

Enforce claim scopes at push time - #20

Closed
Deep-Sixed wants to merge 1 commit into
mainfrom
claude/eloquent-galileo-xu0ah7
Closed

Deep-Sixed wants to merge 1 commit into
mainfrom
claude/eloquent-galileo-xu0ah7

Conversation

@Deep-Sixed

Copy link
Copy Markdown
Owner

1 of 4 restoring ideas from the retired Codex_Kernel repo. The PRs are stacked: this one → approver quorum → approve request --push → ref namespaces. Merge in that order.

Problem

Declared --scope globs were only compared against other claims when a claim was granted. The pre-receive hook never looked at what a push changed. An agent that claimed src/api/** could push edits to src/db/** and the hook accepted it, which undercut the README's "overlapping edits" guarantee.

Codex_Kernel's gate did inspect the pushed content (changed paths derived from the pushed commits). This PR brings that idea back and applies it to QuorumGit's claim scopes.

Change

  • gate._new_commit_paths lists the paths changed by the commits the push introduces, using rev-list <newrev> --not --all piped into diff-tree.
    • Merge commits use the combined diff (-c). Merging already-published history such as main is not charged. Conflict resolutions and edits made inside the merge commit are.
    • --no-renames is set, so moving a file into scope still charges the path it moved from.
  • check_ref_update: when the owner pushes a claimed branch, any path outside the claim's scopes makes the push an out_of_scope_update. That needs an approval bound to the exact update. The operation includes the sorted out_of_scope_paths, so the approver sees what they are approving. The same field is added when the push is also protected or forced, so one approval covers both.
  • work.scope_matches / paths_outside_scopes / claim_scopes add real glob matching:
    • ** spans any number of directories.
    • * and ? stay within one path segment.
    • [...] character classes work.
    • A glob with no wildcards names a single file or a whole directory.
  • README: documents push-time scope enforcement and the glob semantics.

Not affected: unclaimed branches, branch deletions, and the local (no-hook) model.

Tests

tests/test_scope_enforcement.py adds 30 tests:

  • 22 unit cases for glob matching.
  • 8 real-push hook tests covering: an in-scope push; an out-of-scope push rejected and then accepted with the exact approval (which gets consumed); a follow-up push checked only against its own commits; a rename out of scope; a merge of published history not charged; an edit hidden inside a merge commit charged; an unclaimed branch not checked.

Local results: pytest 125 passed, ruff clean, pyright 0 errors.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Mc44WsAZx9hzZdWEihDk5a


Generated by Claude Code

Scopes were only compared against other claims when a claim was granted;
the hook never looked at what a push actually changed, so an agent could
claim src/** and push edits anywhere. The pre-receive hook now lists the
paths changed by the commits a push introduces (rev-list newrev --not
--all, combined diff for merges, renames split) and requires an approval
bound to the exact update, including the sorted out_of_scope_paths, when
the owner of a claimed branch touches anything outside its scopes.

This restores the content-aware check the predecessor Codex_Kernel gate
performed on incoming objects, applied to QuorumGit's claim scopes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mc44WsAZx9hzZdWEihDk5a

Copy link
Copy Markdown
Owner Author

Closing in favour of #10, which enforces claim scopes in the hook as part of a broader change (it also moves approval consumption into the reference-transaction hook). The repository owner chose to keep #10.

The branch stays in place because #21 and #22 are stacked on it. tests/test_scope_enforcement.py on this branch has hook cases worth comparing against #10's coverage:

  • a rename out of scope charges the old path;
  • a merge of published history is not charged;
  • an edit hidden inside a merge commit is charged;
  • a follow-up push is checked only against its own commits.

Any that #10 lacks can be ported there.


Generated by Claude Code

@Deep-Sixed Deep-Sixed closed this Sep 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants