Skip to content

Fix #151: sync uninstalls what it no longer wires, and release 0.9.2 - #152

Open
jothimani-rajendran wants to merge 4 commits into
mainfrom
claude/fix-151-release-0-9-2
Open

jothimani-rajendran wants to merge 4 commits into
mainfrom
claude/fix-151-release-0-9-2

Conversation

@jothimani-rajendran

@jothimani-rajendran jothimani-rajendran commented Sep 21, 2026

Copy link
Copy Markdown
Collaborator

What

Fixes #151. 0.9.1's sync wires in-agent hooks only for the vendors supported_agents
names (wired_vendors(), #149) and prunes a vendored runtime once its vendor falls out of
that list (#150), but neither step removed chock's entries from that vendor's own hook
config file, which 0.9.0 had written for every vendor chock knew. A repo synced on 0.9.0
and then narrowed on 0.9.1 (chock-example, chock-mise) ends up with .cursor/hooks.json,
.codex/hooks.json, .windsurf/hooks.json and four more config files still naming a
.chock/bin/<vendor>.py that sync had just deleted — a hook whose command fails on every
tool call in Cursor, Codex or Windsurf, silently, while chock sync --check and
chock check both reported clean.

Reproduced first with a failing test
(test_dropping_a_vendor_removes_its_hook_config_entries_too in
tests/test_sync_wires_only_supported_agents.py): wire every vendor in CHOCK_AGENT, then
narrow to ["claude"].chock/bin/cursor.py is gone but .cursor/hooks.json still names
it.

The fix

Compiling is agent-agnostic: a vendor's fragments stay compiled even after it drops out of
supported_agents, so calling the ordinary installer for it would find those fragments and
reinstall rather than remove — the naive "call install_hooks for it and let it see no
fragments" approach doesn't work here, since fragments are always present regardless of the
current agent list.

install_merged/install_generic/_install_agent_hooks each gain an uninstall=True path
that treats the vendor as having no fragments at all, regardless of what is actually
compiled — the same removal shape the installers already have for "nothing to install":
chock's entries come out, a config file that held only chock's is deleted, and the runtime
is unlinked once nothing references it. Exposed as
in_agent_install.uninstall_hooks(repo_root, vendor).

recompile()'s _uninstall_unwired_vendors() (formerly _prune_unwired_runtimes) calls
this for every vendor showing any trace of a past install (_vendor_needs_uninstall(): its
vendored runtime still on disk, or — since an earlier, buggy sync can have already deleted
the runtime without touching the config, which is exactly how #151 stays silent — chock's
own marker still sitting in the vendor's config), before pruning that runtime. A vendor
the adopter never had matches neither and is a true no-op: nothing is read or written for
it.

CI on this PR's own head caught a live instance of #151 in this repo's dogfooded
config: antigravity left supported_agents under #150 and its runtime was pruned, but
.agents/hooks.json still named it. The new check correctly failed the build on it, and
the fix (committed separately) cleaned it up — .agents/hooks.json is now deleted since it
held only that dangling entry.

The new check

chock check gains check_dangling_hook_targets() in validation/checks_repo.py
(registered in validation/engine.py beside check_gate_log_untracked): it reads every
hook config chock can write (config_path() for each WIRED_VENDORS entry, plus the owned
.github/hooks/chock.json), finds any command naming a .chock/bin/ runtime, and reports
an error when that path doesn't exist on disk, with chock sync as the fix. Silent on a
clean tree and on a repo with no hook configs.

docs/cli-reference.md's sync paragraph now says it uninstalls unwired vendors before
pruning their runtime (rewrapped in place; the file stays at 300 lines).
spec/enforcement-matrix.md lists no row for check_gate_log_untracked itself, so no row
was added for the new check either — nothing in that file's shape to follow.

Release 0.9.2

Separate commit, after the fix: pyproject.toml, CITATION.cff, action.yml's version
default, and the docs/installation.md action example → 0.9.2; a CHANGELOG.md entry as
the new first ## section (no empty ## Unreleased above it); docs/assets/social-preview.{svg,png}
regenerated with gen_brand_assets.py (its version string is read from pyproject.toml).

Not in this PR: the tag.

Verification

$ python -m ruff check .
All checks passed!

$ python -m ruff format --check .
287 files already formatted

$ python -m pytest -q
1391 passed, 2 skipped in 211.52s

$ python -m pytest acceptance/
21 passed in 39.24s

$ chock check --repo .
28 policies: 99 pass, 102 skipped; enforcement matrix OK; matrix mechanisms PASS
(4 pre-existing unautomated-mechanism infos, unrelated to this change);
index freshness OK; ambient conflicts PASS; baseline skipped: no --base given

$ chock sync --repo . --check
Compiled artifacts match their manifests.

$ chock check --only verify
verify: all packs match lockfile

$ chock registry scan
Scanned 28 artifact(s) across 28 unique ID(s).

$ cd docs/assets && python gen_brand_assets.py --check
social-preview.svg is current

CI on the current head (all 8 jobs, including validate on 3.11/3.12/3.13, CodeQL and
Security checks) is green.

Definition of done

  • chock check → clean (0 errors, 0 warnings; the four [INFO] lines are pre-existing
    unautomated-mechanism notes, unrelated to this change)
  • chock check --only matrix passes; no matrix change needed (see above)
  • chock sync --repo . --check clean
  • chock check --only verify clean
  • Registry rescanned; no stale entries
  • pytest -q green; new tests cover the reproduction, the self-heal-after-0.9.1 case,
    and the new check (dangling entry fails, clean tree and no-hook-configs repo are
    silent, the fixed sync leaves nothing to find)
  • pytest acceptance/ green
  • Touched manifests: none; version bump + changelog entry done for the release
  • ruff check . and ruff format --check . clean

Claims

  • No surface is described as enforcing more than it installs — this PR only removes
    stale claims (dangling hook entries) and adds a check that catches them; no
    INSTALLED_SURFACES/coverage/docs/enforcement-surfaces.md change needed.

🤖 Generated with Claude Code

jothimani-rajendran and others added 3 commits September 21, 2026 01:29
…time

Fixes #151. 0.9.1 wired in-agent hooks only for the vendors
supported_agents names (#149) and pruned a vendored runtime once its
vendor fell out of that list (#150), but neither step removed chock's
entries from that vendor's own hook config file, which 0.9.0 had
written for every vendor chock knew. A repo synced on 0.9.0 and then
narrowed on 0.9.1 was left with hook configs naming a runtime file
sync had just deleted -- a hook that fails on every tool call in
Cursor, Codex or Windsurf, silently, while chock sync --check and
chock check both reported clean.

Compiling is agent-agnostic: a vendor's fragments stay compiled even
after it drops out of supported_agents, so calling the ordinary
installer for it would find those fragments and reinstall rather than
remove. install_merged/install_generic/_install_agent_hooks each gain
an uninstall=True path that treats the vendor as having no fragments
regardless of what is compiled, exposed as
in_agent_install.uninstall_hooks(). recompile()'s
_uninstall_unwired_vendors() (formerly _prune_unwired_runtimes) calls
it for every vendor that still has a vendored runtime on disk but is
no longer wired, before pruning that runtime -- a vendor the adopter
never had stays untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
Part of #151. sync's stale-entry bug was invisible to both
sync --check and check, because neither read a vendor's own hook
config back and compared it against what is actually on disk. Add
check_dangling_hook_targets() to validation/checks_repo.py, wired
into the engine beside check_gate_log_untracked: it reads every hook
config chock can write (config_path() for each wired vendor, plus the
owned agent-hooks file), finds chock's entries by the runtime path
named in the command, and errors when that path does not exist,
naming chock sync as the fix.

Tests: a hand-planted dangling entry fails; a clean tree and a repo
with no hook configs are silent; sync's own fix (previous commit)
leaves nothing for this check to find after wiring every vendor and
then narrowing supported_agents.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
Bump pyproject.toml, CITATION.cff, action.yml's version default and
the docs/installation.md action example to 0.9.2. Add the CHANGELOG
entry for the fix and the new check. Regenerate
docs/assets/social-preview.{svg,png} with gen_brand_assets.py, whose
version string is read from pyproject.toml at render time.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
@jothimani-rajendran
jothimani-rajendran marked this pull request as ready for review September 21, 2026 01:31
CI on this PR's own head caught a live instance of #151 in this
repo's dogfooded config: antigravity left supported_agents under
#150 and its runtime was pruned, but .agents/hooks.json still named
it, and the new check_dangling_hook_targets() failed the build on it.

_uninstall_unwired_vendors() gated uninstalling a vendor on its
runtime still being on disk, which is exactly the trace #151 itself
does not leave -- the earlier, buggy sync already deleted the
runtime without touching the config. _vendor_needs_uninstall() also
recognises chock's own marker still sitting in a vendor's config
(the owned agent-hooks file's mere existence for that vendor family,
or the vendored-runtime path -- current or legacy-named, per
owned_markers() -- appearing in the vendor's config text), so a repo
already broken by 0.9.1 self-heals on its next sync too, not only one
that narrows supported_agents with the runtime still present.

Regenerates this repo's own .agents/hooks.json (deleted: it held only
chock's now-dangling antigravity entries).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Jothi Mani Rajendran <250249270+jothimani-rajendran@users.noreply.github.com>
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.

0.9.1 sync prunes an unwired vendor's runtime but leaves its hook config pointing at it

1 participant