What happens
A repo that was synced on 0.9.0 (which wired every vendor chock knows) and then syncs on 0.9.1 with supported_agents narrower than that ends up with hook config files that point at runtimes sync just deleted.
Reproduced on chock-example main (5287c6c, synced on 0.9.0, supported_agents: [claude, copilot, gemini]) with a fresh pip install chock==0.9.1:
$ chock sync --repo .
$ git status --short
D .chock/bin/antigravity.py
M .chock/bin/claude_code.py
D .chock/bin/codex_cli.py
D .chock/bin/cursor.py
D .chock/bin/devin.py
M .chock/bin/gemini_cli.py
D .chock/bin/grok.py
D .chock/bin/tabnine.py
M .chock/bin/vscode_copilot.py
D .chock/bin/windsurf.py
$ git grep -n 'chock/bin/codex_cli.py' -- .codex
.codex/hooks.json:8: "command": "\"/usr/local/bin/python3\" \".chock/bin/codex_cli.py\" --guard ..."
.agents/hooks.json, .codex/hooks.json, .cursor/hooks.json, .devin/hooks.v1.json, .grok/hooks/agentseam.json, .tabnine/agent/settings.json and .windsurf/hooks.json all still carry chock's entries, each naming a file that no longer exists. chock sync --repo . --check and chock check both report clean afterwards, so nothing tells the adopter. chock-mise main (a669d0f, same supported_agents) shows the same shape on --check.
For an adopter who opens that repo in Cursor, Codex or Windsurf, every tool call now runs a hook whose command fails before the gate is evaluated: a non-blocking error in every client, on a hook that was enforcing yesterday.
Why
0.9.1's sync wires in-agent hooks only for the vendors supported_agents names (recompile.wired_vendors(), #149) and prunes a vendored runtime whose vendor is no longer wired (#150). Neither step removes chock's entries from the unwired vendor's config file, which 0.9.0 wrote and which sync used to refresh on every run.
Fix
sync uninstalls chock's entries from every wired-capable vendor that is not in wired_vendors(agents) (the same removal path install_hooks takes when no compiled fragment exists; deleting the config file when only chock's entries were in it, as the generic installer already does), and only then prunes the runtime. A vendor the adopter never had is a no-op.
chock check gains a repo check that fails on any chock-written hook entry whose command names a path under .chock/bin/ that does not exist, so a dangling target is a finding rather than a silent client error. The pre-0.9.1 audit ran exactly this scan by hand across six repos; it belongs in validate.
- Tests: a repo synced with every vendor wired, then re-synced with
supported_agents: [claude], has no chock entry left in .cursor/hooks.json (or no file) and no .chock/bin/cursor.py; the new check fails on a hand-planted dangling entry and is silent on a clean tree.
- Release as 0.9.2. chock-example and chock-mise resync on it; their 0.9.1 resync is held until then (chock-quickstart wires no vendor hooks and resynced cleanly).
What happens
A repo that was synced on 0.9.0 (which wired every vendor chock knows) and then syncs on 0.9.1 with
supported_agentsnarrower than that ends up with hook config files that point at runtimessyncjust deleted.Reproduced on chock-example
main(5287c6c, synced on 0.9.0,supported_agents: [claude, copilot, gemini]) with a freshpip install chock==0.9.1:.agents/hooks.json,.codex/hooks.json,.cursor/hooks.json,.devin/hooks.v1.json,.grok/hooks/agentseam.json,.tabnine/agent/settings.jsonand.windsurf/hooks.jsonall still carry chock's entries, each naming a file that no longer exists.chock sync --repo . --checkandchock checkboth report clean afterwards, so nothing tells the adopter. chock-misemain(a669d0f, samesupported_agents) shows the same shape on--check.For an adopter who opens that repo in Cursor, Codex or Windsurf, every tool call now runs a hook whose command fails before the gate is evaluated: a non-blocking error in every client, on a hook that was enforcing yesterday.
Why
0.9.1's
syncwires in-agent hooks only for the vendorssupported_agentsnames (recompile.wired_vendors(), #149) and prunes a vendored runtime whose vendor is no longer wired (#150). Neither step removes chock's entries from the unwired vendor's config file, which 0.9.0 wrote and whichsyncused to refresh on every run.Fix
syncuninstalls chock's entries from every wired-capable vendor that is not inwired_vendors(agents)(the same removal pathinstall_hookstakes when no compiled fragment exists; deleting the config file when only chock's entries were in it, as the generic installer already does), and only then prunes the runtime. A vendor the adopter never had is a no-op.chock checkgains a repo check that fails on any chock-written hook entry whose command names a path under.chock/bin/that does not exist, so a dangling target is a finding rather than a silent client error. The pre-0.9.1 audit ran exactly this scan by hand across six repos; it belongs invalidate.supported_agents: [claude], has no chock entry left in.cursor/hooks.json(or no file) and no.chock/bin/cursor.py; the new check fails on a hand-planted dangling entry and is silent on a clean tree.