Skip to content

sync emits duplicate PreToolUse/SessionStart entries in chock-catalog, but not in a clean repo #84

Description

@open-coder-ai

What happens

Regenerating chock-catalog against chock v0.7.0 (via tools/adopt_framework.py, see chock-catalog#46) produces duplicated hook entries in the compiled agent config:

  • .claude/settings.json14 PreToolUse entries where 7 should exist, and 2 SessionStart entries where 1 should exist
  • .cursor/hooks.json14 entries likewise

Exactly 2× in every case.

What does not happen — the part that narrows the search

It does not reproduce on a clean repo, and it does not accumulate. Reproduction attempt with chock==0.7.0 installed from PyPI into a fresh venv, a fresh git init, chock init ., two guard-shipping policies copied from the catalog (block-curl-pipe-sh, scan-secrets), then chock sync --repo . run four times:

after sync #1:  .claude/settings.json {'SessionStart': 1, 'PreToolUse': 1}   .cursor/hooks.json {'beforeShellExecution': 1}
after sync #2:  .claude/settings.json {'SessionStart': 1, 'PreToolUse': 1}   .cursor/hooks.json {'beforeShellExecution': 1}
after sync #3:  .claude/settings.json {'SessionStart': 1, 'PreToolUse': 1}   .cursor/hooks.json {'beforeShellExecution': 1}
after sync #4:  .claude/settings.json {'SessionStart': 1, 'PreToolUse': 1}   .cursor/hooks.json {'beforeShellExecution': 1}

Stable across repeated syncs. So this is not unbounded accumulation on re-sync, and the emitter's basic merge logic is not broadly broken.

Why that matters for severity

  • No adopter is affected by a fresh install. A clean repo gets correct output.
  • The symptom is a doubled entry, not a wrong one. A duplicated PreToolUse entry means the guard runs twice on a tool call — wasteful, not unsound. It cannot allow something it should deny.
  • Consequently this is not judged a hotfix. Fixing it in the next release rather than cutting a patch on top of v0.7.0 seems right, but that is a maintainer call, not a conclusion of this report.

Where the trigger probably is

The clean-repo reproduction rules out the simple path. Two differences remain between it and the catalog, and the trigger is likely in one of them:

  1. Scale / pre-existing state. The catalog carries 25 policies and a committed .claude/settings.json that predates this run; the repro started from nothing. A merge that appends rather than replaces when entries already exist would show exactly this 2× signature.
  2. The multi-tree plugin build. The catalog runs chock plugin build across three published trees (base, compliance, agentic-security) in addition to chock sync. The repro ran sync only. If a per-tree pass re-emits into the same repo-level config, that would also produce 2×.

Distinguishing those two is probably a short experiment: re-run chock sync on a checkout of the catalog with .claude/settings.json deleted first, and see whether the duplication returns.

Provenance

Found by the worker performing the 0.7.0 catalog adoption. It did not hand-fix the generated files, per that repo's "never hand-edit generated output" rule — the artifacts in chock-catalog#46 contain the duplication as generated, so the evidence is preserved in that diff.

The clean-repo reproduction above was run separately to establish whether adopters are at risk.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions