|
1 | 1 | # Changelog |
2 | 2 |
|
| 3 | +## [1.35.0.1] - 2026-05-15 |
| 4 | + |
| 5 | +**`/freeze` and `/careful` now actually block. Three independent bugs — no bin/ symlink, no hook registration, wrong response format — were all required for the hooks to fire and enforce.** |
| 6 | + |
| 7 | +The `/freeze` skill blocks Edit and Write outside a declared directory. `/careful` warns before destructive Bash commands. Both worked during skill invocation (state file written, UI feedback shown) but provided zero actual enforcement. An Edit on a blocked file would succeed silently. Telemetry logged a `boundary_deny` event, which made it look like the hook fired — it did fire, the deny was just ignored. |
| 8 | + |
| 9 | +### The three numbers that matter |
| 10 | + |
| 11 | +Root cause: `check-freeze.sh` and `check-careful.sh` ran fine when called directly. The problem was in how they were wired, not in the pattern-matching logic. |
| 12 | + |
| 13 | +| Bug | Symptom | Root cause | |
| 14 | +|-----|---------|-----------| |
| 15 | +| Bug 1 | Hook script not found | `link_claude_skill_dirs` symlinked `SKILL.md` but not `bin/` — `${CLAUDE_SKILL_DIR}/bin/check-freeze.sh` resolved to a non-existent path | |
| 16 | +| Bug 2 | Hook never fired | SKILL.md frontmatter `hooks:` is documentation — Claude Code only fires hooks registered in `~/.claude/settings.json`. Setup never wrote there | |
| 17 | +| Bug 3 | Hook fired, deny ignored | Scripts returned `{"permissionDecision":"deny","message":"..."}` — a flat object Claude Code doesn't recognise. Correct format is `{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"..."}}` | |
| 18 | + |
| 19 | +With all three fixed, `/freeze` enforces the boundary on Edit, Write, and MultiEdit. `/careful` warns on destructive Bash commands when active. Both work in every new session after re-running `./setup`. |
| 20 | + |
| 21 | +### What this means for users who ran `/freeze` or `/careful` before |
| 22 | + |
| 23 | +Re-run `./setup` once. Setup now symlinks each skill's `bin/` directory and writes the PreToolUse hook entries to `~/.claude/settings.json`. After that, `/freeze` actually blocks edits outside the declared directory. `/careful` is a no-op unless you invoke it (gated by `~/.gstack/careful-active.txt`), so it won't interfere with sessions where you didn't ask for safety mode. |
| 24 | + |
| 25 | +### Itemized changes |
| 26 | + |
| 27 | +#### Fixed |
| 28 | +- **Bug 1** — `setup` and `gstack-relink` now symlink each skill's `bin/` directory alongside `SKILL.md` so hook scripts in the frontmatter are reachable at `${CLAUDE_SKILL_DIR}/bin/` |
| 29 | +- **Bug 2** — `setup` registers PreToolUse entries for freeze (Edit|Write|MultiEdit) and careful (Bash) in `~/.claude/settings.json` at install time via the extended `gstack-settings-hook`; `gstack-settings-hook` gains `add-pretooluse` and `remove-pretooluse` actions |
| 30 | +- **Bug 3** — `check-freeze.sh` now outputs `{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"..."}}` as required; `check-careful.sh` does the same for `permissionDecision:"ask"` |
| 31 | +- **Bonus** — `check-freeze.sh` resolves its state root via `gstack-paths` to honour `GSTACK_HOME` in addition to `CLAUDE_PLUGIN_DATA`; `check-careful.sh` is a no-op unless `~/.gstack/careful-active.txt` exists (created when `/careful` is invoked) |
| 32 | + |
| 33 | +#### For contributors |
| 34 | +- `test/hook-scripts.test.ts`: all careful tests use `withCarefulActive()` and pass `CLAUDE_PLUGIN_DATA` pointing to a temp dir; freeze tests assert `hookSpecificOutput` shape; two new tests verify the envelope structure and confirm allow responses remain `{}` |
| 35 | + |
3 | 36 | ## [1.35.0.0] - 2026-05-13 |
4 | 37 |
|
5 | 38 | ## **Docs become a tracked surface, not an afterthought. `/document-generate` writes them from scratch, `/document-release` audits coverage in four Diataxis quadrants.** |
|
0 commit comments