Skip to content

Add protected fields: structured rules for values in JSON/TOML files - #33

Draft
Deep-Sixed wants to merge 8 commits into
mainfrom
claude/vigilant-darwin-1ebqfk
Draft

Deep-Sixed wants to merge 8 commits into
mainfrom
claude/vigilant-darwin-1ebqfk

Conversation

@Deep-Sixed

@Deep-Sixed Deep-Sixed commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner

A protected path covers a whole file. A protected field covers one value inside matching JSON or TOML files. This is the "structured rules" follow-up to protected paths, and the general version of Codex_Kernel's PROTECTED_JSON_FIELDS. A push needs a protected_field_update approval only when it changes that value. Other edits to the same file are ordinary content.

quorumgit repo protect-field myproject config/state.json /max_depth --agent lead
quorumgit repo protect-field myproject 'services/*/settings.toml' /limits/replicas --agent lead
quorumgit repo protect-field myproject config/state /schema_version --format json --agent lead

What changes

  • structured.py works out field changes from Git objects alone:
    • It looks at each commit that is new to the repository, using the same set of already-known commits as protected paths.
    • A commit changes a value when the value differs from its value in every parent. So a clean merge changes nothing of its own, and a change that is reverted later in the same push still counts.
    • Values are compared as canonical JSON. 1, 1.0 and true count as different; reordered object keys don't.
    • A value appearing or disappearing is a change.
    • Unparseable content counts as a change, and so do file names that can't be read back (not valid UTF-8). Rules fail closed.
    • Values are named by a JSON Pointer (RFC 6901), including ~0/~1 escapes and array indices. '' means the whole document.
  • Migration 012_protected_fields.sql:
    • Adds a protected_fields table.
    • Adds ref_updates.field_changes. The reference-transaction prepared stage replays the field changes pre-receive recorded, the same way it replays paths and scope_paths, so the operation hashes don't shift. Rows validated before the migration have NULL there and are recomputed.
  • Gate: adds the protected_field_update operation, with fields shown as path#pointer in rejection messages. approve prepare works out field changes from the clone using the same code and reports them.
  • Registry and CLI:
    • repo protect-field <repo> <glob> <pointer> [--format json|toml] [--remove] is operator-only and audited. The format comes from the glob's extension unless --format is given.
    • repo policy lists the rules.
    • protected_field_update is a new operation type, so it can have its own per-operation policy (for example, two votes).
  • README: docs, a command-reference row, and an upgrade note. The migration adds no rules, so nothing changes until an operator adds one.

Testing

  • New tests/test_protected_fields.py (11 tests):
    • pointer validation and resolution
    • only the protected value counts, and a value changed then reverted within a push still counts
    • comparison by type and value
    • values appearing, disappearing, or unparseable (including on both sides)
    • non-UTF-8 file names fail closed (Linux only)
    • globs and TOML
    • clean merges
    • operator-only and validated registration
    • a real push rejected, prepare's hash matching the hook's, approved, pushed, and consumed; other edits to the file need nothing, and changing the value again needs a new approval
    • a per-operation threshold of 2 applying to field updates
    • the CLI
  • Updated the migration-list assertions for 010.
  • pytest: 273 passed. ruff check and pyright are clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_0142xh1CDDPxBvB1EHu6PN3m


Generated by Claude Code

Integration after review fixes

Merged current main through #32, #34, and #35 into this branch without rebasing or rewriting history.

The integration review also caught and fixed the replay case where a validation has pre-derived paths but no stored field-change list (for example, an update validated before the protected-fields migration). A regression test now covers recomputing only the missing field changes without re-deriving or referencing absent path variables.

claude and others added 8 commits September 30, 2026 05:31
A protected path governs a whole file; a protected field governs one value
inside matching JSON or TOML files, named by a JSON Pointer. A push needs a
protected_field_update approval only when it changes that value.

- structured.py derives field changes from Git objects, per commit new to
  the repository (same known tips as protected paths), with combined-diff
  semantics for merges. Values compare as canonical JSON (1, 1.0 and true
  differ; key order does not). Missing <-> present is a change; content
  that cannot be parsed or read fails closed.
- Migration 010: protected_fields table, and ref_updates.field_changes so
  the reference-transaction prepared stage replays what pre-receive
  derived (NULL rows from before the migration are recomputed).
- gate: protected_field_update operation in the hook, the prepared-stage
  re-validation, and approve prepare (which now also reports fields).
- registry/CLI: repo protect-field <repo> <glob> <pointer> [--format]
  [--remove], operator only and audited; repo policy lists the rules;
  protected_field_update can take a per-operation policy override.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0142xh1CDDPxBvB1EHu6PN3m
# Conflicts:
#	README.md
#	src/quorumgit/gate.py
#	src/quorumgit/git_objects.py
#	tests/test_approval_authority.py
#	tests/test_authorization_binding.py
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