- Craftsmanship
- DevEx
What changed in AI Craftsman Superpowers 4.14
Version 4.14.0 of the plugin, for Claude Code, Codex, Grok and Hermes: 23 recorded changes across 1 releases, taken from the CHANGELOG rather than rewritten.
Alexandre Mallet12 min read
Version 4.14.0 is out. It carries 23 recorded changes across 1 release, and the plugin now ships 22 skills, 12 agents and 7 packs.
Every entry below is copied from the CHANGELOG in the repository rather than rewritten for this page. A release note that says something the CHANGELOG does not is a release note nobody can check.
4.14.0 (2026-10-24)
Two guards, a faster gate, and a cockpit you see without opening it. A git command that switches the project's hooks off is refused (CR-258), and a red test waved away as flaky without a ticket sends the turn back (CR-289). SEC001 covers JavaScript and Python with today's key shapes (CR-257), files a Bash command changed are reviewed (CR-256), and a test run that tested nothing grants no evidence (CR-255). A write costs about half of what it did, a prompt a third, and a session start half (CR-278, CR-290). On Claude Code the cockpit now ships in the plugin: a line above the prompt says where the session stands, and each answer that changed it gets a receipt.
Upgrading can refuse a file that 4.13.1 let through: a credential literal in a .js, .jsx, .mjs, .cjs or .py file, and a key with a recent issuer prefix (sk-ant-, sk-proj-, glpat-, npm_) in any covered language. SEC001 is never ignorable: move the value to the environment and rotate it.
The cockpit now ships in the craftsman plugin. If you installed the separate craftsman-cockpit plugin, uninstall it: its 4.14.0 is empty, so updating it also removes the old module.
Added
- On Claude Code, a line above the prompt says where the session stands: the
writes the gate judged, the blocks still open with their rule and file, the warnings, and what the tests say (red with the run that failed, verified, a run that tested nothing, not run). It appears once the session has something to report and is redrawn after every write the hooks judge; Ctrl+X then Ctrl+A folds it. Each answer that changed it gets one line under it, as in craftsman: 3 writes, 1 blocked, 0 warnings · 1 open: LAYER001 Order.php · tests not run; a turn that only read or talked gets none. Both read craftsman-helper session status, a JSON view of what the hooks keep for the session, and decide nothing (ADR-0031). Measured in a headless Claude Code 2.1.310 session on a synthetic project: the receipt line came from the real hooks through the real helper.
- SEC001 covers JavaScript and Python, and knows today's key shapes. An
Anthropic or OpenAI project key was written to a .ts file with no finding while the repository's own secrets scanner knew both prefixes, and a .js or .py file got no SEC001 at all. The shapes now live in one list, hooks/lib/secret-patterns.sh, that every pack reads: Anthropic sk-ant-, OpenAI sk-proj-, sk-svcacct-, sk-admin- and legacy sk-, GitLab glpat-, npm npm_, Slack webhooks, GitHub ghs_, ghu_, ghr_, Stripe rk_live, AWS ASIA, a typed API_TOKEN: str = "...", and a *_KEY constant holding a 32-character key. Issuer prefixes match with case and never inside a longer word, so Asia/Kolkata, task-... ids and storage keys stay silent. An env read on the line no longer hides an issued key: process.env.KEY ?? "sk-ant-..." ships the key. This reverses the CR-231 choice to leave secrets out of JavaScript (CR-257).
- A turn no longer ends on a red test waved away. A new synchronous Stop
hook, test-dismissal-guard.sh, sends the turn back when a test run that failed in the session is still outstanding and the last message calls it flaky, intermittent, known, pre-existing, unrelated, or green alone, with no ticket named (ORD-42, #123, an issue URL). A test red in the suite and green alone depends on shared state, order, time or the filesystem: a defect, not noise. Blocks in strict, warns in moderate, silent in relaxed; a second stop on the same excuse is shown to the user, never looped. The words are data, a TEST_DISMISSAL category of the bias patterns (English and French). /craftsman:debug and /craftsman:verify name the excuse among their red flags, with pre-existing deprecations reported instead of fixed (CR-289).
- The agent can no longer switch the project's git hooks off. A new
PreToolUse hook, git-bypass-guard.sh, refuses --no-verify on any git subcommand, git commit -n, -c core.hooksPath=..., git config core.hooksPath <dir>, HUSKY=0, SKIP=..., LEFTHOOK=0, and a git stash run in the same command as a commit, with the reason. A model has been seen committing six times in a row with --no-verify and git stash against rules forbidding it (anthropics/claude-code #40117): a rule the model reads is advice, a refusal is not. The command is read token by token, so --no-verify quoted in a commit message passes. It runs on the git subcommands Claude Code's if filter sees, which is best effort (env git ... and sh -c are not seen), and fails closed. The user keeps the choice: run the command themselves, or set hooks.disabled: git-bypass-guard (CR-258).
- Files a Bash command changed are reviewed. Claude Code 2.1.269+ lists them
in tool_response.bashEditDiff; post-write-check.sh now runs after every Bash call and judges each listed file as a Write, under its own path. A call that changed nothing leaves before any library loads. The host records the list in bypass and auto mode, elsewhere only with bashEditDiffEnabled in user or managed settings, and documents it as best effort: the pre-write gate remains the only refusal before the disk (CR-256).
Changed
- The latency suite's ceilings follow the hooks down: post-write from 6.5 to
3.55 times the calibration baseline on macOS and from 11.4 to 4.9 on Linux, the other rows alike, from four CI runs on the final code and a maintainer's laptop under load. Left where they were, they sat at twice what the hooks now cost, so a doubling would have passed.
- The cockpit moved into the craftsman plugin: one install, and
/instincts
finds the core at the plugin's own root instead of through the session binding, the repository layout and PATH, the lookup two field failures came from (CR-279, CR-291). Codex and Grok never see the module: it is declared by hooks/claude-code.json, which only Claude Code's manifest names, while hooks/hooks.json holds none. Measured: a module that fails to load leaves the command hooks running on 2.1.310, and Claude Code 2.1.218, the minimum, ignores the module file and runs the hooks (ADR-0031 amended).
- Every write costs less: a post-write about half, a pre-write about a
third less (median of seven alternating runs against the previous code, 2043 to 1030 ms and 1421 to 905 ms on a loaded machine). Claude Code had started offering to disable the plugin's slow hooks. Each hook now reads a pack manifest once instead of four times through subshells, a write reads the metrics schema stamp instead of running ten migration probes, and the craft config is read in one jq instead of five per custom rule (CR-278). A second pass, from a profile rather than a guess, took a further 16 percent off a post-write and 15 off a pre-write (CPU time, alternating runs against the previous code: 600 to 503 ms and 558 to 480 ms). The manifest memo of the first pass searched every pair for each question, 2,200 loop turns per hook; it now jumps to the key. The PHP root namespace lookup ran a process per directory up to /, the project root behind every metrics row ran git four times, and each pack manifest had its own stat; each is now done once or in-shell.
- A prompt waits less before the model sees it: the bias detector, which
runs on every prompt, takes about 70 percent less CPU (alternating runs against the previous code under the same load: 478 to 144 ms on a prompt with no bias, 496 to 149 ms on a signal hit), and the latency suite reads it at 0.50x its baseline instead of 1.60x. The pattern registry derived each language's variable suffix in a subshell, thirteen times for every pattern it was asked for: 221 subshells per prompt. It now derives them once, when it loads. The reported-speech check ran once per category instead of once per prompt, and the pending Sentry request was read by a Python start where jq does (CR-278).
- A session starts in about half the time: SessionStart, which Claude
Code waits for before its first response, went from 1630 to 900 ms at startup and from 1680 to 950 ms after a compaction (alternating runs against the previous code, on a copy of a 26,000-violation metrics database), same banner. On macOS each pack skill cost a python3 start to compute a relative path; the known-language registry was rebuilt by each of eight subshells; the healthcheck and the routing table ran an awk and a grep per skill; and the metrics database ran its fourteen migration probes although it carried this version's schema stamp. Each is now done once or in-shell. The cost: SessionStart no longer re-runs the migrations over a database damaged by hand whose stamp still matches, the same trade the write hooks already made (CR-290).
- SessionStart sweeps the files of sessions that ended without a SessionEnd
once a day instead of on every start: what it takes is a week old, and it cost each start a find and a Python start (32 ms). The sweep had no test; it has three now.
- The structural ratchet records function-level debt only: worst-function
complexity, longest function and suppression count. A file that grew with a feature failed the ratchet until someone raised its budget: on this repository 270 of 473 raises moved file_lines alone, while LOC001, the rule that owns file length, is advisory. file_lines and fan_out are still printed by ratchet.py measure and dropped from an older row on its next update. A raise keeps one reason, the newest (git keeps the history), and ratchet.py check --tight refuses a mark looser than its file or a touched file with no row, so a pipeline can require the tightened mark to be committed. ADR-0025 amended (CR-288).
Fixed
- A compaction no longer restarts the session's metrics. SessionStart fires
again after a compaction, with the same session id, and it rewrote the session's start time and deleted its write and violation tallies: a session that ran five days and compacted five times was measured from its last compaction, with every earlier write gone. After a compaction the session now keeps both; a session that starts, resumes, forks or clears restarts them as before (CR-296).
craftsman-ciscans a file once however it is spelled on macOS. The key
that deduplicates the scan folds //, and under bash 3.2 that fold kept a backslash, so src//Order.php and src/Order.php were two files and their findings counted twice. Linux runners, on bash 5, were not affected (CR-297).
- Pack skills no longer disappear while a session starts. SessionStart
deleted every pack skill and copied it back, and Claude Code's hooks reference says skill discovery runs before SessionStart hooks finish, so a session that looked in between could start without them (deduced from that timing, never observed). A skill a loaded pack still serves is now refreshed in place (CR-290).
- The pack manifest identity list from the second CR-278 pass never
answered under bash 3.2, the macOS shell: ${path//\/\//\/} keeps the backslash there, so no lookup matched and every manifest was stat'ed on its own. Answers were right, the saving was not there. A test now fails when a manifest the list holds is stat'ed (CR-290).
- A new interactive Claude Code session was taken for a Codex session: its
SessionStart carries model and no prompt_id, which the host detection read as Codex. The session binding went under ~/.codex, the startup healthcheck talked about Codex, and the cockpit mod's /instincts pane said "craftsman-helper not found". Found on 32 sessions of one machine since 4.12.3. Claude Code's scratchpad_dir now names it, and model means Codex on SessionStart only with Codex's permission_mode; the interactive payload is captured as a fixture (CR-291).
- A test run that failed no longer has its failure erased by a narrower run
that passes. The suite went red on one test, the agent ran that test alone, it passed, and post-bash-test-verify.sh granted the verification evidence and cleared the failure: the plugin endorsed "flaky, passes alone" instead of refusing it. Each red run now stays outstanding until a run that adds no argument to it passes (hooks/lib/test_scope.py): a filter, a path or a test name may narrow the run, and which flags narrow is each runner's business, so any added argument counts. Another suite going green does not answer this one either. The cost of that doubt is one re-run of the red command (CR-289).
- The structural ratchet measured every PSR-12 method (opening brace on its
own line) as complexity 0 over one line, and a multi-line signature as its signature only: it closed the function on the brace. It now finds the body after the header, so PHP methods and functions with long parameter lists are measured. The ruler changed (instrument 3): a mark taken by the previous one is re-taken on the next touch, without failing an untouched file (CR-288).
- Re-taking a mark after an instrument change erased the row's reason and the
rule baseline's counts stored with it, so inherited rule findings would have blocked again after an upgrade. Only the measured numbers are re-taken now (CR-288).
- A test command that exits 0 no longer grants verification evidence when it
tested nothing: an npm script that only echoes, a suite with every test skipped, PHPUnit with zero assertions. Packs declare the wording in a new empty_run_markers capability, each marker taken from a real capture in tests/fixtures/test-runs; the skill's set-verified is refused until a run that tested something passes (CR-255).
- A plugin update inside a session left the session binding on the old
plugin root, so the cockpit mod ran the previous version's helper (/instincts printed a usage line in its pane). The binding now follows the root the hooks run from, and the pane names the helper it ran when it fails (CR-279).
Removed
config/default-config.yml. It said it was copied to
~/.claude/.craft-config.yml on first run; no code has read it since the v4 configuration format, and it described that file in the pre-v4 one (version: "2.1", a packs: mapping). The configuration follows schemas/craft-config.schema.json and /craftsman:setup.
Upgrading
The same engine runs on every host; update it the way that host installs plugins:
# Claude Code
claude plugin update craftsman@ai-craftsman-superpowers
# Codex (then review the handlers in /hooks)
codex plugin marketplace upgrade
# Grok (re-exports the gate as well)
bash <your clone>/bin/craftsman-grok-install
# Hermes
git -C ~/.hermes/plugins/craftsman pullThe full history, including every patch summarised here, is in CHANGELOG.md.