Skip to content

v3.21.2 - the installer now leaves your own git hooks working, and a junk value in a cloned repo's config no longer overrides your global off switches

Choose a tag to compare

@github-actions github-actions released this 02 Oct 01:56
· 46 commits to main since this release
v3.21.2
621f40b

The installer now leaves your own git hooks working, and a junk value in a cloned repo's config no longer overrides your global off switches.

Security

  • CWK-158 item 1 + CWK-141 (1) — a project config value the clamp does not recognise is now dropped, not kept. The config cascade lets a cloned repo's .coalmine.json only quieten what your global config sets. Before this fix, a project value outside a key's allowed set (null, a string such as "on", a number) was left in the merge as it was, and it replaced your global choice: {"rotCanaryMode": "on"}, {"enableConductor": null} or {"updateMode": "x"} in a cloned repo defeated your global off, and {"disabledCanaries": null} (or a string or a number) replaced your global list. Now such a value reads as absent: your global value stands, or the factory default where your global layer says nothing. An unknown value in your GLOBAL file reads as the factory default. This covers updateMode, enableConductor, rotCanaryMode, disabledCanaries and scanExcludePaths in the three hooks and in the PowerShell fallbacks (alt/powershell), where a JSON null counts as a present value and a non-boolean is never turned into a boolean. scanEverything was not affected (its strict true check never accepted the string "true"); its row is kept as a control. — test: scripts/lib/hooks.test.mjs (clamp drops a junk project value (…), one row per key and shape) · scripts/lib/conductor-update.test.mjs · scripts/lib/ps-config.test.ps1
  • CWK-158 item 7 — install and uninstall no longer delete a folder only because an install manifest names it. A manifest (.coalmine-manifest.json) can arrive with a cloned repository, and a folder it listed, such as victim-dir, was removed recursively. A listed folder is now removed only when every file in it is recorded in the manifest's own hashes with the hash it has today. An unrecorded file, a changed file or a link makes it unproven: the installer prints [kept] <folder>: …, leaves it in place and exits non-zero. A manifest from before the hashes existed proves ownership only by CoalMine's own skill-meta.json marker. — test: scripts/lib/install.test.mjs (CWK-158 item 7: …, three tests)

Changed

  • CWK-158 item 2 — the git hook the installer writes into your repo is a small chain-only hook, not CoalMine's own repo gate. Before, the installer copied CoalMine's .githooks/pre-commit and .githooks/pre-push into your repo. Those run scripts/verify.mjs and scripts/test.mjs of the project they sit in, which is a no-op in most projects and a surprise blocker in one that has such scripts, and your own hook was renamed aside and never ran again. The installed hook now runs the hook it replaced (kept beside it as <hook>.pre-coalmine, with its arguments and exit status passed through) and nothing else, so installing CoalMine never turns off a gate you had. A hook your repo tracks (core.hooksPath at a versioned directory such as .husky/ or .githooks/) is not rewritten: the installer prints [refused] <hook>: … and exits non-zero, the same refusal uninstall already had. — test: scripts/lib/install.test.mjs (CWK-158 item 2: …, three tests)

Fixed

  • CWK-158 item 3 — uninstall no longer restores a backed-up hook over a hook you wrote after installing. The restore put <hook>.pre-coalmine back over whatever was at the hook path. It now does so only over a hook that is CoalMine's. If the hook there is yours (or unreadable), both files stay, the installer prints [kept] <hook>: … and exits non-zero, and you merge them. — test: scripts/lib/install.test.mjs (CWK-158 item 3: …)
  • CWK-158 item 4 — a core.hooksPath that starts with ~ now expands the way git expands it. The installer read the value as plain text, so ~/hooks became <project>/~/hooks while git ran $HOME/hooks: the hook landed in a folder git never reads. It now reads the value as a path (git config --type=path). If that git is too old for --type and the value starts with ~, the installer reports that it cannot expand it instead of guessing. — test: scripts/lib/install.test.mjs (CWK-158 item 4: …)
  • CWK-158 item 5 — configure.mjs no longer anchors its project search on a legacy config in a folder above the one you ran it from, unless that folder is the git root. Its search used to stop at the first parent folder holding .claude/.coalmine.json, then move that file into the project's own config folder and delete the original. That legacy file now anchors the search only in the folder you ran configure.mjs from. A .git folder above still anchors it: run from a subfolder of a git project, a legacy config at the git root is still read and migrated, as before. The hooks' read order is unchanged. — test: scripts/lib/configure.test.mjs (CWK-158 item 5: …)

What you need to do: nothing is required, and if the installer printed a [refused] or [kept] line it left that file alone: read the line and decide. To receive the changed files, on Claude Code run claude plugin update coalmine@coalmine; users of coalmine@claude-community receive them when that catalog's pin moves; for any other agent, update your CoalMine checkout and re-run node scripts/install.mjs <agent>. If CoalMine's old gate hooks are already in a repo of yours, re-run the installer there to replace them with the chain-only hook.