docs: reconcile kill-switch doctrine — per-hook toggles are userConfig - #321
Merged
Conversation
…g, env channel retires Resolves doctrine finding 1 of the fleet conformance audit (#313): the philosophy mandated env-toggle-to-userConfig migration while the playbook endorsed HOOK_<PLUGIN>_ENABLED as the ecosystem norm and its security review required that env var to exist. Ruling: migration wins. Per-hook selectivity ships as a userConfig boolean (default true) read through the hook-process CLAUDE_PLUGIN_OPTION_<KEY> mirror; per-project control stays whole-plugin via scope-level enabledPlugins; a genuinely project-scoped per-hook behavior graduates to the tracked consumer-project file on demonstrated need — never a custom env channel. Playbook bundling rationale, security-review check, reintegration seam map, and cutover checklist updated to match. The consumer-side HOOK_TELEMETRY_SINK seam and legacy in-repo hook toggles are unchanged. Unblocks the #315 userConfig wave (facts re-verified against the plugins reference and hooks pages this session; pluginConfigs is user/--settings/ managed scope only, v2.1.207+). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kyle-sexton
enabled auto-merge (squash)
July 18, 2026 01:35
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: de5bee5cde
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
This was referenced Jul 18, 2026
Codex review: --config applies only on a fresh install and is ignored once the plugin is installed (smoke-test C), so the checklist now seeds toggles in step 1's install command and reserves step 2 for interactive /plugin configure; headless reconfiguration = uninstall/reinstall. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kyle-sexton
added a commit
that referenced
this pull request
Jul 18, 2026
…plugins) (#325) Part of #315 (userConfig wave). No linked issue closes with this PR — closing #315 is gated on the hook-channel PR #323 landing and the wave acceptance sweep. ## What Completes the #315 scalar migrations outside the shared hook-utils wave (#323). These scalars are read by skill-invoked scripts or skill content, where the `CLAUDE_PLUGIN_OPTION_*` mirror does not reach — each value now flows through non-sensitive `${user_config.*}` substitution in the skill body into an explicit script flag, or (for the two skill-scoped guard hooks, which ARE hook processes) through the mirror directly. ## Per plugin - **source-control 0.7.0** — `worktree_stale_days` (default 14), `babysit_self_logins` (csv, default empty), `fetch_logs_max_bytes` (default 52428800). `babysit-readiness-gate.sh` gains an additive `--extra-self` flag — preserving the retired env var's add-to-personal-login semantics; the pre-existing `--self` full-override contract is unchanged. `fetch-failed-logs.sh` gains `--max-bytes`. Skill bodies carry the substituted values (reference/context files are not substituted, so the values enter context via SKILL.md); every instruction includes the unexpanded-token fallback. - **knowledge 0.7.0** — `yt_dlp_js_runtimes` (default "node"), `yt_dlp_cookies_file`, `yt_dlp_cookies_from_browser`, `max_concurrent_acquires` (default 1, max 3), wired through new `run.mjs` launcher flags on the plugin's existing `library_dir` → `--work-root` precedent; the `YOUTUBE_*` env vars remain only as the launcher-to-child internal interface. Course-platform credentials (`TEACHABLE_*`/`COURSE_*`) deliberately stay in shell env — `sensitive: true` persists as plaintext in `~/.claude/.credentials.json` on Windows — and that misfit is now documented at the site (the audit's dim-6 requirement). - **disk-hygiene 0.3.0** — `disk_hygiene_enabled` (default true) read by the skill-scoped guard via `CLAUDE_PLUGIN_OPTION_DISK_HYGIENE_ENABLED`. - **repo-hygiene 0.2.0** — `clean_destructive_guard_enabled` (default true) via `CLAUDE_PLUGIN_OPTION_CLEAN_DESTRUCTIVE_GUARD_ENABLED`. - **planning** — the audit's `use_ask_user_question` description-copy defect no longer reproduces (fixed upstream before this wave); no change shipped. **BREAKING (consumer-visible):** `WORKTREE_STALE_DAYS`, `BABYSIT_SELF_LOGINS`, `FETCH_LOGS_MAX_BYTES`, `HOOK_DISK_HYGIENE_ENABLED`, `HOOK_CLEAN_DESTRUCTIVE_GUARD_ENABLED`, and the four `YOUTUBE_*` consumer channels are no longer read from consumer-set env; re-express non-default values as the matching userConfig options. Zero-config behavior unchanged everywhere. `FETCH_LOGS_SCRATCH` / `FETCH_LOGS_REPO` remain env (out of #315 scope). ## Verification - babysit-readiness-gate suite 47 PASS (including the new `--extra-self` path); repo-hygiene guard suite 30 PASS; disk-hygiene pytest OK; knowledge vitest 33/33 on the four affected files + `tsc --noEmit` clean. - fetch-failed-logs suite requires `zip` (absent on this machine — it self-skips; CI runs it). The `--max-bytes` change is a one-line default-substitution swap plus a validated flag parse. - `scripts/validate-plugins.sh` (manifests + catalog, strict) and `node scripts/generate-catalog.mjs --check` green; markdownlint 0 errors on every edited file; residual grep confirms no consumer-facing doc still instructs setting a retired env var. - knowledge `package-lock.json` verified pristine (a subagent's local `npm install` was reverted before commit). ## Related - #313 (fleet conformance audit epic — dims 3+6 remediation) - #315 (userConfig wave — this PR completes the skill-channel portion) - #321 (kill-switch doctrine reconciliation) - #323 (hook-channel userConfig wave) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
kyle-sexton
added a commit
that referenced
this pull request
Jul 18, 2026
) Part of #315 (userConfig wave). No linked issue closes with this PR — #315 stays open for the remaining skill-channel scalar migrations (knowledge, source-control, disk-hygiene, repo-hygiene, planning description fix). ## What Applies the kill-switch doctrine ruling (PR #321) across every plugin carrying the shared `lib/hook-utils.sh`: per-hook selectivity is now a `userConfig` boolean (default `true`) read through the hook-process `CLAUDE_PLUGIN_OPTION_<KEY>` mirror, and the custom `HOOK_*` env channel retires in the same release. **Scope note:** #315 listed 9 same-pattern plugins; the shared-library reader change atomically flips every plugin carrying a synced copy, which adds `biome-format` and `claude-ops` (both call `hook::check_enabled`) to this wave — 10 plugins total. `disk-hygiene` and `repo-hygiene` (listed in #315) carry no hook-utils copy; their switches live in skill-invoked scripts where the mirror does not reach, so they move with the skill-channel PR. ## Per plugin | Plugin | Version | Options added | |---|---|---| | guardrails (flagship) | 0.5.0 | 7 per-guard `*_enabled` toggles, `cli_flag_verify_bins`, `cli_flag_verify_skip_bins`, `stdin_read_timeout` | | claude-ops | 0.11.0 | 7 audit-hook toggles, `instructions_loaded_audit_log_session_start`, `stdin_read_timeout` (existing `registry_dir`/`skill_usage_dir`/`install_new` preserved) | | desktop-notification | 0.2.0 | master toggle + 3 per-channel mutes, `stdin_read_timeout` | | actionlint 0.2.0 · bash-format 0.3.0 · biome-format 0.2.0 · eol-normalizer 0.2.0 · markdown-format 0.3.0 · powershell-format 0.2.0 · ruff-format 0.2.0 | — | single `<plugin>_enabled` toggle + `stdin_read_timeout` each | Every plugin: README configuration prose rewritten (`/plugin configure`, `claude plugin install --config`, user-scope note, per-repo disable via `enabledPlugins`) and a CHANGELOG entry with the BREAKING env-retirement note. **BREAKING (consumer-visible):** `HOOK_<NAME>_ENABLED`, `HOOK_STDIN_READ_TIMEOUT`, `HOOK_CLI_FLAG_VERIFY_BINS`, `HOOK_CLI_FLAG_VERIFY_SKIP_BINS`, `HOOK_INSTRUCTIONS_LOADED_AUDIT_LOG_SESSION_START` are no longer read; re-express any non-default value as the matching userConfig option. Zero-config behavior unchanged. The consumer-side `HOOK_TELEMETRY_SINK` seam is untouched. ## Verification - Boolean mirror serialization verified against the installed CC 2.1.214 bundle: options export as `String(value)` → `"true"`/`"false"`, so the enabled-unless-"false" reader semantics hold; the reader's `${!var:-true}` fallback also covers an absent mirror. - Full plugin test suite green (`scripts/run-plugin-tests.sh` exit 0; guardrails 248 + lib 46 assertions pass). - `scripts/sync-hook-utils.sh --check`, `scripts/check-cross-plugin-source-drift.sh --check`, `scripts/validate-plugins.sh` (manifests + catalog, strict), `node scripts/generate-catalog.mjs --check`: all green. - markdownlint clean on every edited markdown file. - One sed collateral caught and repaired: a `LEFTHOOK_*` lefthook-prefix test fixture in block-no-verify.test.sh. ## Related - #313 (fleet conformance audit epic — dims 3+6 remediation) - #315 (userConfig wave — this PR covers the hook-channel portion) - #321 (kill-switch doctrine reconciliation this PR implements) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 18, 2026
Closed
kyle-sexton
added a commit
that referenced
this pull request
Jul 18, 2026
Reconcile the block-dangerous-git guard with the kill-switch doctrine main adopted while this PR was in review (#321/#323): per-hook toggles and tuning scalars are native userConfig, not HOOK_* env vars. - lib/hook-utils.sh: take main's userConfig check_enabled/buffer_stdin and shfmt formatting; keep this branch's argv-grammar parser. - guardrails plugin.json: bump 0.5.0 -> 0.6.0 (main's 0.5.0 was the userConfig migration); add block_dangerous_git_enabled (boolean) and block_dangerous_git_allow (string list) userConfig options; correct the guard count Seven -> Eight. - block-dangerous-git.sh/.test.sh: kill switch flows through hook::check_enabled (CLAUDE_PLUGIN_OPTION mirror); allowed() reads CLAUDE_PLUGIN_OPTION_BLOCK_DANGEROUS_GIT_ALLOW; docs/messages/tests migrated off HOOK_BLOCK_DANGEROUS_GIT_*. - block-no-verify.sh: keep this branch's shared-parser refactor; take main's userConfig bypass message. - README/CHANGELOG: merge main's userConfig tables/entries with this guard's; new guard entry is [0.6.0] above main's [0.5.0]. - Nine format-plugin versions resolved to main's + patch (check-bump gate: lib changed vs main). Contract suites 181 + 75 green, shellcheck clean, check-bump and hook-utils drift checks pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves doctrine finding 1 of epic #313 (the finding, not the epic). No linked issue closes with this PR.
What
The philosophy doc mandated migrating personal env toggles to
userConfig(env channel retires), while the migration playbook endorsedHOOK_<PLUGIN>_ENABLEDas the ecosystem norm and its plugin-acceptance security review required that env var to exist. This PR reconciles: migration wins, with a scope carve-out.The reconciliation line (added to PLUGIN-PHILOSOPHY § Configuration ownership and scope): a hook kill switch is a personal scalar — per-hook selectivity ships as a
userConfigboolean withdefault: true, read through the hook-processCLAUDE_PLUGIN_OPTION_<KEY>mirror. Per-project control stays whole-plugin via scope-levelenabledPlugins; a genuinely project-scoped per-hook behavior graduates to the tracked consumer-project file on demonstrated need — never a custom env channel.Playbook sites updated to match
/plugin configure/claude plugin install --config; per-repo control is theenabledPluginsentry; the no-seam plugin-gap escape now names a tracked consumer-project config key instead of a new env var.Deliberately unchanged: the consumer-side
HOOK_TELEMETRY_SINKseam (project-scoped observability wiring, not a personal toggle — classified in the flagship migration), legacy in-repo hook toggles in the cutover procedure, anddocs/hook-migration-audit.md(a historical audit record).Verification
CLAUDE_PLUGIN_OPTION_<KEY>;pluginConfigsis read from user settings,--settings, and managed settings only (v2.1.207+); no native per-hook disable exists (onlydisableAllHooks,if, or whole-plugin disable —enabledPluginsis scope-level).HOOK_*_ENABLEDdoctrine text remains in either doctrine doc.Unblocks the #315 plugin migrations (guardrails flagship + 9 single-switch hook plugins + knowledge/source-control scalars).
🤖 Generated with Claude Code
Related