chore(memory): BDR-106 superpowers vendored — contract, plan r3, oracles, TODO, journal

This commit is contained in:
bastien
2026-09-28 14:54:53 +02:00
parent ddea411491
commit 7177258f3d
7 changed files with 404 additions and 0 deletions
+20
View File
@@ -1,5 +1,25 @@
# TODO
## 2026-09-28 — tier 2: vendor 7 superpowers skills, drop the plugin (feature/superpowers-vendored)
User go "fais le tier 2" (decision 2026-09-28, batch 1). Contract
`.claude/tasks/contracts/2026-09-28-superpowers-vendored-1357.md`.
- [x] V1 plugins.lock.json `superpowers` entry (obra/superpowers @ 5bf4e78 = v6.4.1,
path skills, dict of 7 file lists); install-plugins.sh STEP 5 stops installing
the plugin, STEP 8e vendors it; update-all.sh refresh; link.sh EXTERNAL_SKILLS;
.gitignore; profile.sh PROTECTED_PLUGINS; detect-plugins/session-start/doctor
read the vendored dir, injection cost gone.
- [x] V2 citers: `superpowers:<x>` → `<x>` in ship-feature, init-project, tour, deploy,
audit-delta, lib/analyze-before-plan, plugin-advisor; finishing-a-development-
branch prose in capitalize-commit/doc-commit/gitflow; CLAUDE.global.md routing
map for the 8 dropped skills; README/USAGE/plugin-advisor/profile SKILL.md;
CHANGELOG.
- [x] V3 plan r1→r3 (3 challengers + confirmation), 2 feater DONE, live vendor + link
(VENDORED_LINKED), settings.json hand-edited, plugin + marketplace uninstalled,
GATE 0 MET 10/10, verifier CONFORME 12/12, security PASS; 18f8c89 ddea411; BDR-106.
Catalog 82 skills, passive plugins 670 t. UNMERGED — human gate. Other machines:
`make plugin` + `make link`, uninstall the cached plugin by hand. User: remove
`/tmp/tmp.PKDTRyaCw8` `/tmp/tmp.99Fu0dm8ll` (executor fixtures, rm refused).
## 2026-09-28 — design gate asks for `21st login` and waits (feature/skill-catalog-prune)
User: "si on veut l'utiliser, on demande à l'utilisateur de se log, plus simple que
dire c'est pas logged on utilise pas… on demande de log si c'est pas fait et on
@@ -0,0 +1,69 @@
# CONTRACT — superpowers-vendored
- date: 2026-09-28 | flow: feat (ad-hoc dispatch, /feat gates replayed by the orchestrator, 2 parallel feater executors) | branch: feature/superpowers-vendored
- status: active
## REQUEST (verbatim — IMMUTABLE)
> ok merge le tout et écris les registres puis fais le tier 2
Tier 2 as decided 2026-09-28 (batch 1, option "Vendoriser 7, retirer le plugin (Recommended)"): vendor brainstorming, writing-plans, subagent-driven-development, test-driven-development, requesting-code-review, using-git-worktrees, writing-skills from obra/superpowers at the v6.4.1 commit via `lib/vendor-skills.sh`, drop the superpowers plugin (its 8 other skills and its session-start injection), rename the `superpowers:` citers.
## CLARIFICATIONS
- Pass A: none — request complete (the decision batch fixed scope and outcome).
- Pass B: no visible / public-name choice left open — the vendored skills keep their upstream names (bare, no `superpowers:` prefix; renaming would break their internal cross-references), the lock key is `superpowers`, the always-on status is inherited (not in MANAGED_EXTERNALS, like darwin-skill). Proceeds silently.
- Byte-for-byte upstream text (BDR-104 convention): the vendored files are never edited, so their internal `superpowers:<x>` mentions and references to the 8 dropped skills (executing-plans, finishing-a-development-branch, systematic-debugging, verification-before-completion, dispatching-parallel-agents, receiving-code-review, using-superpowers, diagnosing-superpowers) stay in the text; CLAUDE.global.md carries the routing map (bare names; executing-plans → subagent-driven-development; finishing-a-development-branch → `gitflow finish` on a human signal; systematic-debugging → bugfix; verification-before-completion → the verifier gates). Known residual, documented.
- Scripts inside the vendored skills are invoked as `bash scripts/<x>` upstream: no exec bit needed after curl.
- `docs/superpowers/{specs,plans}` stays the transient path (brainstorming/writing-plans still write there; gitflow purge unchanged, BDR-065).
- Live steps are the orchestrator's: criterion 2 runs the vendor helper (network) + link.sh; the plugin uninstall (`claude plugin uninstall superpowers@superpowers-marketplace`) runs AFTER the 7 skills are linked, then criterion 8 checks the catalog. Executors never run `claude plugin …`, the vendor helper against the network, link.sh, `profile.sh set`, never commit.
- [challenge 2026-09-28, 3 lenses: simplicity CONCERNS(2), robustness CONCERNS(3), correctness FATAL(5); every BLOCKER/MAJOR closed by a named plan change, r2] (a) CLAUDE.global.md map never spells the colon form; (b) `always_on` lock field + doctor-vendored always-on class, test case; (c) no uninstall code in the installer, one-shot by the orchestrator after criterion 2, marketplace removed too, rollback step; (d) settings.json hand-edited (enabledPlugins key + marketplace block) and committed; (e) detect_superpowers = file test on the linked skill, no fallback, negative control in criterion 5; (f) map trimmed, lock note trimmed, session-start line deleted plainly.
- [confirmation pass 2026-09-28, correctness CONCERNS(1), all closed by named changes, r3] map identifiers kept whole per line (grep is line-based); `always_on` mechanism pinned (third lock column, 5th `_dv_check_link` param, headers); settings.json edited by the orchestrator only after criterion 2 is green; stale installer edge case removed; doctor pass line worded on what it proves.
- Functions ≤ 25 logic lines, 80-char lines, shellcheck clean.
## ACCEPTANCE CRITERIA
1. plugins.lock.json carries the `superpowers` entry: obra/superpowers, commit 5bf4e78011075bcfc0dc295f0724994cd123ee71 (v6.4.1), path `skills`, dict of exactly the 7 skills with every upstream file listed (SKILL.md each; SDD scripts, code-reviewer.md, anthropic-best-practices.md included).
CHECK: python3 .claude/tasks/contracts/2026-09-28-superpowers-vendored-1357.oracles/c1.py
EXPECT: LOCK_OK
EVIDENCE: MET exit=0 marker-found :: LOCK_OK
2. Vendored + linked live: every listed file is under skills-external/<skill>/, byte-identical to the plugin cache copy, and the 7 symlinks resolve under ~/.claude/skills.
CHECK: bash -c 'source lib/vendor-skills.sh; vendor_pinned_skills superpowers' >/dev/null 2>&1; bash link.sh >/dev/null 2>&1; python3 .claude/tasks/contracts/2026-09-28-superpowers-vendored-1357.oracles/c2.py
EXPECT: VENDORED_LINKED
EVIDENCE: MET exit=0 marker-found :: VENDORED_LINKED
3. No `superpowers:` prefix remains in the personal catalog, agents, lib, hooks or doctrine (fixtures excluded; positive control first).
CHECK: echo 'x superpowers:brainstorming' | grep -q 'superpowers:' || exit 1; if git grep -n 'superpowers:' -- skills agents lib hooks CLAUDE.global.md ':!lib/tests/fixtures' | grep -v '^skills/synced'; then exit 1; fi; echo NO_PREFIX
EXPECT: NO_PREFIX
EVIDENCE: MET exit=0 marker-found :: NO_PREFIX
4. Installers and link wired: install-plugins.sh no longer installs/enables the plugin and vendors `superpowers` in STEP 8e; update-all.sh refreshes it; link.sh EXTERNAL_SKILLS lists the 7; .gitignore ignores the 7 skill symlinks and the 7 skills-external dirs.
CHECK: ! grep -qE 'install_plugin +"superpowers"|enable_plugin +"superpowers"' install-plugins.sh && grep -q 'vendor_pinned_skills superpowers' install-plugins.sh && grep -q 'vendor_pinned_skills superpowers refresh' update-all.sh && for s in brainstorming writing-plans subagent-driven-development test-driven-development requesting-code-review using-git-worktrees writing-skills; do grep -qE "^skills/$s\$" .gitignore || { echo "gitignore skills/$s"; exit 1; }; grep -qE "^skills-external/$s/\$" .gitignore || { echo "gitignore ext $s"; exit 1; }; sed -n '/^EXTERNAL_SKILLS=(/,/)/p' link.sh | grep -qw "$s" || { echo "link $s"; exit 1; }; done && echo WIRED
EXPECT: WIRED
EVIDENCE: MET exit=0 marker-found :: WIRED
5. profile.sh no longer protects the plugin; detect_superpowers is true on the linked vendored skill alone and false under an empty HOME (no plugin-cache glob, no claude call). [challenge r2]
CHECK: ! grep -q 'superpowers@superpowers-marketplace' lib/profile.sh && bash -c 'source lib/detect-plugins.sh; detect_superpowers' && E=$(mktemp -d) && ! HOME="$E" bash -c 'source lib/detect-plugins.sh; detect_superpowers' && rmdir "$E" && ! grep -qE 'compgen.*superpowers|plugin list.*superpowers' lib/detect-plugins.sh && echo DETECT_OK
EXPECT: DETECT_OK
EVIDENCE: MET exit=0 marker-found :: DETECT_OK
6. Suites and shellcheck: vendor-skills, doctor-vendored (with the new ALWAYS_ON_LINK_CHECKED case), doctrine-citers, skill-routing-census (live catalog with the 7), profile-default, profile-set-managed green; shellcheck clean on every touched shell file. [challenge r2]
CHECK: shellcheck install-plugins.sh update-all.sh link.sh lib/profile.sh lib/detect-plugins.sh hooks/session-start.sh doctor.sh lib/doctor-vendored.sh lib/vendor-skills.sh lib/tests/doctor-vendored.test.sh && out=$(make test suite=lib/tests/doctor-vendored.test.sh 2>&1) && echo "$out" | grep -q 'PASS ALWAYS_ON_LINK_CHECKED' && for s in vendor-skills doctor-vendored doctrine-citers skill-routing-census profile-default profile-set-managed; do out=$(make test suite=lib/tests/$s.test.sh 2>&1) || { echo "$s rc"; exit 1; }; echo "$out" | grep -qE 'FAIL=[1-9]|^FAIL ' && { echo "$s FAIL"; exit 1; }; done; echo SUITES_OK
EXPECT: SUITES_OK
EVIDENCE: MET exit=0 marker-found :: SUITES_OK
7. CLAUDE.global.md Skill routing carries the map for the dropped skills and says the seven are vendored, bare names.
CHECK: grep -q 'finishing-a-development-branch' CLAUDE.global.md && grep -q 'executing-plans' CLAUDE.global.md && grep -q 'systematic-debugging' CLAUDE.global.md && grep -qi 'vendored' CLAUDE.global.md && echo ROUTING_OK
EXPECT: ROUTING_OK
EVIDENCE: MET exit=0 marker-found :: ROUTING_OK
8. Live after the orchestrator's uninstall + marketplace removal: no superpowers plugin installed, no enabledPlugins key, no extraKnownMarketplaces block, the 7 skills still resolve, `make doctor` reports superpowers as vendored, not failed. [challenge r2]
CHECK: ! claude plugin list 2>/dev/null | grep -q 'superpowers@superpowers-marketplace' && python3 -c "import json,sys;d=json.load(open('settings.json'));assert 'superpowers@superpowers-marketplace' not in d['enabledPlugins'];assert 'superpowers-marketplace' not in d.get('extraKnownMarketplaces',{})" && for s in brainstorming writing-plans subagent-driven-development test-driven-development requesting-code-review using-git-worktrees writing-skills; do [ -f "$HOME/.claude/skills/$s/SKILL.md" ] || { echo "missing $s"; exit 1; }; done && bash doctor.sh 2>/dev/null | grep -qi 'superpowers.*vendored' && ! bash doctor.sh 2>/dev/null | grep -qi 'Superpowers not detected' && echo PLUGIN_GONE
EXPECT: PLUGIN_GONE
EVIDENCE: MET exit=0 marker-found :: PLUGIN_GONE
9. doctor.sh and session-start.sh stop charging the plugin injection (no `+ 1500` / `+ 800` superpowers constant; doctor message names the vendored skills).
CHECK: ! grep -qE 'detect_superpowers.*\+ ?(1500|800)' doctor.sh hooks/session-start.sh && grep -qi 'vendored' doctor.sh && echo DOCTOR_OK
EXPECT: DOCTOR_OK
EVIDENCE: MET exit=0 marker-found :: DOCTOR_OK
10. Docs: README component table row (vendored skills, pinned v6.4.1, lock entry), USAGE.md mentions of "superpowers" as a plugin or a passive cost reworded, agents/plugin-advisor.md compatibility/recommended-set rows and the "not active → install" remedy reworded, skills/profile/SKILL.md:59 always-on sentence updated, CHANGELOG `[Unreleased]` entry (Changed: superpowers plugin → 7 vendored skills; Removed: the 8 other skills + injection; Known residual: upstream cross-references).
11. lib/capitalize-commit.md, lib/doc-commit.md, lib/analyze-before-plan.md and skills/gitflow/SKILL.md describe finishing-a-development-branch as the upstream skill this config does not vendor (gitflow finish replaces it), not as an active skill.
12. doctor-vendored treats the 7 as always-on: `bash doctor.sh` prints a pass line for each of the 7 (linked) and never "parked" for them. [challenge r2]
CHECK: out=$(bash doctor.sh 2>/dev/null); for s in brainstorming writing-plans subagent-driven-development test-driven-development requesting-code-review using-git-worktrees writing-skills; do echo "$out" | grep -qE "✓.*\b$s\b" || { echo "no pass for $s"; exit 1; }; echo "$out" | grep -qE "$s.*parked" && { echo "parked $s"; exit 1; }; done; echo ALWAYS_ON_OK
EXPECT: ALWAYS_ON_OK
EVIDENCE: MET exit=0 marker-found :: ALWAYS_ON_OK
## FILE SCOPE
- plugins.lock.json, install-plugins.sh (STEP 5 superpowers block, STEP 8e, summary lines), update-all.sh (7.3), link.sh (EXTERNAL_SKILLS), .gitignore, lib/profile.sh (PROTECTED_PLUGINS + comments), lib/detect-plugins.sh, hooks/session-start.sh, doctor.sh, lib/doctor-vendored.sh, lib/tests/doctor-vendored.test.sh, lib/vendor-skills.sh (lock-shape header comment line)
- skills/{ship-feature,init-project,tour,deploy,audit-delta,gitflow,profile}/SKILL.md, lib/{analyze-before-plan,capitalize-commit,doc-commit}.md, agents/plugin-advisor.md, CLAUDE.global.md (Skill routing lines), README.md, USAGE.md, CHANGELOG.md
- Orchestrator-only, after criterion 2: settings.json (enabledPlugins key + extraKnownMarketplaces block, hand edit), `claude plugin uninstall` + `claude plugin marketplace remove` (cache), rollback if criterion 8 fails; skills-external/<7> (gitignored, curl) and ~/.claude/skills symlinks are written by criterion 2
- Orchestrator-only: .claude/tasks/**, .claude/memory/**
@@ -0,0 +1,16 @@
import json,re
d=json.load(open('plugins.lock.json'))
e=d['superpowers']
assert e['source']=='https://github.com/obra/superpowers', e['source']
assert e['commit']=='5bf4e78011075bcfc0dc295f0724994cd123ee71', e['commit']
assert e['path']=='skills', e.get('path')
assert e.get('managed_by')=='curl'
want={'brainstorming','writing-plans','subagent-driven-development','test-driven-development','requesting-code-review','using-git-worktrees','writing-skills'}
assert set(e['skills'])==want, set(e['skills'])^want
for k,files in e['skills'].items():
assert 'SKILL.md' in files, k
for f in files: assert re.fullmatch(r'[A-Za-z0-9._/-]+',f) and '..' not in f, f
assert 'scripts/sdd-workspace' in e['skills']['subagent-driven-development']
assert 'code-reviewer.md' in e['skills']['requesting-code-review']
assert 'anthropic-best-practices.md' in e['skills']['writing-skills']
print('LOCK_OK')
@@ -0,0 +1,17 @@
import json,os,hashlib,glob
H=os.path.expanduser('~')
e=json.load(open('plugins.lock.json'))['superpowers']
cache=glob.glob(H+'/.claude/plugins/cache/superpowers-marketplace/superpowers/6.4.1/skills')
missing=[];mism=[]
for k,files in e['skills'].items():
for f in files:
p=f'skills-external/{k}/{f}'
if not os.path.isfile(p): missing.append(p); continue
if cache:
c=f'{cache[0]}/{k}/{f}'
if os.path.isfile(c) and hashlib.md5(open(p,'rb').read()).hexdigest()!=hashlib.md5(open(c,'rb').read()).hexdigest(): mism.append(p)
link=f'{H}/.claude/skills/{k}'
if not (os.path.islink(link) and os.path.isfile(link+'/SKILL.md')): missing.append(link)
assert not missing, missing
assert not mism, ('byte mismatch vs plugin cache',mism)
print('VENDORED_LINKED')
@@ -0,0 +1,271 @@
# PLAN — superpowers-vendored (feat, ad-hoc dispatch) — r3 (after confirmation pass)
- r3 closes the confirmation pass (correctness CONCERNS(1)): MAJOR 1 — the
CLAUDE.global.md map keeps every skill identifier whole on one line (grep
is line-based); MINOR 2 — `always_on` mechanism pinned: the python lock
reader emits a third column, `_dv_check_link` gets a 5th param, headers
updated, a helper extracted if `check_vendored_skills` would exceed 5
locals; MINOR 3 — stale "uninstall || true" edge case deleted; MINOR 4 —
settings.json edit moves to the orchestrator, AFTER criterion 2 is green
(a disabled plugin + a failed fetch must never coincide); MINOR 5 —
profile.sh comments located by grep, doctor pass line worded on what is
proven.
- r2 closes: correctness BLOCKER 1 (the CLAUDE.global.md map never spells the
colon form — criterion 3 greps it), MAJOR 2 (doctor-vendored gains an
always-on class driven by a lock field `always_on`, so the 7 are
link-checked instead of "parked"), MAJOR 3 + robustness MAJOR 1 (NO
uninstall code in the installer — comment only, one-shot by the
orchestrator after criterion 2 is green), MAJOR 4 + robustness MAJOR 3
(settings.json hand-edited: enabledPlugins key and
extraKnownMarketplaces.superpowers-marketplace block removed, committed),
MAJOR 5 + robustness MAJOR 2 + simplicity MAJOR 1 (detect_superpowers =
one file test on the linked skill, no plugin fallback, no new global),
simplicity MAJOR 2 (same: no installer uninstall), MINORs: session-start
line deleted plainly, map trimmed to the four referenced skills, lock note
kept to maintainer facts, summary line placement pinned, rollback step,
mixed-version rollback note.
- date: 2026-09-28 | contract: contracts/2026-09-28-superpowers-vendored-1357.md
- branch: feature/superpowers-vendored
- executors: 2 feater (sonnet-pinned), parallel, disjoint file sets
## Ground truth (verified 2026-09-28)
- Plugin superpowers 6.4.1 installed at
`~/.claude/plugins/cache/superpowers-marketplace/superpowers/6.4.1/`
(gitCommitSha 5bf4e78011075bcfc0dc295f0724994cd123ee71 = upstream tag
v6.4.1 on obra/superpowers; raw files served at
`https://raw.githubusercontent.com/obra/superpowers/<sha>/skills/<skill>/<file>`,
brainstorming/SKILL.md md5 identical local vs raw). Enabled in settings.json
(`superpowers@superpowers-marketplace: true`), PROTECTED in lib/profile.sh,
installed + enabled by install-plugins.sh STEP 5 (marketplace add,
install_plugin, enable_plugin), summary line "ALWAYS ON … superpowers".
Its hooks.json SessionStart (startup|clear|compact) injects
using-superpowers (~3.6 KB) every start.
- The 7 skills to vendor and their files (upstream layout `skills/<name>/`):
brainstorming: SKILL.md, spec-document-reviewer-prompt.md, visual-companion.md,
scripts/frame-template.html, scripts/helper.js, scripts/server.cjs,
scripts/start-server.sh, scripts/stop-server.sh
writing-plans: SKILL.md, plan-document-reviewer-prompt.md
subagent-driven-development: SKILL.md, implementer-prompt.md,
re-review-prompt.md, task-reviewer-prompt.md, scripts/review-package,
scripts/sdd-workspace, scripts/task-brief
test-driven-development: SKILL.md, writing-good-tests.md
requesting-code-review: SKILL.md, code-reviewer.md
using-git-worktrees: SKILL.md
writing-skills: SKILL.md, anthropic-best-practices.md,
examples/CLAUDE_MD_TESTING.md, graphviz-conventions.dot,
persuasion-principles.md, render-graphs.js, testing-skills-with-subagents.md
Scripts are invoked upstream as `bash scripts/<x>` (SDD lines 137, 252,
290…; brainstorming visual-companion.md) → no exec bit needed.
- Internal cross-references that will dangle (byte-for-byte text):
writing-plans → superpowers:subagent-driven-development, superpowers:executing-plans (dropped), superpowers:using-git-worktrees;
SDD → superpowers:finishing-a-development-branch ×4 (dropped), superpowers:using-git-worktrees, superpowers:requesting-code-review, executing-plans ×2;
TDD → superpowers:writing-skills; writing-skills → superpowers:test-driven-development ×4, superpowers:systematic-debugging (dropped), using-superpowers, verification-before-completion.
- `lib/vendor-skills.sh` `vendor_pinned_skills <lock-key> [refresh]`: lock
entry `{source, commit (40 hex), path, skills: {name: [files]}, managed_by}`;
files must match `[A-Za-z0-9._/-]+`, no `..`; tmp+mv; skips existing files
unless `refresh`. install-plugins.sh STEP 8e calls it for agent-skills and
mengto-skills with `EXT_SKILL_NAMES` symlink check; update-all.sh 7.3 calls
it with `refresh`. link.sh `EXTERNAL_SKILLS=(…)` symlinks
`skills-external/<name>` into `~/.claude/skills/<name>`; .gitignore lists
`skills/<name>` (symlink) and `skills-external/<name>/` (vendored text) per
external. lib/doctor-vendored.sh reads the lock + EXTERNAL_SKILLS generically.
- lib/profile.sh: `PROTECTED_PLUGINS=("security-guidance@claude-code-plugins"
"superpowers@superpowers-marketplace")`; MANAGED_EXTERNALS is the allowlist
`set` parks — the 7 are NOT added (always on, like darwin-skill).
- lib/detect-plugins.sh `detect_superpowers`: plugin cache glob then `claude
plugin list`. Consumers: hooks/session-start.sh:122 (`+ 800` passive),
doctor.sh:225-228 (pass/fail "Superpowers plugin detected / not detected —
orchestrators will fail") and :423 (`+ 1500`).
- `superpowers:` citers (personal): skills/ship-feature:103,117,176,237;
skills/init-project:71,182,215,259; skills/tour:318; skills/deploy:515;
skills/audit-delta:321; lib/analyze-before-plan.md:106;
lib/capitalize-commit.md:20 (finishing-a-development-branch);
agents/plugin-advisor.md:182. Prose mentions of
finishing-a-development-branch: lib/capitalize-commit.md:68,
lib/doc-commit.md:84, lib/analyze-before-plan.md:108, skills/gitflow:16,110.
Docs: README.md:121 (component table), USAGE.md ×19 (plugin/cost
narrative), agents/plugin-advisor.md ×19 (matrix, recommended sets, remedy
:324), skills/profile/SKILL.md:59, install-plugins.sh:1210 summary.
`docs/superpowers/` paths (CLAUDE.md, gitflow, onboard) stay: brainstorming
and writing-plans still write there.
- lib/tests: gitflow-test.sh mentions superpowers only through the purge
path (unchanged). No suite asserts PROTECTED_PLUGINS content.
## Approach
### E1 — wiring (lock, installers, link, gitignore, profile, detect, doctor)
Files: plugins.lock.json, install-plugins.sh, update-all.sh, link.sh,
.gitignore, lib/profile.sh, lib/detect-plugins.sh, hooks/session-start.sh,
doctor.sh, lib/doctor-vendored.sh, lib/tests/doctor-vendored.test.sh,
lib/vendor-skills.sh (header comment line only).
1. plugins.lock.json: new entry `"superpowers"` after `"mengto-skills"`:
source `https://github.com/obra/superpowers`, commit
`5bf4e78011075bcfc0dc295f0724994cd123ee71`, path `skills`, `skills` = the
dict above (exact file lists), managed_by `curl`, `"always_on": true`,
note (maintainer facts only, history lives in CHANGELOG/BDR-106): "Seven
superpowers skills vendored byte-for-byte at the v6.4.1 tag commit
(obra/superpowers), always on (no profile lists them). Bump the commit
deliberately. Scripts inside run as `bash scripts/<x>`, no exec bit
needed. Upstream cross-references to the plugin prefix and to the 8
non-vendored skills stay in the text; CLAUDE.global.md Skill routing maps
them."
2. install-plugins.sh STEP 5: delete the three superpowers lines (marketplace
add, install_plugin, enable_plugin) and replace with a 3-line comment
"Superpowers plugin removed 2026-09-28 (tier 2 of the skill-catalog prune):
its 7 wired skills are vendored in Step 8e (plugins.lock.json
'superpowers'); a still-cached plugin is uninstalled by hand once
(claude plugin uninstall superpowers@superpowers-marketplace), never here".
NO uninstall code in the installer (precedent: frontend-design, caveman).
Update the `enable_plugin` comment (:490) to name only security-guidance.
STEP 8e: heading/comment mention superpowers; `EXT_SKILL_NAMES` += the 7;
`vendor_pinned_skills superpowers` after mengto. Summary: replace line
~1210 ("✅ superpowers — brainstorm/plan/implement/debug workflow", ALWAYS
ON block) by "✅ superpowers skills — 7 vendored (brainstorming,
writing-plans, subagent-driven-development, test-driven-development,
requesting-code-review, using-git-worktrees, writing-skills), pinned
v6.4.1, curl → symlink, no plugin, no session injection"; add one "at:"
line right after the mengto "at:" line (~1234): "Superpowers skills at:
~/.claude/skills/{brainstorming,…}/ (symlink → skills-external)".
3. update-all.sh 7.3: `echo "── Updating superpowers skills (obra/superpowers)..."`
+ `vendor_pinned_skills superpowers refresh`; comment names it.
4. link.sh EXTERNAL_SKILLS += the 7 (keep the array multi-line ≤ 80 chars).
5. .gitignore: 7 `skills/<name>` lines next to the other external symlinks
(:65-68 block) and 7 `skills-external/<name>/` lines next to the mengto
block (:203-207), each block with a one-line comment "superpowers, vendored
(plugins.lock.json 'superpowers')".
6. lib/profile.sh: PROTECTED_PLUGINS keeps only security-guidance; every
comment naming superpowers as an always-on plugin (`grep -n superpowers
lib/profile.sh`, currently ~:22 and ~:65) reworded ("superpowers is
vendored skills now, not a plugin").
7. lib/detect-plugins.sh `detect_superpowers`: exactly
`[ -f "$HOME/.claude/skills/brainstorming/SKILL.md" ]` (the linked
vendored skill: proves vendored AND linked; no plugin cache glob, no
`claude plugin list`, no new global, no fallback). Comment: "superpowers
= 7 vendored skills since 2026-09-28; the plugin is gone". A negative
control (empty HOME) must return 1.
7b. lib/doctor-vendored.sh: lock entries may carry `"always_on": true`
(the `superpowers` entry does). Skills of such an entry are expected
LINKED whatever the active profile says (today every EXTERNAL_SKILLS name
absent from the profile is reported `parked`, link unchecked — the 7 are
in no profile by design). Mechanism: `_dv_lock_expectations` (python)
prints a THIRD column `name\tfile\t1` for skills of an `always_on` entry
(awk `$1==n {print $2}` in `_dv_check_files` keeps working unchanged);
`check_vendored_skills` reads the flag and passes it as a 5th parameter to
`_dv_check_link`, which treats `1` as "expected linked whatever the
profile says". If `check_vendored_skills` would exceed 5 locals, extract
the per-name dispatch into a helper (≤ 25 logic lines each). Update the
file header ("A name absent from the profile is reported parked" → "…
unless its lock entry is always_on") and the test header. Message
unchanged for the linked case, fail "<name>: symlink missing/wrong — run:
make link" when absent. Add a case
`ALWAYS_ON_LINK_CHECKED` to lib/tests/doctor-vendored.test.sh (fixture
entry with always_on true, name absent from the profile, link missing →
fail line, never `parked`). Document the field in lib/vendor-skills.sh's
lock-shape header comment (one line: ignored by the vendor helper, read
by doctor-vendored).
8. hooks/session-start.sh: delete line 122 (`detect_superpowers … + 800`)
outright — the banner's ALWAYS_ON list comes from detect_rtk + settings
enabledPlugins (lines ~147-160), not from this call. doctor.sh :225-228:
pass "superpowers: 7 skills vendored + linked (plugins.lock.json, v6.4.1)"
/ fail "superpowers skills not linked — run: make plugin && make link";
the pass line is worded on what `detect_superpowers` proves
("superpowers skills linked (brainstorming found); per-skill check under
Vendored skills"); :423 delete the `+ 1500` line (comment: counted by the
skill catalog stats).
9. settings.json: NOT an executor file any more — the orchestrator edits it
after criterion 2 is green (see Orchestrator steps), so a disabled plugin
never coincides with a failed fetch.
### E2 — citers, routing map, docs
Files: skills/{ship-feature,init-project,tour,deploy,audit-delta,gitflow,profile}/SKILL.md,
lib/{analyze-before-plan,capitalize-commit,doc-commit}.md,
agents/plugin-advisor.md, CLAUDE.global.md, README.md, USAGE.md, CHANGELOG.md.
1. `superpowers:<x>` → `<x>` (bare) in ship-feature ×4, init-project ×4,
tour:318, deploy:515, audit-delta:321, lib/analyze-before-plan.md:106,
agents/plugin-advisor.md:182. Wording around them: "Invoke `brainstorming`
(vendored superpowers skill)" on first mention per file, bare afterwards.
2. finishing-a-development-branch prose: lib/capitalize-commit.md:20 → "Orchestrators
that integrate via `gitflow finish` (the upstream
finishing-a-development-branch is not vendored)"; :68, lib/doc-commit.md:84,
lib/analyze-before-plan.md:108, skills/gitflow:16,110 → say "upstream
superpowers skill, not vendored here; `gitflow finish` is the only
integration path" where they present it as available.
3. CLAUDE.global.md § Skill routing, after the "Before /clear or /compact"
line, ≤ 80 chars per line, about 4 lines, and NEVER the literal
"superpowers" followed by a colon (criterion 3 greps that string):
"- superpowers skills are vendored, called by bare name; an upstream
`superpowers` prefix means the bare skill. Not vendored:
executing-plans → subagent-driven-development;
finishing-a-development-branch → `gitflow finish` (human signal);
systematic-debugging → bugfix; verification-before-completion → the
verifier gates."
Every skill identifier stays WHOLE on its line (criterion 7 greps
`finishing-a-development-branch`, `executing-plans`, `systematic-debugging`
line by line); wrap at spaces only.
4. README.md:121 row → "**Superpowers skills** | Vendored (7, always on) |
brainstorming, writing-plans, subagent-driven development, TDD, code
review request, git worktrees, writing-skills — pinned v6.4.1 in
plugins.lock.json, no plugin, no session injection | obra/superpowers".
README:208 unchanged.
5. USAGE.md: every line presenting superpowers as a plugin to keep ON/OFF or
as ~800 t passive (184-185, 589, 650, 751, 864, 959-965, 971, 995, 1018)
→ "skills superpowers (vendorisés, toujours actifs, 0 t passif)" or the
equivalent in the sentence's French; keep the narrative otherwise.
6. agents/plugin-advisor.md: rows 177-182 (compat matrix) → "superpowers
skills (vendored)" wording, drop the plugin-dev overlap row's "plugin"
framing; recommended-set table 190-198: replace "superpowers" by
"(superpowers skills always on)" in the ON column and subtract ~800 t from
each cost; :80, :146, :242, :254, :298 reword; :324 remedy → "Superpowers
skills missing → `make plugin` (vendors them) then `make link`".
7. skills/profile/SKILL.md:59: "Always-on plugins (`security-guidance`) and
the vendored superpowers skills are never toggled by a profile".
8. CHANGELOG `[Unreleased]`: Changed (superpowers plugin → 7 vendored skills,
pinned, always on; `superpowers:` citers renamed), Removed (plugin, its 8
duplicate skills, the SessionStart injection), Known residual (upstream
cross-references inside the vendored text; CLAUDE.global.md map).
## Orchestrator steps
- Criterion 2 vendors + links live (network fetch of 30 files); only when
every file is present and byte-identical (c2.py) does the next step run.
- Then the orchestrator edits settings.json by hand: remove the
`"superpowers@superpowers-marketplace": true` key from `enabledPlugins` and
the whole `extraKnownMarketplaces."superpowers-marketplace"` block, nothing
else; validate with `python3 -c 'import json;json.load(open("settings.json"))'`.
- Then, one shot by hand: `claude plugin uninstall superpowers@superpowers-marketplace`
and `claude plugin marketplace remove superpowers-marketplace`; re-check
`git diff settings.json` afterwards (the CLI must not have re-added
anything), then criterion 8.
- Rollback if criterion 8 fails: `claude plugin marketplace add
obra/superpowers-marketplace && claude plugin install superpowers@superpowers-marketplace`,
`git checkout -- settings.json`, stop and report.
- Verifier; security; commit; BDR-106 + journal.
## Edge cases
- Mid-migration machine (plugin cached, skills not yet vendored): doctor
fails "not vendored or linked — run make plugin && make link"; the user
uninstalls the plugin by hand (CHANGELOG says so). No fallback that could
print "vendored" for a plugin-only machine.
- Mixed-version rollback (an older checkout re-installs the plugin while the
7 symlinks are still linked → duplicate descriptions): CHANGELOG note
"after a rollback, delete skills/<7> symlinks or re-run the new make plugin".
- The running session keeps the plugin's `superpowers` skills until restart;
the bare names appear after `make link` + a new session.
- Fresh clone: link.sh symlinks a non-existent skills-external dir only if
present (existing `[ -d ]` guard).
- skill-routing-census live run gains 7 descriptions: brainstorming's "You
MUST use this before any creative work" vs personal descriptions — the
suite's live FAIL threshold must not trip (check by running it).
## Tests
- make test suite= vendor-skills, doctor-vendored (with the new
ALWAYS_ON_LINK_CHECKED case), doctrine-citers, skill-routing-census,
profile-default, profile-set-managed.
- shellcheck on every touched shell file.
## Disposition (RELATED MEMORY)
- honors BDR-102 / BDR-104 — vendor over plugin, shared helper, pinned commit,
byte-for-byte text.
- honors BDR-105 — tier 2 of the prune decision.
- honors BDR-065 — docs/superpowers transient path unchanged.
- honors LRN-178 — no new top-level `source`; detect-plugins reads a path.
- honors BDR-077 — requesting-code-review's reviewer dispatch keeps the
model-routing note in ship-feature/init-project.