Summary
rhiza-hooks has gained a check-managed-files hook (Jebel-Quant/rhiza-hooks#304,
shipped in Jebel-Quant/rhiza-hooks#309). It fails a commit that modifies a path listed in
.rhiza/template.lock's files: block, minus anything under exclude: in
.rhiza/template.yml.
It exists because the rule every managed repo's CLAUDE.md opens with — managed files
are overwritten on the next sync — has had nothing enforcing it since make validate
was removed after v1.1.3. The failure it catches is silent and total: the edit works, is
reviewed, is merged, and disappears at the next sync.
This issue is not a request to adopt it. It is a request to make the sync path safe
first, because adopting it before that would break sync for every consumer.
Why the sync path breaks
A sync commit modifies template-owned files wholesale — that is its entire purpose — so
it is precisely the commit this hook is built to reject.
Concretely, in /rhiza:update (Jebel-Quant/rhiza-claude, commands/update.md step 7):
uv run --python 3.12 --no-project python "${CLAUDE_PLUGIN_ROOT}/scripts/stage_synced.py" .
# "It reads the lock's `files` list, stages exactly that plus template.yml/the lock"
...
git commit -m "chore: apply rhiza sync $TARGET"
stage_synced.py stages exactly the lock's files: list. check-managed-files reads
exactly the same list. So with the hook enabled and pre-commit installed, that commit
fails 100% of the time, in every consumer, on every sync.
What is already safe
Two cases need no change, so the fix is narrower than it might look:
- The ref-bump commit (
commands/update.md step 4) stages only
.rhiza/template.yml. That file is project-owned, not template-owned, so the hook
ignores it.
- CI. The hook only reports paths that differ from
HEAD, not merely paths that are
managed and present. On a checked-out PR branch the tree is clean, so
pre-commit run --all-files passes. (This narrowing was itself found by the hook
firing on its own repo under make fmt, which passes every tracked file.)
The gap is the local sync commit, and only that.
Proposed fix
Set the documented bypass on the sync commit:
SKIP=check-managed-files git commit -m "chore: apply rhiza sync $TARGET"
SKIP is pre-commit's own mechanism, so this needs no cooperation from the hook, and it
is scoped to the one commit that legitimately rewrites managed files. check-managed-files
also accepts --allow PATH, but that is per-path and wrong for a wholesale restore.
The concrete edit is one line in Jebel-Quant/rhiza-claude (commands/update.md
step 7), and the same applies to any other sync path that commits on the user's behalf —
rhiza sync in rhiza-cli if it commits rather than only writing files. I have not
filed there; say the word and I will, or link this issue from a companion one.
The ask on this repo
Gate adoption on the above:
- Land the
SKIP change in whatever performs the sync commit.
- Then add
- id: check-managed-files to bundles/core/.pre-commit-config.yaml
(alongside the existing rhiza-hooks block) and cut a release.
Adopting in the other order breaks /rhiza:update for all ~26 consumers at once, and the
failure is confusing rather than obvious — a hook telling you not to edit files that the
tool you just ran was supposed to edit.
Two side observations from the same investigation
Both are about this repo's shared hook list; happy to split them out if you would rather
track them separately.
Filed from a /rhiza:quality follow-up on rhiza-hooks. Evidence: stage_synced.py
behaviour as documented in commands/update.md; hook behaviour as implemented in
src/rhiza_hooks/check_managed_files.py and src/rhiza_hooks/_managed.py.
Summary
rhiza-hookshas gained acheck-managed-fileshook (Jebel-Quant/rhiza-hooks#304,shipped in Jebel-Quant/rhiza-hooks#309). It fails a commit that modifies a path listed in
.rhiza/template.lock'sfiles:block, minus anything underexclude:in.rhiza/template.yml.It exists because the rule every managed repo's
CLAUDE.mdopens with — managed filesare overwritten on the next sync — has had nothing enforcing it since
make validatewas removed after v1.1.3. The failure it catches is silent and total: the edit works, is
reviewed, is merged, and disappears at the next sync.
This issue is not a request to adopt it. It is a request to make the sync path safe
first, because adopting it before that would break sync for every consumer.
Why the sync path breaks
A sync commit modifies template-owned files wholesale — that is its entire purpose — so
it is precisely the commit this hook is built to reject.
Concretely, in
/rhiza:update(Jebel-Quant/rhiza-claude,commands/update.mdstep 7):stage_synced.pystages exactly the lock'sfiles:list.check-managed-filesreadsexactly the same list. So with the hook enabled and pre-commit installed, that commit
fails 100% of the time, in every consumer, on every sync.
What is already safe
Two cases need no change, so the fix is narrower than it might look:
commands/update.mdstep 4) stages only.rhiza/template.yml. That file is project-owned, not template-owned, so the hookignores it.
HEAD, not merely paths that aremanaged and present. On a checked-out PR branch the tree is clean, so
pre-commit run --all-filespasses. (This narrowing was itself found by the hookfiring on its own repo under
make fmt, which passes every tracked file.)The gap is the local sync commit, and only that.
Proposed fix
Set the documented bypass on the sync commit:
SKIP=check-managed-files git commit -m "chore: apply rhiza sync $TARGET"SKIPis pre-commit's own mechanism, so this needs no cooperation from the hook, and itis scoped to the one commit that legitimately rewrites managed files.
check-managed-filesalso accepts
--allow PATH, but that is per-path and wrong for a wholesale restore.The concrete edit is one line in Jebel-Quant/rhiza-claude (
commands/update.mdstep 7), and the same applies to any other sync path that commits on the user's behalf —
rhiza syncinrhiza-cliif it commits rather than only writing files. I have notfiled there; say the word and I will, or link this issue from a companion one.
The ask on this repo
Gate adoption on the above:
SKIPchange in whatever performs the sync commit.- id: check-managed-filestobundles/core/.pre-commit-config.yaml(alongside the existing
rhiza-hooksblock) and cut a release.Adopting in the other order breaks
/rhiza:updatefor all ~26 consumers at once, and thefailure is confusing rather than obvious — a hook telling you not to edit files that the
tool you just ran was supposed to edit.
Two side observations from the same investigation
Both are about this repo's shared hook list; happy to split them out if you would rather
track them separately.
.claude/is no longer synced but is still documented as if it were. Atv1.2.5the lock's
files:block lists no path under.claude/, yet consumerCLAUDE.mdsnapshots still name
commands/rhiza_book.md,commands/rhiza_quality.mdandcommands/rhiza_update.md. That functionality moved into therhiza-claudeplugin asskills. (Fixed downstream in docs: correct the sync instruction and the stale .claude/ snapshot rhiza-hooks#311; mentioning it because the
stale text presumably originates in a bundle.)
test_license_classifier_presentremains unsatisfiable for PEP 639 projects —already filed as test_license_classifier_present is unsatisfiable for PEP 639 projects (breaks setuptools>=77 builds) #1440, noted here only because it is the one synced test a
PEP 639 repo cannot pass, and it interacts with the new
check-license-metadatahook (New hook: check-license-metadata — reject SPDX license plus License:: classifier rhiza-hooks#307), which fails a project declaring both forms.
Filed from a
/rhiza:qualityfollow-up onrhiza-hooks. Evidence:stage_synced.pybehaviour as documented in
commands/update.md; hook behaviour as implemented insrc/rhiza_hooks/check_managed_files.pyandsrc/rhiza_hooks/_managed.py.