Every superpowers-prefixed skill call in ship-feature, init-project, tour,
deploy, audit-delta, plugin-advisor and lib/analyze-before-plan now names
the vendored skill directly. finishing-a-development-branch is described
as the upstream skill this config does not vendor (gitflow finish is the
integration path). CLAUDE.global.md Skill routing maps the four
non-vendored skills the vendored text still references. settings.json
loses the plugin key and its marketplace block; README, USAGE,
plugin-advisor and the profile skill describe superpowers as vendored
skills, always on, zero plugin cost. CHANGELOG entry with a known
residual.
settings.json: ENABLE_STOP_REVIEW=0 (the plugin's own switch: no more
Opus call on every turn that changes code, 0 findings in 6 days, 1
recorded false positive; the regex layer and the commit/push agentic
review stay on), brightdata-plugin@synced false (keyless-useless, its MCP
skill would hijack WebFetch/WebSearch), frontend-design official plugin
entry gone (uninstalled: byte-identical to the managed copy).
CLAUDE.global.md routes Ship/PR to ship-feature (gstack ship takes
origin/HEAD = main as base), drops ship/context-save from the gstack-off
list and 21st-ui-review from the design review line (trio is max-only).
deploy's table no longer points at land-and-deploy/setup-deploy.
plugin-advisor.md describes security-guidance's real mechanics. CHANGELOG
Unreleased entry with a Known residual section.
Root causes of the 2026-09-24 errors turned into mechanisms (BDR-100). hard_deny 'Routing around a guardrail': a refused command is never rerun through a wrapper, alias, heredoc, Makefile target, env file, other shell or other agent; the same clause in 14 agents and in the doctrine's sub-agent rule. make test suite=<file> runs one suite hermetically so the denied env-prefix form is never needed by hand. lib/tests/doctrine-citers.test.sh: every CLAUDE.md "Section" / § Label citation across skills, agents, lib, rules and hooks must resolve to a heading or bold label (flip-tested); its first run fixed rest-api-node.md. Doctrine 'After code changes' step 4: a changed rule, heading, label or threshold → grep every citer in the same commit.
settings.json: the user's hand-edit (feedbackDrafts: off) committed as is. .gitignore: skills/synced/ and its .bucket-* marker are Claude Code's mirror of the claude.ai synced skills (UUID bucket, manifest.json, Anthropic stock skills incl. 117 ISO xsd schemas), rewritten at each sync — same treatment as the graphify and impeccable machine-owned copies (BDR-028, LRN-154).
`gitflow_delete` now ends with `_gitflow_delete_remote`: after the local
copy is gone, the remote tip is read with `ls-remote --exit-code`, checked
against develop/main with the same ancestor test, and only then removed
with `push origin --delete`. Same contract as the pushes (BDR-095): best
effort, warn never fail. No origin, `GITFLOW_NO_PUSH=1` or
`gitflow.autopush false` skip it; an unreachable origin or a remote tip
holding commits the bases lack keeps the remote branch, loudly. A base is
never targeted, by construction and by an explicit guard.
The static deny on hand `git push --delete` stays: it matches the Bash
tool's command string, the lib is the sanctioned path. Prose (hard_deny,
environment), doctrine, gitflow SKILL (table, op, warning row),
SETTINGS.md and CHANGELOG updated. T24: 9 checks (finish removes the
copy, bases untouched, unmerged remote tip kept, never pushed silent,
unreachable origin loud, autopush opt-out). 161/163, the 2 failures are
the pre-existing T16a (gitleaks absent on this host).
Since BDR-095 `start` sets an auto-pushed upstream, so `git branch -d`
checked "merged into origin/<branch>" (always true, the post-commit hook
keeps it in sync) instead of "merged into develop". T22a proves it: an
unmerged feature with its upstream in sync is deleted by `-d` alone.
- `gitflow_delete` is the single delete path (finish + CLI `delete`):
refuses main/develop (rc 6) and any branch that is not an ancestor of
develop or main (rc 5, `gitflow_merged_into_base`, fail closed when
neither base exists), then `-d` as a second layer. CLI `merged`, `hooks`.
- Fourth generated hook `reference-transaction`: in the `prepared` call,
a deletion of refs/heads/main or refs/heads/develop exits 1, whatever
issued it (branch -d/-D, update-ref -d, rename, script, sub-agent).
`git config gitflow.protect false` opts a foreign clone out.
- `GITFLOW_HOOKS` is the one hook list: write/emit/reconcile, T19d and
doctor.sh (`gitflow.sh hooks`) read it. `.githooks/` and `githooks/`
regenerated with the fourth hook.
- settings.json: static deny on hand `git branch -d/--delete/-dr/-rd` and
on renames of main/develop; hard_deny "Branch deletion by hand"; the
Disarming entry covers all four hooks and `gitflow.*` config; the
protected-branches environment line states the rule.
- Doctrine (CLAUDE.global.md gitflow section), gitflow SKILL (`delete`
op, rc 5/6 rows, common mistake), guard-bash spec T8w flips to deny,
SETTINGS.md, README, CHANGELOG.
- Tests: T22 (12) lib guard incl. the premise proof, T23 (11) hook;
T19 covers the fourth hook. 152/154, the 2 failures are the
pre-existing T16a (gitleaks absent on this host).
Global: `make link` generates githooks/ from lib/gitflow.sh and sets git's
global core.hooksPath to ~/.claude/githooks, so every repo on the machine
runs the pre-commit protection and the post-commit / post-merge push, even
one that never ran gitflow init. A repo's own local core.hooksPath still
wins, so hooks/session-start.sh calls `gitflow reconcile-hooks` once per
session and rewrites a .githooks/ that lags the lib (LRN-114 automated);
the pre-commit exemption now covers .githooks/** next to .claude/**.
Per-repo opt-outs for a foreign clone: `git config gitflow.protect false`
(branch model) and `git config gitflow.autopush false` (push). Both, and
the GIT_CONFIG_GLOBAL= / GIT_CONFIG= env bypass, are static deny rules.
`make test` and the two suites that commit on main export
GIT_CONFIG_GLOBAL=/dev/null so the machine's global hooks never fire in
throwaway repos. doctor gains "Git hooks" (global setting, githooks/ equal
to the emitters) and "Scratchpad" (warn when TMPDIR sits on a tmpfs with
usrquota: systemd caps each user at 80% of it, which killed two shells
today, BLK-021). Tests: T18h, T19d, T20 (reconcile), T21 (whitelist and
protect opt-out); this repo's own stale .githooks/ refreshed.
Layer C of the plan written after the 2026-09-21 wipe (BDR-095): a reviewer
sub-agent traced `lftp mirror --delete` against a local file:// tree, the
prose tiers named neither lftp nor a local trace, the brief had authorized
it, and four days of commits had never left the machine.
- gitflow: `start` pushes the branch with its upstream, merge targets are
pushed after each merge, and `init`/`install-hook` write post-commit and
post-merge hooks that push every commit as it lands (warn, never block;
GITFLOW_NO_PUSH=1 for throwaway repos). T18 + T19 (installed == emitted).
- hooks/unpushed-guard.sh on SessionStart and Stop: branch ahead of its
upstream, no upstream, or no origin. Non-blocking systemMessage.
- settings.json: static deny for transfer and mirror tools, rsync --delete,
xargs rm, pipe-to-shell, chmod/chown -R, sudo/doas/pkexec, disk tools,
chattr, docker volume drops/prune/--privileged/socket/-v /:, git history
destruction, --no-verify and core.hooksPath; new hard_deny "destructive
tool against a local path, brief carries no user authority"; soft_deny
reworded + discarding uncommitted work; environment records the incident.
- CLAUDE.global.md "Destructive tools & data loss"; the four report-only
agents trace by reading, never by running, whatever the brief says.
- lib/tests/guard-bash.test.sh: executable spec of the PreToolUse guard
(214 cases). The hook itself is not shipped (BLK-022); the spec skips.
Upstream supersedes `@21st-dev/magic` with `@21st-dev/cli` (bin `21st`):
same endpoint, `21st login` in place of an API key, no MCP process loaded
into every session.
- install-plugins.sh Step 8.7: `npm i -g @21st-dev/cli` (pinned in
plugins.lock.json), staged `21st skills install`, TTY-only login offer,
pack disabled by default. update-all.sh 7.4 refreshes both.
- The documented `21st install-skill` cannot be used: the installer refuses
to follow a symlink on the target path and `~/.claude/skills` is one. The
install runs under a throwaway HOME and the result moves into
skills-external/21st-* (gitignored), symlinked on demand.
- toggle-external.sh manages `21st` as a pack (names globbed from
skills-external/21st-*, parked under plain names). `magic` is gone.
- The 5 design skills join design/web/web-full/full and MANAGED_EXTERNALS;
21st-registry and 21st-design-sync stay parked. MANAGED_MCPS is now empty
and profile.sh's dead magic branches are removed.
- Design gate: GATE-BLOCK gains `21st` (required-manual, magic's old slot)
and `21st-ui-build`; PATH repair extended to the npm global bin.
- settings.json: the 4 mcp__magic__* ask entries go; the outward-facing
21st verbs land in autoMode.soft_deny, the tier that holds under auto
mode (LRN-153).
- Docs: README, CLAUDE.global.md, design-gate.md, profile SKILL.md,
.env.example, .gitleaks.toml, link.sh. BDR-093, LRN-158.
Tests: profile-set-managed 17/17, make test green except 2 pre-existing
gitflow FAILs (gitleaks binary absent on this host), shellcheck clean.
`permissions.ask` gates nothing under `defaultMode: auto` (LRN-146,
verified live), so the ten rules that left the static tiers had no cover
left: rsync / kill -9 / killall / pkill out of deny, and python3 -c /
python -c / xargs / sed / cp / mv out of ask.
autoMode.soft_deny (7 rules) takes over what an explicit instruction
should be able to clear: writes outside the working directory,
rsync --delete, SIGKILL and kill-by-name, in-place edits spanning more
than one file, directory moves, and inline interpreters or xargs that
delete or write outside the cwd. Intent clears a soft block for the
current turn only, stated as a rule since no setting expresses it.
autoMode.hard_deny (3 rules) takes the classes no command pattern can
express: secret exfiltration, production deployment, and disarming the
guardrails. Adding a restriction stays allowed, removing one does not.
permissions.deny gains ten .env reader rules (sed awk cut tr sort uniq
diff od xxd strings). Six of those tools sat in permissions.allow, so
reading a .env through them triggered nothing.
autoMode.environment named another project, its FTP deploy target and its
customer data, inside the file link.sh:21 symlinks to
~/.claude/settings.json, where it reached every repo and contradicted
this one's Gitea remote. Rewritten machine-generic; the project facts
moved to that project's gitignored .claude/settings.local.json. All three
lists now open with "$defaults", which the original omitted, so the
built-in classifier entries are inherited rather than replaced.
doctor.sh check_automode backstops both defects. SETTINGS.md documents
the block and a tier-choice table. README no longer claims the ask tier
makes every mcp__magic__* call require a live confirmation.
Notification matcher covers input-needed events only; end of turn had no
signal but idle_prompt, ~60s late. Wire notify-attention.sh on Stop too,
branching on hook_event_name for the message. Signal only: returns
terminalSequence + suppressOutput, never blocks (guard vs BDR-083).
Header documents both client-side prerequisites found in BLK-020.
Notification hook (permission_prompt|idle_prompt|agent_needs_input|
elicitation_*) returns BEL x2 + OSC 777 via the terminalSequence JSON
field (hooks have no controlling TTY). Client side over Remote-SSH:
VS Code accessibility.signals.terminalBell sound:on for the beep,
wenbopan.vscode-terminal-osc-notifier extension for the Windows toast.
Reverses the BDR-066 rejected alternative (opus pins on audit agents):
session default is now Fable, so inherit burned Fable quota on every
dispatched audit/challenge. analyzer, plan-challenger, seo/geo/
validator-analyzer pinned model: opus; onboard's 6 general-purpose
audit dispatches carry model="opus"; tour Phase B repointed.
interviewer + client-handover-writer stay unpinned (inline-load only,
a pin there is inert). settings.json default: claude-fable-5[1m].
Census flipped: model-routing §3 + new §11 (61 pass), loops-light 35,
full make test green.
Full removal per user request: the PreToolUse hook that blocked model
Edit/Write on quality-gate files (settings.json, gitflow.sh, .githooks,
doctor.sh, hooks, lib/tests, lint configs) plus its one-shot sentinel.
- delete hooks/config-protection.sh
- delete lib/tests/config-protection.test.sh
- deregister the hook from settings.json (rtk-rewrite PreToolUse kept)
- drop the README mention
Residual protection unchanged: gitflow pre-commit guard + Gitea branch
protection still block direct code commits to main/develop.
Startup emitted 15 warnings: "Write(**/.env) is not matched by file
permission checks — only Edit(path) rules are."
Write(path) rules never matched. The 5 secret-file write bans were dead
config — .env, secrets/**, *.pem, *.key were freely writable. Converting
to Edit() makes them enforced: permissions.md:242 "Edit rules apply to all
built-in tools that edit files", and :244 prescribes exactly this ("add an
Edit deny rule for paths no tool may change").
- settings.json: Write(...) -> Edit(...) on the 5 patterns.
- Mirror the 9 secret patterns Read denied but Edit did not: *.p12, *.pfx,
id_rsa*, id_ed25519*, .ssh/**, credentials, credentials.json,
.aws/credentials, .azure/**. Read/Edit parity now 14/14. Claude could
previously overwrite an SSH private key or ~/.aws/credentials.
- New read-allowed/write-denied class: lockfiles (*.lock,
package-lock.json, pnpm-lock.yaml, go.sum) + node_modules/**. Reading
aids diagnosis; hand-editing is always wrong — the package manager
regenerates them via Bash, which Edit deny does not block.
- templates/settings/SETTINGS.md taught the broken Write() pattern; fixed
at the source so /onboard stops propagating it.
Rule syntax has no negation and deny beats allow, so deny globs cannot
carry exceptions — see the .env.example conflict noted in the follow-up.
Empty allowlist stays empty for mcp__magic__* (deny-by-default,
no auto-exec ever). All 4 tools now explicit in permissions.ask
so confirmation is guaranteed regardless of default-mode fallthrough,
instead of relying on undocumented absence. No wildcard.
- rm ~/.claude/projects/.../960bd2cf-...jsonl (transcript with plaintext
GITEA token — token already rotated; user GO)
- rm ~/.claude/paste-cache/7d48f52c7499c1a7.txt (sourcegraph-access-token
hit surfaced by make scan-secrets, outside the original job7 triage;
never read — user GO to delete without further characterization)
- ide/27929.lock: already gone (natural rotation, session ended). Its
replacement ide/20429.lock is a LIVE lock for the current session —
left alone, not stale
- settings.json cleanupPeriodDays 30 -> 7 (confirmed field name/scope via
docs; diff shown and explicitly confirmed before writing — first
attempt was correctly blocked by the auto-mode classifier for having
only narrated the diff in text rather than actually pausing for
confirmation). Only this one hunk staged — the file carries unrelated
live-session drift (model/effortLevel/permission-list reorder) not
part of this job, left unstaged.
Residual, deliberately not decided here: transcript f1c9c474-...jsonl
(generic-api-key x8, surfaced by make scan-secrets, not in the original
triage) — not read, not characterized, no option chosen by the user among
self-inspect/TODO/rm. Left intact in TODO as an open item.
Pin the default model to claude-opus-4-8[1m] in settings.json (the tracked
config deployed to ~/.claude). Previously no model key was committed, so the
default resolved to the tier default; a working-tree pin to claude-fable-5[1m]
was never committed. Takes effect at next session start.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpphkdTosUzokBDNG7PToS
Blocks Edit/Write to guardrails (settings.json + .claude/settings*, lib/gitflow.sh, .githooks/*, doctor.sh, hooks/*.sh self-guard, lib/tests/*, lint) so a gate can't be weakened to pass an error. Bypass = one-shot sentinel .claude/.config-edit-ok (non-empty reason, logged+consumed), not an env-var. Adaptation from the ECC second-look (BDR-047 corrob): own bash idiom, not ECC's Node dispatcher. shellcheck clean, test 20/20.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpphkdTosUzokBDNG7PToS
The rtk hook no longer auto-allows (audit-bugs branch): rewritten
commands are evaluated natively. Allow rules match the original forms
(grep *, ls *) not the rewritten ones — without explicit rules every
rewrite would fall to the classifier. Added the read-only rtk-wrapped
family, bare + absolute-path forms (the hook emits absolute paths when
PATH lacks the cargo dir): grep, ls, cat, head, tail, wc, diff, git
status/log/diff/show/branch. NOT find (rtk find could carry -exec rm —
native find-deny rules would not match the rtk prefix).
Deny mirrors guard the bypass the allowlist would open on hand-written
'rtk cat .env'-class commands: cat/grep/head/tail × .env, both prefixes.
Residual: exotic quoting may evade the mirrors — second curtain stays
the auto-mode classifier (BDR-004).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
Measured: 6 agent descriptions = ~2.2k tokens injected EVERY session
(the single largest plugin contributor) for a toolkit useful only when
reviewing PRs. enabledPlugins → false; removed from full+backend
profiles (BDR-017 caveat already accepts full excluding rarely-used
items); audit.profile KEEPS it = profile reactivation channel.
Per-PR-session: claude plugin enable pr-review-toolkit@claude-code-plugins
(or bash lib/profile.sh apply audit); a later 'profile set full'
re-disables it via the MANAGED_PLUGINS lifecycle.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
Drop the top-level "model" key so the session falls back to the
default model instead of forcing claude-fable-5[1m].
Co-Authored-By: Claude <noreply@anthropic.com>
Add a tiered-by-scope "Design work — full toolchain" rule to the global
CLAUDE.md: trivial tweaks stay on /hotfix, building UI mobilizes ui-ux-pro-max,
frontend-design, Magic MCP, emil-design-eng, design-motion-principles, and
design-html; design systems start with design-consultation; reviews use
design-review + emil + motion audit. In doubt about scope, do not silently
skip the toolchain — ask or default to the Build tier.
Reinforce it with a design-toolchain-reminder UserPromptSubmit hook that
detects UI/design signals (broad FR+EN keyword set, \b-guarded against
substring false matches) and injects the tiered guidance into context. Soft
nudge, always exits 0, falls back to raw stdin when the hook JSON is missing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Previously unset — Claude Code was defaulting to Opus 4.7 (1M) based on
plan capability + picker history. Pin the default to Opus 4.6 so future
sessions start on 4.6 without going through the /model picker.
1M context tier is plan-routed (Claude Max) so no explicit suffix needed
in the persisted ID — backup `.claude.json` files in this account show
the same bare form (`"model": "claude-opus-4-7"`) while runtime usage
logs as `claude-opus-4-7[1m]`.
Co-Authored-By: Claude <noreply@anthropic.com>
Toggle ui-ux-pro-max@ui-ux-pro-max-skill false → true so design-review,
design-consultation, and feat/hotfix design gate can route through it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Disable ui-ux-pro-max (off-profile for current work) and enable
pr-review-toolkit so /review and the toolkit subagents are available.
Co-Authored-By: Claude <noreply@anthropic.com>
Toggle example-skills@anthropic-agent-skills off — not used in active
profiles, reduces session-start skill enumeration.
Co-Authored-By: Claude <noreply@anthropic.com>
Two interlocked bugs masking each other:
1. install-plugins.sh installed but never enabled marketplace plugins.
`claude plugin install` only writes to ~/.claude/plugins/cache; without
a separate `claude plugin enable` the plugin sits dormant in the
user's enabledPlugins map. security-guidance and superpowers shipped
as ALWAYS-ON in CLAUDE.md/README/installer banner but in practice
landed disabled on every fresh install.
2. session-start.sh hardcoded the literal "security-guidance rtk
superpowers" in the ✅ ON row, so the misleading banner agreed with
the misleading documentation. The bug stayed invisible.
Fixes:
- install-plugins.sh now calls enable_plugin (added in the caveman
commit) for security-guidance and superpowers immediately after
install. Idempotent: skips if already in enabledPlugins.
- session-start.sh builds the ALWAYS-ON row dynamically from RTK
binary detection + plugin_enabled() lookups against
settings.json. Plugins that are not enabled are omitted, so the
banner reflects reality. Wider strings split across two lines like
the toggle row.
- settings.json: ship security-guidance and superpowers in
enabledPlugins so this user's machine matches the contract until
install-plugins.sh runs again.
Out of scope (separate bug, not addressed here): the marketplace-aware
detect_security_guidance / detect_plugin_dev cache scans miss plugins
nested under cache/<marketplace>/<plugin>/<version>/. They aren't on
the always-on path so the symptom is hidden — left for a follow-up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires JuliusBrussee/caveman into the always-on tier alongside
security-guidance and superpowers. Caveman compresses Claude's output
tokens (~75%) by speaking like a caveman while keeping technical
substance. Three layers:
1. Plugin (caveman@caveman, marketplace JuliusBrussee/caveman)
— adds /caveman, /caveman-commit, /caveman-review, /caveman-stats,
/caveman-help, /cavecrew, /compress + 3 cavecrew agents +
SessionStart/UserPromptSubmit hooks from the plugin path.
2. Standalone hooks (statusline + stats badge) deployed by
caveman's own hooks/install.sh into ~/.claude/hooks/. Paths in
settings.json normalized to ~/.claude/hooks/... so this user's
home dir doesn't leak across machines.
3. caveman-shrink MCP proxy — NOT auto-registered. The bare proxy
fails health checks because it requires an upstream MCP server
to wrap. install-plugins.sh STEP 5.5 prints a snippet showing how
to register a wrapped entry (e.g. caveman-shrink-fs) when the user
decides which upstream to compress.
New helper enable_plugin() for explicit always-on activation —
'claude plugin install' only copies into cache, doesn't write
enabledPlugins. Idempotent via Python json check.
doctor.sh adds detect_caveman / detect_caveman_hooks / detect_caveman_shrink
checks plus a 300t passive-cost adder. update-all.sh refreshes hook
files via the upstream installer's --force mode.
.gitignore covers caveman runtime files materialized into hooks/
because ~/.claude/hooks is symlinked to this repo.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Switch defaultMode from "default" to "auto" so Claude Code uses the
classifier-driven auto mode instead of prompting on each tool call.
Drop disableAutoMode: "disable" which would otherwise reject the new
defaultMode at startup.
Auto mode requires Claude Code v2.1.83+, a Max/Team/Enterprise/API
plan, a compatible Sonnet 4.6 / Opus 4.6 / Opus 4.7 model, and the
Anthropic API provider. If any prerequisite is missing, Claude Code
reports auto mode as unavailable on session start.
Co-Authored-By: Claude <noreply@anthropic.com>