67 Commits
Author SHA1 Message Date
Bastien Chanot d8962824c0 Merge release/1.4.0 into main 2026-07-22 15:48:41 +02:00
Bastien Chanot 817a866b7c chore(release): 1.4.0 — version.txt + CHANGELOG 2026-07-22 15:26:34 +02:00
Bastien Chanot 2940134c86 Merge feature/gitflow-auto-purge-transient into develop 2026-07-22 15:12:50 +02:00
Bastien Chanot 78a25aeb5e chore(memory): BDR-065 amendment (auto-purge coded) + LRN-138 + journal
BDR-065 delete-side now automated (lib/gitflow.sh _gitflow_purge_transient).
LRN-138: gitignore != delete for run-time artifacts read from disk — use
commit-during-run + auto-delete at the integration boundary. TODO checked,
journal line.
2026-07-22 15:12:28 +02:00
Bastien Chanot 9b89da29be feat(gitflow): auto-purge transient superpowers artifacts at finish (BDR-065)
_gitflow_purge_transient removes docs/superpowers/{specs,plans} on the
feature/bugfix branch just before the directed merge, so develop's tip
lands clean while the feature commits stay reachable as the archive
(git show <sha>:...). Best-effort: never aborts a finish (no-op when
absent, skip on dirty paths, restore index+tree on commit failure).
Opt-out GITFLOW_PURGE_TRANSIENT=0; purge-transient CLI verb. Automates
the manual post-merge cleanup BDR-065 left as doctrine (slipped once,
655e364). Universal via the ~/.claude/lib symlink. gitflow-test T17 a-d;
shellcheck clean; make test exit 0.
2026-07-22 15:12:22 +02:00
Bastien Chanot 95ddd28992 Merge chore/skill-routing-bugfix into develop 2026-07-21 16:16:24 +02:00
Bastien Chanot 1ef6e6e694 chore(memory): BDR-080 bug routing inversion + journal line 2026-07-21 01:15:06 +02:00
Bastien Chanot 6c489ebcfb chore(routing): invert bug routing — bugfix primary, investigate explicit-only
investigate (gstack ON default) bypassed the whole quality pipeline:
gitflow aiguillage, contract, fresh verifier + security gates, doc-sync,
memory registries. bugfix now primary; investigate reserved for explicit
gstack-ecosystem asks (cross-project learnings, /freeze scope lock,
no-commit investigation). BDR-080.
2026-07-21 01:15:01 +02:00
Bastien Chanot 33f9529b9e chore(memory): BLK-018 classifier-blocked finish span + v1.3.1 journal line 2026-07-21 00:27:39 +02:00
Bastien Chanot 648bc6e90d Merge release/1.3.1 into main 2026-07-20 22:23:47 +02:00
Bastien Chanot f82ea1e4f8 Merge release/1.3.1 into develop 2026-07-20 22:23:47 +02:00
Bastien Chanot 75c81f3f9c chore(release): 1.3.1 — version.txt + CHANGELOG 2026-07-20 22:20:29 +02:00
Bastien Chanot 533fcc841e chore(memory): journal — README rebuild session 2026-07-20 22:17:48 +02:00
Bastien Chanot db6f476685 Merge chore/readme-v2 into develop 2026-07-20 22:09:02 +02:00
Bastien Chanot 90850096ef docs: README rebuilt — short pitch (what/how/why) on top, old content demoted to reference manual 2026-07-20 22:08:58 +02:00
Bastien Chanot 1cb77c3f57 Merge release/1.3.0 into main 2026-07-20 19:40:23 +02:00
Bastien Chanot 0a8ecf6c34 Merge release/1.3.0 into develop 2026-07-20 19:40:23 +02:00
Bastien Chanot 711eacd900 chore(release): 1.3.0 — version.txt + CHANGELOG 2026-07-20 19:39:35 +02:00
Bastien Chanot ce5b7fb3f4 Merge chore/readme-seo-env into develop 2026-07-20 19:25:07 +02:00
Bastien Chanot 10589d484b docs: README polish — real clone URL + make targets, magic example simplified, SEO env vars section
- Fresh-install block: clone URL → github.com/bchanot/claude, bash
  install.sh/doctor.sh → make install / make doctor (user pass)
- magic MCP example: placeholder key line instead of WRONG/RIGHT contrast
- new subsection: SEO data layer needs GOOGLE_OAUTH_CLIENT_ID/SECRET +
  CRUX_API_KEY in ~/.claude/.env (GCP steps, make seo-connect, graceful
  degradation) — mirrors .env.example
2026-07-20 19:25:07 +02:00
Bastien Chanot dc90aae9bd Merge chore/purge-transient-docs into develop
# Conflicts:
#	.claude/tasks/TODO.md
2026-07-20 17:07:11 +02:00
Bastien Chanot 37c79f0524 Merge feature/profile-managed-externals into develop 2026-07-20 17:04:53 +02:00
Bastien Chanot e75ea79ae6 chore(tasks): reconcile 2026-07-20 — 4 stale claims corrected + pending-gates section
- seo/geo STATUS: H1+C1 were done (url-guard, sitemap verb) and the branch
  merged (92301fe) + shipped v1.2.0 — 'NEXT'/'nothing merged' lines stale
- ctx7 + opus-pin sections: 'NO merge' notes stale (8ee7d19, 17fbe51 both
  shipped v1.2.0)
- f1c9c474 transcript decision moot: auto-rotated (cleanupPeriodDays=7)
- new open items: 2 unmerged branches + Makefile help-text fix
2026-07-20 16:46:42 +02:00
Bastien Chanot 655e364e80 chore(docs): purge transient plan/spec artifacts missed by post-merge cleanup
- docs/plans + docs/specs (deploy-skill 2026-06-27): predate the BDR-065
  lifecycle codification, never swept
- docs/superpowers/{plans,specs} (model-routing 2026-07-15): 6-wave
  chantier — final W6 merge closed it without the purge step
Git history at the feature commits is the archive (BDR-065).
2026-07-20 16:40:58 +02:00
Bastien Chanot ecbe8abde7 docs: profile list ×3 + test glob + BDR-ID strip + layout tree → ARCHITECTURE.md — /doc clean pass 2026-07-20 16:29:31 +02:00
Bastien Chanot 8008d8233c feat(profile): set symmetric on managed externals + MCPs (BDR-079)
- MANAGED_EXTERNALS (emil-design-eng, frontend-design,
  design-motion-principles, impeccable) + MANAGED_MCPS (magic):
  cmd_set now trims both when the profile does not list them —
  design leftovers no longer survive a 'set backend'
- cmd_set refactored to 4 symmetric trim helpers; nothing outside
  the MANAGED_* allowlists is ever auto-toggled (darwin-skill manual)
- enable_skill external: from-source fallback (ln -sf
  skills-external/<name>), mirrors toggle-external.sh
- stale usage() NOTE + SKILL.md updated to the both-ways reality
- hermetic test: 16 checks, fixture repo + fake claude shim (gstack
  on-demand, from-source, park/restore, magic add/remove, non-managed
  untouched); shellcheck + full make test green
2026-07-20 14:47:53 +02:00
Bastien Chanot 3c243ece97 Merge release/1.2.1 into main 2026-07-20 14:34:09 +02:00
Bastien Chanot 3166c1161e Merge release/1.2.1 into develop 2026-07-20 14:34:09 +02:00
Bastien Chanot e5a62cc049 chore(release): 1.2.1 — version.txt + CHANGELOG 2026-07-20 14:32:50 +02:00
Bastien Chanot b3a03fd974 chore(memory): journal — v1.2.0 cut + doc pass 2026-07-20 14:24:39 +02:00
Bastien Chanot aeb7bc05d8 Merge chore/doc-sync-v1.2.0 into develop 2026-07-20 14:24:11 +02:00
Bastien Chanot 45e679ac23 docs: README model-routing v2 table (BDR-076/077) + ctx7 surfaces (BDR-078) + STEP 2b — post-release 1.2.0 doc pass 2026-07-20 14:21:24 +02:00
Bastien Chanot 51b65727e7 Merge release/1.2.0 into main 2026-07-20 14:07:28 +02:00
Bastien Chanot 2d38ffd843 Merge release/1.2.0 into develop 2026-07-20 14:07:28 +02:00
Bastien Chanot 56571805a1 chore(release): 1.2.0 — version.txt + CHANGELOG 2026-07-20 14:06:14 +02:00
Bastien Chanot 8ee7d19d70 Merge feature/ctx7-coverage into develop 2026-07-20 14:02:33 +02:00
Bastien Chanot b7026e4bda feat(ctx7): coverage extension — fast-libs single source + reminder hook + executor briefs (BDR-078)
- lib/fast-libs.sh: detect/cache-status verbs, JS+Python manifests,
  7-day cache freshness, LC_ALL=C sort — replaces 3 hardcoded lists
  (ship-feature 0c, init-project 5c, onboard 3.5)
- hooks/ctx7-reminder.sh: once-per-session UserPromptSubmit nudge when
  the project carries fast-libs and .ctx7-cache/ is missing/stale
- find-docs: before-writing-code trigger + cache-first rule; dist is
  machine-owned (gitignored) so the durable patch lives in
  install-plugins.sh STEP ctx7 (idempotent, grep-guarded)
- feater/bugfixer briefs: fast-lib docs rule (fresh cache read, else
  2-topic ctx7 fetch, else NOTES cache miss + proceed)
- tests: lib/tests/fast-libs.test.sh (11 checks); shellcheck + full
  make test green (review-guards 5/0)
2026-07-20 10:45:06 +02:00
Bastien Chanot 6838d5a8fa Merge feature/model-tiering-w6-doctrine into develop 2026-07-19 23:56:58 +02:00
Bastien Chanot 444c79acb2 fix(routing): W6 ronde — 6 findings closed (README bootstrap path, 2 census gaps, 3 stale refs)
Fresh-opus whole-chantier ronde (17fbe51..HEAD, EVAL-023 style): axes
severed-wires / gate-regressions / fail-open PROVEN CLEAN (all 7 new
handoffs traced end-to-end both sides). Fixed: init 5b now dispatches the
FULL-AUDIT path (auto-mode gated a missing README as SIGNIFICANT →
[CREATE-AUTO] unconditional restored, sole greenfield README path);
census locks added for /geo ERROR CONTRACT + never-re-derive (deleting
the fail-closed handler would have stayed green), doc-audit
model=opus override x5 flows, SYNTH REPORT grammar; doc-syncer ex-STEP-8
prose repointed to the dispatcher gate; plugin-advisor anim rows read
the PROBE REPORT ANIM field (no Bash anymore); client-handover 9.7
cleans the transient draft. Census caught one more line-wrapped lock
before it shipped vacuous. 133 pass / 0 fail, make test exit 0.
2026-07-19 23:56:58 +02:00
Bastien Chanot 07253e093c feat(doctrine): W6 — prose sweep + BDR-077 + LRN-137 + plan execution notes
LRN-113 whole-surface sweep: client-handover x2 + commit-change prose
repointed to the two-mode reality; code-cleaner/status historic notes
kept (accurate). Memory: BDR-077 (full architecture), LRN-137
(mode-based re-tiering + fail-safe pin rule), journal. Plan carries
as-built EXECUTION NOTES.
2026-07-19 23:44:24 +02:00
Bastien Chanot 6c59a424ae Merge feature/model-tiering-w5-seo-geo into develop 2026-07-19 23:23:11 +02:00
Bastien Chanot 9e4ebb4cf4 feat(agents): W5 seo/geo 3-mode pipelines — collect sonnet / judge opus pin / template sonnet (BDR-077)
seo-analyzer + geo-analyzer gain MODE: collect|judge|template around the
dispatcher (mode-based, zero body-text moves — seo-data fetch-wiring
locks survive; opus pin kept = fail-safe direction, a forgotten override
over-tiers but never downgrades judgment). Run-scoped gitignored
signals handoff (.audit/*-signals-<RUNID>.md + COLLECTION COMPLETE
sentinel), judge fails closed on absent/mismatched/unsealed signals.
/seo rewired to 3 phases (domains parallel per phase) + DISPATCHER ERROR
CONTRACT (mute/ERROR judge never carried into templating; retry once,
escalate); /geo same single-domain; legacy no-MODE single-shot kept on
the opus pin for /harden narrow-scope + /onboard report-only. Dropped
/geo's 'ask and I relay' fiction (dispatched agents cannot ask).
In-wave smokes PASSED disk-verified: collect signals+sentinel; judge
ERROR-verdict on wrong RUNID; real judge = honest N/A + deterministic
engine + full scoring grammar; template = complete envelope + verbatim
sentinel + zero re-derivation. Census §18 (125 pass — one vacuous
line-wrapped lock caught by the census itself and fixed), make test
exit 0.
2026-07-19 23:23:10 +02:00
Bastien Chanot 6886622ecf Merge feature/model-tiering-w4-handover into develop 2026-07-19 22:59:35 +02:00
Bastien Chanot d2a10de08b feat(agents): W4 handover two-mode — synthesize opus / render sonnet, run-scoped draft handoff (BDR-077)
handover-doc-writer: MODE synthesize (model="opus" call-site — STEP
9/10/12 → .audit/handover-draft-<RUNID>.md + DRAFT COMPLETE sentinel) /
MODE render (sonnet pin — STEP 13-16 from the draft, fail-closed on
absent/mismatched RUNID). Mode-based, not a file split: the §9
name+dispatch census locks survive untouched. client-handover-writer 9.6
dispatches twice with the FULL PACKAGE both times (LRN-126) + RUNID mint
+ post-run draft cleanup. In-wave smokes PASSED disk-verified: draft
written+sentinel+gitignored; render BLOCKED on wrong RUNID (no phantom
synthesis); render consumed draft + honored skip-write, report grammar
intact. Census §17 (111 pass), make test exit 0.
2026-07-19 22:59:34 +02:00
Bastien Chanot d3d5e3802c Merge feature/model-tiering-w3-tier-moves into develop 2026-07-19 22:46:19 +02:00
Bastien Chanot 5e8bb0c22e feat(routing): W3 tier moves — validator-analyzer opus→sonnet, commit-changer propose=opus override (BDR-077)
validator-analyzer tiered down (deterministic validator-runner + fixed
deduction tables — no deep judgment; supersedes its BDR-076 opus pin).
commit-changer: MODE propose dispatched model="opus" (narrative
reconstruction + capitalize routing = judgment), MODE apply on the sonnet
pin. Typed-pin precedence smoke PASSED: sonnet-pinned verifier dispatched
model="haiku" ran on claude-haiku-4-5 — call-site wins, documented +
now behaviorally proven. Census §11 flip + §16 (106 pass), make test
exit 0.
2026-07-19 22:45:00 +02:00
Bastien Chanot e4d2629c88 Merge feature/model-tiering-w2-conversions into develop 2026-07-19 22:42:39 +02:00
Bastien Chanot 18075a38db feat(agents): W2/S2 doc pipeline two-mode + last inline conversions (BDR-077)
doc-syncer: ONE agent, TWO dispatch modes around the dispatcher's gate —
MODE: audit (model="opus" call-site override, READ-ONLY, drafts + PATCH
PLAN) / MODE: patch (sonnet pin, applies the APPROVED plan, shape oracle
w/ revert-on-fail, emits CHANGE SUMMARY + PATCHED_FILES). Deviation from
plan's 2-file split, per the challenge's own commit-changer mode
precedent: zero text duplication, zero lock moves. Fixes a LATENT DEFECT:
/doc dispatched an agent whose STEP 8 gate could never fire (dispatched
agents cannot ask) — the gate now lives in the dispatcher (DISPATCHER
PROTOCOL section). doc-commit.md consumes the patcher's CHANGE SUMMARY
(the in-thread context now crosses the dispatch boundary, LRN-126).
Consumers rewired: /doc (audit→gate→patch→commit), onboard (audit
report-only, opus), doc-commit steps in bugfix/hotfix/feat/ship-feature/
init-project(5b+10c); scaffolder loses PHASE 6 (README = init 5b's job);
scaffolder + onboarder now DISPATCHED in init-project/onboard (pins live,
was inline on session model). In-wave planted-drift smoke PASSED
end-to-end, disk-verified (audit caught npm-run-dev drift → [MINOR] plan
→ patch applied → summary crossed). Census §14-15 (103 pass), make test
exit 0. Typed plugin-probe dispatch resolution verified post-restart.
2026-07-19 22:28:09 +02:00
Bastien Chanot 74528a6910 feat(agents): W2/S1 plugin split — probe (sonnet) + advisor reasoner (opus) + plugin-gate include (BDR-077)
plugin-advisor keeps its name, becomes the opus REASONER: PHASE 1 bash
extracted to new plugin-probe (sonnet, facts-only PROBE REPORT), PHASE 4
apply + checkpoint hoisted to new lib/plugin-gate.md (main-loop include,
doc-commit.md x6 pattern). Fail-closed: advisor ERRORs on missing report.
4 consumers rewired (plugin-check, onboard, init-project, ship-feature).
In-wave planted-input smoke PASSED: probe report complete w/ fallbacks;
advisor consumed every planted field (monorepo per-package note, fast-libs
ctx7 reco) with zero re-detection; ERROR verdict on absent report.
Census §13 (81 pass). Note: new subagent_type registers next session —
resolution re-check before wave merge.
2026-07-19 20:57:10 +02:00
Bastien Chanot 896d3faaf9 Merge feature/model-tiering-w1-no-inherit into develop 2026-07-19 20:00:37 +02:00
Bastien Chanot 3f7c754239 feat(routing): W1 no-inherit — fable skill-runners, opus review dispatches, dispatch-tier doctrine (BDR-077)
No dispatched agent inherits the session model anymore:
- client-handover-writer's 7 general-purpose skill-runner dispatch sites
  carry model: "fable" (+ normative rule; spike-verified alias — resolves
  claude-fable-5, enum-validated, loud failure, never silent fallback)
- ship-feature STEP 6 + init-project STEP 10 code-review dispatches carry
  model: "opus" (was: inherit — the leak the maps exposed)
- model-gate.md §4: dispatch-tier doctrine (typed = frontmatter pin,
  built-ins = explicit model= at every call site)
- census §12 (66 pass), make test green
2026-07-19 19:51:06 +02:00
Bastien Chanot 1c2d30dbf0 chore(tasks): model-tiering v2 — analysis + challenged plan v3 + TODO reconcile
Plan challenged by 3 blind lenses + 1 confirmation pass (1 BLOCKER closed
by fable-dispatch spike, 6 MAJORs + 8 MINORs fixed by named changes, 0
deferred). TODO: seo-geo-integrity 'UNMERGED' note was stale (92301fe
already in develop) — corrected.
2026-07-19 19:51:06 +02:00
Bastien Chanot 17fbe51aa1 Merge feature/opus-pin-audit-agents into develop 2026-07-19 19:46:10 +02:00
Bastien Chanot 3eaf31ca09 chore(memory): BDR-076 + journal + TODO — opus-pin dispatched judgment agents 2026-07-19 17:38:58 +02:00
Bastien Chanot 354ff2644f feat(agents): pin dispatched judgment agents to opus — Fable = inline reflection only (BDR-076)
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.
2026-07-19 17:38:55 +02:00
Bastien Chanot 9bc6ab7e07 Merge feature/hotfix-challenge-guard into develop 2026-07-18 23:24:04 +02:00
Bastien Chanot 727a41ad71 chore(memory): BDR-075 amendment (hotfix included) + journal — Option B + behavioral smoke 2026-07-18 23:08:53 +02:00
Bastien Chanot 311ea14789 feat(skills): wire hotfix into plan-challenge under a logic-only guard
STEP 1.8 (Option B): skip purely cosmetic fixes (CSS/copy/typo), run the 3-lens
challenge only when the fix touches control flow/behaviour (off-by-one, wrong
operator, behaviour-changing config, execution-altering import); a BLOCKER means
it was never a hotfix -> escalate to /bugfix. 12th orchestrator wired.

- skills/hotfix/SKILL.md          — STEP 1.8 guarded challenge
- lib/tests/plan-challenger.test.sh — lock hotfix into the census (43 assertions)
2026-07-18 23:08:52 +02:00
Bastien Chanot a68f26ca9c Merge feature/plan-challenge-phase into develop 2026-07-17 22:57:40 +02:00
Bastien Chanot d5d1584c1c Merge feature/drop-config-protection into develop 2026-07-17 22:57:10 +02:00
Bastien Chanot 2aa95636ee chore(memory): BDR-074 BDR-075 EVAL-026 LRN-136 — config-protection removal + plan-challenge phase 2026-07-17 22:54:12 +02:00
Bastien Chanot 6bfc0543e5 feat(skills): add 3-way adversarial plan-challenge phase to reflection orchestrators
After a plan/reflection is elaborated and before it executes, three fresh blind
sub-agents (correctness / robustness / simplicity) attack it on the big model;
the main loop RE-THINKS every aspect a BLOCKER lands (a named plan change, or
[deferred]) and re-challenges once if the plan materially changed. Advisory into
each skill's existing human gate — the human stays the decider.

- lib/challenge-plan.md — reusable phase: fail-safe (never fail open),
  severity-driven (any single-lens BLOCKER = must-address), RE-THINK loop
- agents/plan-challenger.md — challenger role (read-only, big-model per BDR-066)
- lib/tests/plan-challenger.test.sh — 41-assertion structure lock
- wired into 11 orchestrators: ship-feature/init-project/feat/bugfix (build-plan),
  onboard/audit-delta/code-clean (proposals), seo/geo/harden/web-validate (fix-bundle)

Hardened by dogfooding: 3 blind challengers reviewed this feature's own v1 plan
and caught 4 BLOCKERs (fail-open, consensus-buries-lone-finding, wrong model
tier vs BDR-066, false on-disk-plan premise) — all fixed here.
2026-07-17 22:51:50 +02:00
Bastien Chanot 0e1b89c71a chore(hooks): remove config-protection edit-block guardrail
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.
2026-07-17 21:56:32 +02:00
Bastien Chanot 2f8dc6be1a Merge release/1.1.0 into main 2026-07-16 13:57:07 +02:00
Bastien Chanot dc4f78b1f0 Merge release/1.0.0 into main 2026-07-16 13:22:29 +02:00
Bastien Chanot 709facfb52 Merge release/4.0.0 into main 2026-06-30 15:33:22 +02:00
Bastien Chanot d3d72fd3ca Merge hotfix/gstack-ignore-gitmodules into main 2026-06-29 13:57:12 +02:00
68 changed files with 2841 additions and 3229 deletions
+7
View File
@@ -208,3 +208,10 @@ rules:
- **Real cause**: two viable-looking paths, both dead. (API KEY) is per-user not per-site (docs), but IS the account identity → one key per client account, exactly what the user feared; non-scoped, no expiry, passed in query string. (OAuth) is the right delegation model (like GSC) but a swamp: Redirect URI rejects ALL local forms (http/https/127.0.0.1 — user-tested); refresh tokens are ROTATED + single-use, self-described non-compliant with OAuth 2.0 → store rewrite every call, AND our parallel seo‖geo dispatch would race the rotation → `invalid_grant` + dead token; undocumented "Could not extract expected anti-forgery token" on refresh, unanswered on MS Q&A; docs contradict themselves on grant_type + token endpoint; no library. MS's own advisor recommends falling back to the API key. - **Real cause**: two viable-looking paths, both dead. (API KEY) is per-user not per-site (docs), but IS the account identity → one key per client account, exactly what the user feared; non-scoped, no expiry, passed in query string. (OAuth) is the right delegation model (like GSC) but a swamp: Redirect URI rejects ALL local forms (http/https/127.0.0.1 — user-tested); refresh tokens are ROTATED + single-use, self-described non-compliant with OAuth 2.0 → store rewrite every call, AND our parallel seo‖geo dispatch would race the rotation → `invalid_grant` + dead token; undocumented "Could not extract expected anti-forgery token" on refresh, unanswered on MS Q&A; docs contradict themselves on grant_type + token endpoint; no library. MS's own advisor recommends falling back to the API key.
- **Verified live**: the Webmaster API itself is ALIVE (`GetUserSites?apikey=INVALID` → HTTP 400 `{"ErrorCode":3,"Message":"InvalidApiKey"}`, 0.4s) — distinct from Bing SEARCH API (retired 2025-08-11). So the block is auth/model, not availability. - **Verified live**: the Webmaster API itself is ALIVE (`GetUserSites?apikey=INVALID` → HTTP 400 `{"ErrorCode":3,"Message":"InvalidApiKey"}`, 0.4s) — distinct from Bing SEARCH API (retired 2025-08-11). So the block is auth/model, not availability.
- **Status**: open/deferred. REVIVAL: a client already on Bing adds the user as Read-Only → test in ~10 min whether one API key sees DELEGATED sites (undocumented, nobody knows). If yes → W2 is cheap+clean (one key, client-owned verification, revocable, read-only, zero OAuth). Value RAISED by [[BDR-071]]: GetUrlLinks is now the only free viable backlink source (first-party only). - **Status**: open/deferred. REVIVAL: a client already on Bing adds the user as Read-Only → test in ~10 min whether one API key sees DELEGATED sites (undocumented, nobody knows). If yes → W2 is cheap+clean (one key, client-owned verification, revocable, read-only, zero OAuth). Value RAISED by [[BDR-071]]: GetUrlLinks is now the only free viable backlink source (first-party only).
## BLK-018 — release-executor finish span blocked by permission classifier (human signal invisible to subagent) — 2026-07-20
- **Friction**: v1.3.1 release — `SPAN: finish` dispatch denied at tool-permission layer: classifier flagged "Merge Without Review" (`gitflow.sh finish` in subagent transcript carries no explicit human merge signal). Executor correctly refused workaround, reported BLOCKED. v1.2.0/v1.3.0 same span passed → classifier behavior change, not skill regression.
- **Real cause**: gitflow doctrine "finish only on explicit human signal" lives in DISPATCHER transcript (user ask + STEP 4 AskUserQuestion go); subagent transcript starts fresh → classifier sees consequential merge with zero authorization evidence. Structural: any human-gated action dispatched to a subagent loses its gate evidence.
- **Solution** (workaround): dispatcher ran `gitflow.sh finish` + tag inline after its own human gate — where the signal is real. Release completed clean (main `648bc6e`, tag v1.3.1).
- **Status**: open. Candidate fixes: (a) quote gate evidence verbatim in span prompt — untested vs classifier; (b) move finish+tag span permanently inline in /release-candidate — keeps prep span dispatched, costs the sonnet pin on ~5 mechanical commands, cheap; (c) permission rule allowing subagent `gitflow.sh finish` — weakens the guard, refused. Decide at next release.
- **Reference**: skill `release-candidate` STEP 5. Pattern adjacent [[LRN-089]] (ambient-state/context assumptions across boundaries). Journal 2026-07-20.
+27
View File
@@ -91,6 +91,7 @@ rules:
| BDR-071 | 2026-07-17 | No viable free backlink source → Off-page axis stays brand-mentions-only (FINAL, not placeholder) | accepted | | BDR-071 | 2026-07-17 | No viable free backlink source → Off-page axis stays brand-mentions-only (FINAL, not placeholder) | accepted |
| BDR-072 | 2026-07-17 | SPA: honest refuse (On-page N/A, not zero), no headless browser (R2 over R1) | accepted | | BDR-072 | 2026-07-17 | SPA: honest refuse (On-page N/A, not zero), no headless browser (R2 over R1) | accepted |
| BDR-073 | 2026-07-17 | Scoring: LLM judges findings+severity, engine does the arithmetic (deterministic /20) | accepted | | BDR-073 | 2026-07-17 | Scoring: LLM judges findings+severity, engine does the arithmetic (deterministic /20) | accepted |
| BDR-080 | 2026-07-21 | Bug routing inverted: /bugfix primary, /investigate explicit-only | accepted |
--- ---
@@ -980,6 +981,7 @@ rules:
- **Why**: user call 2026-07-14 — registries already capture decisions; a stale plan describes a superseded intermediate state and misleads future readers; accumulation pollutes the repo. Precedent: gsc-crux cleanup (8a1fac0, 2026-07-10) did the same — this makes it law, not habit. - **Why**: user call 2026-07-14 — registries already capture decisions; a stale plan describes a superseded intermediate state and misleads future readers; accumulation pollutes the repo. Precedent: gsc-crux cleanup (8a1fac0, 2026-07-10) did the same — this makes it law, not habit.
- **Alternatives rejected**: never-commit (gitignore docs/superpowers) — breaks mid-run: briefs, reviewers, other-machine checkouts need the files; superpowers brainstorming commits the spec by convention. Keep-forever — the drift + pollution complained about. - **Alternatives rejected**: never-commit (gitignore docs/superpowers) — breaks mid-run: briefs, reviewers, other-machine checkouts need the files; superpowers brainstorming commits the spec by convention. Keep-forever — the drift + pollution complained about.
- **Reference**: project CLAUDE.md; cleanup commit this chore; precedent 8a1fac0. Linked [[BDR-064]], [[LRN-124]]. - **Reference**: project CLAUDE.md; cleanup commit this chore; precedent 8a1fac0. Linked [[BDR-064]], [[LRN-124]].
- **Amendment (2026-07-22)**: DELETE side now AUTOMATED — `lib/gitflow.sh` `_gitflow_purge_transient` at `gitflow finish` (feature/bugfix, pre-merge, on HEAD) git-rm's `docs/superpowers/{specs,plans}` + scoped commit → develop TIP clean, feature commits stay reachable (`git show <sha>:…` archive intact). Best-effort: NEVER aborts finish (nothing-tracked no-op / dirty-path skip / commit-fail index+tree restore). Opt-out `GITFLOW_PURGE_TRANSIENT=0`. Retires the manual chore that slipped (655e364). Universal via `~/.claude/lib`→repo symlink (ship-feature STEP 9 + init-project STEP 11 both finish through it). gitignore STILL rejected — unchanged: breaks superpowers' `git add` of the spec (silently skipped, no travel to SDD worktree). `.claude/tasks/{contracts,plans}` kept versioned (user call — durable, referenced by decisions.md). Tests: gitflow-test.sh T17 a-d. [[LRN-138]].
--- ---
@@ -1046,3 +1048,28 @@ rules:
- **Why**: /harden had a real scale (SKILL.md:435), /seo had NONE → every axis felt → two runs over identical code diverged, while /client-handover gates on 17/20. H2 sharpened it: once drift reports real change, a self-moving score is visibly noise. Same principle as engine-side cannibalisation grouping — never hand a model 1000 rows to add. - **Why**: /harden had a real scale (SKILL.md:435), /seo had NONE → every axis felt → two runs over identical code diverged, while /client-handover gates on 17/20. H2 sharpened it: once drift reports real change, a self-moving score is visibly noise. Same principle as engine-side cannibalisation grouping — never hand a model 1000 rows to add.
- **Makes computable (was prose)**: "N/A is not a zero" (R2 on-page, I1 off-page) → axis excluded + weights renormalised, verified all-20 with 2 N/A → global 20.0. Prevalence: affected/sampled shift severity ONE step (≥50% escalate, single de-escalate). - **Makes computable (was prose)**: "N/A is not a zero" (R2 on-page, I1 off-page) → axis excluded + weights renormalised, verified all-20 with 2 N/A → global 20.0. Prevalence: affected/sampled shift severity ONE step (≥50% escalate, single de-escalate).
- **Files**: lib/seo-data/score.py (4818c61). - **Files**: lib/seo-data/score.py (4818c61).
### BDR-074 — Remove config-protection edit-block guardrail [accepted] (2026-07-17)
Deleted hooks/config-protection.sh + its settings.json PreToolUse registration + lib/tests/config-protection.test.sh. Hook blocked model Edit/Write on quality-gate files (settings.json, gitflow.sh, .githooks, doctor.sh, hooks, lib/tests, lint) via one-shot .claude/.config-edit-ok sentinel. Removed per user req — friction editing own config > guardrail value; user = human operator. Residual: gitflow pre-commit guard + Gitea branch protection still block direct code commits main/develop; only edit-time block gone. Alts rejected: warn-only (exit0+log), targeted relaxation. Supersedes any prior config-protection decision.
### BDR-075 — Framework-wide 3-way adversarial plan-challenge phase [accepted] (2026-07-17)
After a plan/reflection elaborated + before execution, 3 fresh blind sub-agents (correctness/robustness/simplicity) attack it; main loop RE-THINKS every aspect a BLOCKER lands (named change or [deferred]) + re-challenges once if plan materially changed. Reusable lib/challenge-plan.md + new agents/plan-challenger.md (read-only, big-model per [[BDR-066]] — audit judgment, NOT sonnet). Fail-safe (never fail open: mute→retry→escalate), severity-driven (any single-lens BLOCKER=must-address, NOT consensus — lenses orthogonal), advisory into existing human gate. KIND tunes lenses: build-plan/proposals/fix-bundle. Wired 11 orchestrators: ship-feature/init-project/feat/bugfix + onboard/audit-delta/code-clean + seo/geo/harden/web-validate. Excluded (no real plan): hotfix/tour/analyze/client-handover/release-candidate/spec. Audit found 0 repo-owned plan-challengers pre-existing (only vendored gstack autoplan, sequential+unwired). See [[EVAL-026]].
### BDR-075 amendment (2026-07-18) — hotfix INCLUDED via logic-only guard
Supersedes the "Excluded: hotfix" clause of [[BDR-075]]. hotfix now wired (STEP 1.8, Option B): GUARD skips purely cosmetic fixes (CSS/copy/typo), fires the 3-lens challenge ONLY when the fix touches control flow/behaviour (off-by-one, wrong operator, behaviour-changing config, execution-altering import); a BLOCKER → escalate to /bugfix (its STEP 3b runs the full phase). 12 orchestrators wired. Still excluded (no forward plan): tour/analyze/client-handover/release-candidate/spec. Per user (Option B). Branch feature/hotfix-challenge-guard, unmerged.
### BDR-076 — Dispatched judgment agents pinned OPUS; session model = orchestration + inline reflection ONLY [accepted] (2026-07-19)
Reverses the BDR-066 rejected alternative "opus pins on audit agents (session-independent)". Context changed: session default now Fable (Mythos tier, /model 2026-07-19) — inherit meant every dispatched audit/challenge burned Fable quota, exactly the waste BDR-066 killed for executors. New rule: Fable does ONLY main-loop orchestration + reflection (brainstorm, plan, contract, synthesis, gates); EVERY dispatched subagent pinned. Pinned `model: opus` (big tier, session-independent; NEVER sonnet — silent audit downgrade, the thing old §F5 guarded): analyzer, plan-challenger, seo-analyzer, geo-analyzer, validator-analyzer + onboard's 6 general-purpose audit dispatches (`model="opus"`) + tour Phase B. NOT pinned (justified deviation from approved "7 agents"): interviewer + client-handover-writer — inline-load only, never dispatched → frontmatter pin inert + misleading (BDR-066 wave-4 precedent: its inert opus pin was dropped); they ARE the main loop = Fable per the rule. Explore built-in stays inherit (wave-3 decision conserved: no owned prompt, search feeds inline reflection). Local session pin `opus-4-8[1m]` dropped from `.claude/settings.local.json` (gitignored) — Fable default from settings.json now applies in this repo too. model-gate.md unchanged (still guards inline reflection, Fable-or-Opus = big). Census: model-routing.test.sh §3 flip + §11 (61 pass), loops-light 35 pass, full `make test` green. User directives via gate: "Opus partout" + "Supprimer le pin". Branch feature/opus-pin-audit-agents, unmerged.
### BDR-077 — Model-tiering v2: 4-tier explicit routing, mode-based splits, no-inherit dispatches [accepted] (2026-07-19)
Supersedes BDR-076 scope + amends BDR-066. Doctrine: session model (Fable) = main-loop reflection/orchestration/planning/logic ONLY; main-loop retention criteria = interactive | conversation-context access | orchestration decision | dispatch overhead > step cost. NOTHING dispatched inherits: typed agents = frontmatter pin, built-ins = `model=` at every call site (`fable` for skill-runner reflection children, else complexity tier). Spike+smoke proven: `model:"fable"` resolves claude-fable-5 (enum-validated, loud fail, no silent fallback); call-site override BEATS a typed pin (sonnet-pinned verifier ran haiku). Fail-safe pin rule: mixed-mode agents keep the HIGH tier as pin, overrides go DOWN — forgotten override over-tiers (cost), never downgrades judgment. Mode-based splits (commit-changer precedent generalized; file splits rejected): doc-syncer audit(opus)/patch(sonnet) — ALSO fixed a latent defect: /doc dispatched an agent whose STEP 8 interactive gate could never fire; gates hoisted to a DISPATCHER PROTOCOL section; handover-doc-writer synthesize(opus)/render(sonnet) via run-scoped `.audit/handover-draft-<RUNID>.md` + DRAFT COMPLETE sentinel; seo/geo collect(sonnet)/judge(OPUS PIN)/template(sonnet) via `.audit/*-signals-<RUNID>.md` + COLLECTION COMPLETE + fail-closed judge + dispatcher ERROR contract (mute/ERROR judge NEVER carried into templating; retry once, escalate). File split only for a genuinely new role: plugin-probe (sonnet, facts-only) + plugin-advisor repinned opus reasoner (fail-closed on missing PROBE REPORT) + lib/plugin-gate.md (checkpoint + apply gate, doc-commit ×N include pattern). Inline→dispatch conversions: scaffolder, onboarder, doc-commit steps ×5 flows — their sonnet pins were INERT since creation, now live; CHANGE SUMMARY crosses the doc dispatch into doc-commit (LRN-126 wire). Tier moves: validator-analyzer opus→sonnet (deterministic runner); commit-changer propose=opus/apply=pin; ship-feature/init-project code-review dispatches = opus explicit (WAS an inherit leak); client-handover-writer's 7 skill-runners = model:"fable". Every wave shipped an IN-WAVE planted-input smoke as its merge gate — all PASSED disk-verified. Census §12-18 (125 pass; one vacuous line-wrapped lock self-caught = LRN-093 live). 6 waves, branches feature/model-tiering-w1..w6, merged on user standing signal. Plan: challenged 3 blind lenses + 1 confirmation (1 BLOCKER closed by spike, 8 MAJORs + 8 MINORs closed by named changes, 0 deferred). Refs: `.claude/tasks/plans/2026-07-19-model-tiering-v2-{analysis,plan}.md`.
### BDR-078 — ctx7 coverage: central fast-libs list + once-per-session reminder hook; every code path covered [accepted] (2026-07-20)
Refines BDR-053 (single surface). Audit 2026-07-20: coverage PARTIAL — find-docs fired on user doc-questions only; ship-feature 0c / init-project 5c pre-fetched; /feat //bugfix executors + ad-hoc coding NEVER consulted ctx7; fast-libs list hardcoded 3× (drift risk). 4 closures shipped: (a) find-docs description += BEFORE-writing-code trigger (fast-moving lib, even without doc question, unless fresh cache) + cache-first rule in body (tee fetched docs to .ctx7-cache/); (b) feater+bugfixer briefs += fast-lib docs rule — read fresh `.ctx7-cache/<lib>*.md`, else `npx ctx7@latest` fetch max 2 topics, else `ctx7 cache miss: <lib>` in NOTES + proceed (executors lack Skill tool → Bash path); (c) hooks/ctx7-reminder.sh UserPromptSubmit — ONE fire/session (sentinel on session_id), only when project manifest carries fast-libs; reports cache state; skips <task-notification> turns; always exit 0; (d) lib/fast-libs.sh = SINGLE SOURCE (detect / cache-status verbs, JS package.json anchored full-key match + Python requirements/pyproject, 7-day freshness, LC_ALL=C sort locale-independent) consumed by hook + 3 pipeline skills + 2 briefs. 2nd session surface DELIBERATE, not a BDR-053 reversal: 053 killed a 490-tok ALWAYS-ON rule duplicate; hook costs ~0 quiet, 1 line once when fast-libs present. Alternatives rejected: PreToolUse Edit/Write gate (fires per-edit = noise); description-only fix (probabilistic, executors unreachable). Tests: lib/tests/fast-libs.test.sh 11 checks (anchored/near-miss/py/none, cache fresh/stale/missing, hook fire/sentinel/quiet×2); shellcheck + full make test green. Branch feature/ctx7-coverage, unmerged (human gate).
Amendment (same session): skills/find-docs = machine-owned dist (gitignored, ctx7 regenerates on fresh clone) → durable copy of closure (a) lives in install-plugins.sh STEP ctx7 (idempotent grep-guarded python patch, fixture-verified); live SKILL.md carries the same edit uncommitted by design.
### BDR-079 — profile `set` symmetric on managed externals + MCPs [accepted] (2026-07-20)
Audit (user ask "profile toggles externals both ways?"): ASYMMETRIC. Enable side OK — gstack on-demand from submodule when pack off (shared `skills-disabled/gstack__*` convention with toggle-external.sh, interoperable), externals restored from parked, magic delegated to toggle-external. Disable side MISSING: `cmd_set` trimmed only gstack + MANAGED_PLUGINS → `set backend` left emil/frontend-design/design-motion/impeccable active + magic registered; SKILL.md claimed both-ways toggle (true only at enable). Shipped: (1) `MANAGED_EXTERNALS` (emil-design-eng, frontend-design, design-motion-principles, impeccable = exact union of profile `external` usage; darwin-skill excluded — not task-type-driven) + `MANAGED_MCPS` (magic) allowlists, same doctrine as MANAGED_PLUGINS; (2) cmd_set refactored to 4 trim helpers (`disable_{gstack,plugins,externals,mcps}_not_in`) — symmetric, nothing outside allowlists ever auto-touched; (3) enable_skill external += from-source fallback (`ln -sf skills-external/<name>`, mirrors toggle-external) — closes the "missing symlink" warn; (4) stale usage() NOTE ("NOT toggled automatically") + SKILL.md fixed. Hermetic test profile-set-managed.test.sh 16 checks: fixture repo (both *_REPO_OVERRIDE), fake `claude` shim on PATH logging calls + flat-file MCP registry — gstack on-demand, external from-source, park/restore round-trip, magic add/remove calls, non-managed untouched. shellcheck + make test green. Branch feature/profile-managed-externals, unmerged (human gate).
### BDR-080 — bug routing inverted: /bugfix primary, /investigate explicit-only [accepted] (2026-07-21)
Old routing "Bug → investigate (bugfix if gstack off)" + gstack ON by default → every bug took path bypassing own quality pipeline (gitflow aiguillage, contract, fresh verifier + security gates, doc-sync, `.claude/memory` registries) — /bugfix relegated to near-never fallback. Skill comparison: same core doctrine (root-cause iron law, hypothesis loop, regression test, 3-strike stop, >5-files alert) but incompatible wrappers — investigate monolithic (same context investigates+fixes+verifies, ~1075-line SKILL.md w/ gstack preamble/telemetry/onboarding, capitalizes to `~/.gstack` learnings.jsonl framework never reads at session start); bugfix orchestrator (reflection inline, sonnet bugfixer executor, fresh gates — BDR-066, LRN-083). Composition rejected: skills superpose in context, don't compose — invoking investigate inside bugfix = two full workflows, two completion protocols, two memory systems loaded at once. Decision: CLAUDE.global.md routing line inverted — bugfix primary; investigate ONLY on explicit ask for gstack ecosystem (cross-project learnings, /freeze scope lock, long no-commit investigation). Alternatives rejected: keep investigate primary (bypasses framework), embed investigate inside bugfix (context conflict, dual memory). Known drift noted at write time: Index table rows BDR-074..079 missing (pre-existing, /prune-memory scope).
+3
View File
@@ -248,3 +248,6 @@ rules:
- **method**: each verifiable claim confronted DURING execution with a primary source or a live test — CrUX API metric list, web.dev, Search Console API reference, HEAD on data.commoncrawl.org, real curl on 2 live sites (zenquality Astro, lavageangels356 native PHP), 2 real repos. - **method**: each verifiable claim confronted DURING execution with a primary source or a live test — CrUX API metric list, web.dev, Search Console API reference, HEAD on data.commoncrawl.org, real curl on 2 live sites (zenquality Astro, lavageangels356 native PHP), 2 real repos.
- **anomalies**: 7/7 of the verifiable claims were false or overstated (VSI exists / Off-page zero-data / stats drive weights / GSC Links API / SPA §0 flag / Twitter 403 / Common Crawl viable). 6 plan corrections mid-execution: I1 over-correction, I6 wrong framing, W1 wrong shape (verb vs extend), C1a false premise (grep already skips gitignore), C1b needless guard, B1 non-viable at 17.3 GB. The REAL corrected every time; re-reading the spec never did. - **anomalies**: 7/7 of the verifiable claims were false or overstated (VSI exists / Off-page zero-data / stats drive weights / GSC Links API / SPA §0 flag / Twitter 403 / Common Crawl viable). 6 plan corrections mid-execution: I1 over-correction, I6 wrong framing, W1 wrong shape (verb vs extend), C1a false premise (grep already skips gitignore), C1b needless guard, B1 non-viable at 17.3 GB. The REAL corrected every time; re-reading the spec never did.
- **action**: keep — see [[LRN-132]]. 4 features killed at measurement (B1/B2/B3 + W2 deferred) beat 4 false-signal features. The most trustworthy output of the session was the code NOT written. Method that worked: show/measure the real artifact before deciding, mirroring [[LRN-074]]'s watch-the-RED discipline applied to a plan. - **action**: keep — see [[LRN-132]]. 4 features killed at measurement (B1/B2/B3 + W2 deferred) beat 4 false-signal features. The most trustworthy output of the session was the code NOT written. Method that worked: show/measure the real artifact before deciding, mirroring [[LRN-074]]'s watch-the-RED discipline applied to a plan.
### EVAL-026 — 3-way plan challenge caught 4 BLOCKERs dogfooding own plan (2026-07-17)
Dogfood: 3 blind lenses attacked the v1 plan for the plan-challenge feature itself. Verdicts CONCERNS(4)/FATAL(6)/FATAL(4). Caught 4 distinct BLOCKERs a single pass would blend: (1) v1 unbuildable — targeted init-project (inline-load, no dispatch) + false "plan on disk" premise for feat/bugfix (only contract persists); (2) failed-open silently dropping a lens while claiming "challenged" (inverts verify-secure-loop "a mute verifier is NEVER a PASS"); (3) consensus-weighting buries lone L2 security finding (lenses orthogonal); (4) sonnet challengers violate [[BDR-066]] (audit judgment=big model). Synthesis REJECTED 1 false positive (allowed-tools-blocks-dispatch — ship-feature has same frontmatter + dispatches fine). Each lens found a DIFFERENT class of flaw → evidence 3-independent > 1-multilens. Action: hardened v2 (severity-driven + fail-safe + re-think loop) shipped. Method validated itself before build.
+25
View File
@@ -402,3 +402,28 @@ rules:
- seo/geo parity vs github.com/AgriciDaniel/claude-seo (11.5k★, MIT): full 20-point plan built from a 3-subagent inventory, then executed. Verdict cherry-pick-never-install ([[BDR-070]]). 21 commits: Phase 1 (I1-I8 integrity, markdown specs) MERGED to develop (02c7a6f, 8 commits); Phases 2-7 on bugfix/seo-geo-integrity UNMERGED (13 commits, human gate). `fetch.sh` 5→11 verbs (richresults via inspect, sitemap, rendercheck, linkgraph, cannibal, drift, score); seo-data test suite 85→167 pass, 0 fail. Dogfooded on 2 live sites (zenquality Astro + lavageangels356 native PHP) — the second caught 2 bugs Astro hid (image:loc counted as page, flat-URL family heuristic). - seo/geo parity vs github.com/AgriciDaniel/claude-seo (11.5k★, MIT): full 20-point plan built from a 3-subagent inventory, then executed. Verdict cherry-pick-never-install ([[BDR-070]]). 21 commits: Phase 1 (I1-I8 integrity, markdown specs) MERGED to develop (02c7a6f, 8 commits); Phases 2-7 on bugfix/seo-geo-integrity UNMERGED (13 commits, human gate). `fetch.sh` 5→11 verbs (richresults via inspect, sitemap, rendercheck, linkgraph, cannibal, drift, score); seo-data test suite 85→167 pass, 0 fail. Dogfooded on 2 live sites (zenquality Astro + lavageangels356 native PHP) — the second caught 2 bugs Astro hid (image:loc counted as page, flat-URL family heuristic).
- 4 features KILLED at measurement, not built: B1/B2 (Common Crawl edges = 17.3 GB, ref impl reads 2.9% and calls it a profile — [[BDR-071]]), B3 (GSC Links API doesn't exist), W2 (Bing OAuth swamp — [[BLK-017]]). 30/70 similarity refused (needs content extraction), Playwright refused (R2 [[BDR-072]]), defusedxml refused (DTD-reject keeps stdlib-only). The most trustworthy output was the code NOT written ([[EVAL-025]]). - 4 features KILLED at measurement, not built: B1/B2 (Common Crawl edges = 17.3 GB, ref impl reads 2.9% and calls it a profile — [[BDR-071]]), B3 (GSC Links API doesn't exist), W2 (Bing OAuth swamp — [[BLK-017]]). 30/70 similarity refused (needs content extraction), Playwright refused (R2 [[BDR-072]]), defusedxml refused (DTD-reject keeps stdlib-only). The most trustworthy output was the code NOT written ([[EVAL-025]]).
- BDR-070/071/072/073 + LRN-131/132/133 + BLK-017 + EVAL-025 capitalized; checked 14 TODO done (I1-I5,W1,W3,C1-C3,B3,R2,H1,H2), W2+R1 left unchecked (deferred/rejected). 2 learnings dropped as dup of [[LRN-074]] (grep/find gitignore + detector-proof). Red thread [[LRN-133]]: an omission must stay legible. Verification discipline [[LRN-131]]/[[LRN-132]]: WebSearch ≠ verification, subagent summary = claim not fact (7 disproven, 3 self-reproduced). - BDR-070/071/072/073 + LRN-131/132/133 + BLK-017 + EVAL-025 capitalized; checked 14 TODO done (I1-I5,W1,W3,C1-C3,B3,R2,H1,H2), W2+R1 left unchecked (deferred/rejected). 2 learnings dropped as dup of [[LRN-074]] (grep/find gitignore + detector-proof). Red thread [[LRN-133]]: an omission must stay legible. Verification discipline [[LRN-131]]/[[LRN-132]]: WebSearch ≠ verification, subagent summary = claim not fact (7 disproven, 3 self-reproduced).
- Removed config-protection edit-block guardrail (full removal, user req) → feature/drop-config-protection (0e1b89c). Residual gitflow+Gitea guards only. [[BDR-074]] [[LRN-136]].
- Built framework-wide 3-way plan-challenge phase → feature/plan-challenge-phase (6bfc054): lib/challenge-plan.md + agents/plan-challenger.md + 41-assertion lock, wired into 11 reflection orchestrators (build-plan/proposals/fix-bundle), excluded 6 no-plan skills. Full suite 16/16. [[BDR-075]].
- Dogfooded the challenge on its own v1 plan: 3 blind lenses caught 4 BLOCKERs + rejected 1 false positive → hardened v2 shipped [[EVAL-026]]. Both branches finished into develop on user signal, NOT pushed.
## 2026-07-18
- hotfix wired into plan-challenge via Option B (STEP 1.8 logic-only guard): skip cosmetic, fire on logic, BLOCKER→/bugfix. 12th orchestrator. structure lock 43/43, suite 15/15. [[BDR-075]] hotfix-exclusion superseded (see amendment). feature/hotfix-challenge-guard, UNMERGED (user: commit only).
- Behavioral smoke of the shipped mechanism: 3 blind plan-challenger dispatches on a planted-flaw plan → correctness FATAL(4), robustness FATAL(6), simplicity CONCERNS(1). Each lens caught ITS planted flaw + stayed in-lens. Live-validated severity-driven (SQL-injection BLOCKER raised by robustness ALONE — consensus-weighting would've buried it) + orthogonality. Confirms [[EVAL-026]]/[[BDR-075]] design.
## 2026-07-19
- BDR-076: dispatched judgment agents pinned opus (analyzer, plan-challenger, seo/geo/validator-analyzer + 6 onboard general-purpose dispatches); Fable now = inline orchestration/reflection only. interviewer + client-handover-writer left unpinned (inline-load, pin inert). Local opus-4-8 session pin dropped from settings.local.json. Census §11 added (61 pass), loops-light 35, make test green. feature/opus-pin-audit-agents, UNMERGED.
- BDR-077 model-tiering v2 SHIPPED: 6 waves (W0 baseline merge → W1 no-inherit+fable skill-runners → W2 plugin split + doc two-mode + inert-pin conversions → W3 tier moves → W4 handover two-mode → W5 seo/geo 3-mode pipelines → W6 doctrine sweep). Plan challenged 4 passes (1 BLOCKER closed by fable spike). Per-wave planted-input smokes disk-verified. Census 125/0, make test green throughout. [[BDR-077]] [[LRN-137]].
## 2026-07-20
- ctx7 coverage audit (user ask "ctx7 appelé à chaque techno ?") → verdict PARTIAL. 4 gaps: find-docs question-only, /feat //bugfix executors blind, ad-hoc coding uncovered, fast-libs hardcoded 3×. All 4 closed → BDR-078 (fast-libs.sh single source + ctx7-reminder hook + description trigger + executor-brief rule). fast-libs test 11/0, make test + review-guards green. feature/ctx7-coverage, UNMERGED.
- v1.2.0 cut + pushed (release-candidate flow: prep/finish via release-executor, tag on main 51b6572). CHANGELOG backfilled at prep: 10 entries added to Unreleased (plan-challenge, seo-data verbs, model-tiering v2, integrity pass, safe_fetch/url-guard) — was ctx7-only. /doc full post-release: README model-routing table v1→v2 reframe + ctx7 two-surface wording, chore/doc-sync-v1.2.0 merged. All pushed on explicit go.
- profile↔toggle-external audit (user) → enable side already symmetric (gstack on-demand LIVE), disable side missing → BDR-079: MANAGED_EXTERNALS+MANAGED_MCPS trim at set, external from-source fallback, 16-check hermetic test (claude shim). feature/profile-managed-externals, UNMERGED.
- README rebuilt: short pitch (what/how/why) top, old content → reference manual below separator. Dedup title/overview/install block, hardcoded version dropped from footer (staleness risk). chore/readme-v2 merged → develop, pushed.
- v1.3.1 cut + pushed (docs-only: README rebuild). prep span via release-executor OK; finish span BLOCKED by permission classifier on subagent (no human signal in its transcript) → ran inline after both gates. [[BLK-018]].
## 2026-07-21
- Skill audit (user ask "pourquoi pas investigate dans bugfix ?") → same core doctrine, incompatible wrappers: investigate = monolithic gstack (own memory ~/.gstack, no gitflow/gates, ~1075-line preamble), bugfix = orchestrator (contract, fresh verifier+security gates, registries). Routing inverted in CLAUDE.global.md: bugfix primary, investigate explicit-only → BDR-080. chore/skill-routing-bugfix, UNMERGED.
## 2026-07-22
- User: auto-gitignore+delete transient pipeline artifacts in all projects. Investigation reframed the ask — gitignore = WRONG tool (files read from disk during run; would break superpowers SDD `git add` of spec). BDR-065 already rejected gitignore + its DELETE side was doctrine-only (no code, manual chore slipped once — 655e364). User picks (2 recommended): keep committed-during-run + AUTOMATE delete; keep `.claude/tasks/{contracts,plans}` versioned.
- Built `lib/gitflow.sh` `_gitflow_purge_transient` at finish (feature/bugfix, pre-merge, best-effort never-abort, opt-out `GITFLOW_PURGE_TRANSIENT=0`) + `purge-transient` CLI verb. Universal via `~/.claude/lib`→repo symlink. gitflow-test T17 a-d (10 checks, `--full-history` recovery), shellcheck clean, make test exit 0. BDR-065 amendment + [[LRN-138]]. feature/gitflow-auto-purge-transient.
+16
View File
@@ -1339,3 +1339,19 @@ rules:
refuse it EVERYWHERE, not just where you look first. A fresh adversarial refuse it EVERYWHERE, not just where you look first. A fresh adversarial
reviewer attacking diff A routinely surfaces a real hole in already-shipped reviewer attacking diff A routinely surfaces a real hole in already-shipped
code B — see [[EVAL-020]]. code B — see [[EVAL-020]].
### LRN-136 — config-protection live state follows checked-out branch's symlinked settings.json (2026-07-17)
~/.claude/settings.json is a SYMLINK to the repo settings.json; Claude Code hot-reloads settings on change → the config-protection PreToolUse hook's active/inactive state tracks the CURRENT branch's settings.json. On feature/drop-config-protection (hook deregistered) a protected edit passed silently, sentinel unconsumed; after gitflow-switch to a branch off develop (hook still registered) the SAME class of edit was blocked. Apply: a change that removes a settings-registered hook is live only on that branch until merged; use the one-shot sentinel for protected edits on any branch that still registers it. ([[BDR-074]] context.)
## LRN-137 — mode-based re-tiering beats file splits for mixed-tier agents
- **pattern**: three planned agent splits (doc-syncer, handover-doc-writer, seo/geo analyzers) shipped as MODES + per-dispatch `model=` instead of new files; only plugin-probe justified a real new file (genuinely new role, no shared body).
- **why**: a file split severs implicit data paths (LRN-126), relocates body-text test locks (seo-data fetch-wiring), breaks name/dispatch-string census locks, duplicates templates. A mode split keeps ALL locks and text in place; the dispatcher's gate sits BETWEEN mode dispatches; call-site `model=` precedence over the frontmatter pin is spike-proven (sonnet-pinned verifier ran haiku on override).
- **fail-safe pin rule**: keep the HIGHEST tier as the frontmatter pin and override DOWN at call sites — a forgotten override then over-tiers (costs money) instead of silently downgrading judgment (costs correctness).
- **future application**: before splitting any agent across model tiers, try MODE + `model=` first; create a new agent file only for a genuinely new role. Run-scoped `.audit/<name>-<RUNID>` files + completeness sentinel + fail-closed consumer for any cross-dispatch artifact.
- **cousin**: [[LRN-125]] [[LRN-126]] [[BDR-077]].
## LRN-138 — gitignore ≠ delete for run-time artifacts read from disk (2026-07-22)
- **pattern**: gitignore is the WRONG tool for an artifact a pipeline READS FROM DISK during a run — it blocks the commit but leaves the file (cleans nothing) AND breaks git-travel flows (superpowers commits the spec via `git add` so it reaches the SDD worktree; a gitignored path is silently skipped w/o `-f`). Right tool = commit-during-run + AUTO-DELETE at the integration boundary (`gitflow finish`, pre-merge, on the working branch → history keeps the archive, develop tip clean).
- **context**: user asked to gitignore transient planning artifacts (`docs/superpowers/{specs,plans}`, `.claude/tasks/{contracts,plans}`) to stop them merging. BDR-065 had already REJECTED gitignore for docs/superpowers on the git-travel ground; the real gap was the DELETE side never being coded (doctrine-only manual chore, slipped once — 655e364). Built `_gitflow_purge_transient`.
- **future application**: "don't merge transient X" → ask: does the run read X from disk? does X travel via git (worktree, foreign checkout)? Yes → auto-purge at finish, not gitignore. Scoped commit `-- <paths>` avoids sweeping a dirty index; `git diff --quiet HEAD -- paths` precheck makes `git rm` all-or-nothing safe; keep the purge best-effort so cleanup NEVER blocks a merge. Prove archive-reachability with `git log --full-history` / `git show <sha>:path` — plain `git log -- path` prunes the purged add-commit via history simplification (bit me writing T17).
- **link**: [[BDR-065]].
+121 -24
View File
@@ -1,13 +1,108 @@
# TODO # TODO
## 2026-07-17 — STATUS seo/geo parity (branch bugfix/seo-geo-integrity, 10 commits, UNMERGED) ## 2026-07-22 — auto-purge transient superpowers artifacts at finish (feature/gitflow-auto-purge-transient)
User: transient planning artifacts (`docs/superpowers/{specs,plans}`) leak into
develop; BDR-065 "post-merge cleanup" is DOCTRINE ONLY (no code) — manual chore,
already missed once (655e364). Decision (user 2026-07-22, 2 recommended picks):
keep committed-during-run (SDD worktree + reviewers read them), AUTOMATE the
delete at `gitflow finish`. NO gitignore (would break superpowers' `git add` of
the spec → no travel to SDD worktree). `.claude/tasks/{contracts,plans}` stay
versioned (durable, referenced by decisions.md e.g. BDR-076). Universal via the
`~/.claude/lib` → repo `lib` symlink: every project's finish gets it.
- [x] lib/gitflow.sh: `_gitflow_purge_transient` (clean-precheck → git rm →
scoped commit `-- paths`; best-effort, NEVER aborts finish; opt-out
`GITFLOW_PURGE_TRANSIENT=0`) wired into finish `feature|bugfix` pre-merge;
`purge-transient` CLI verb.
- [x] lib/gitflow-test.sh T17 a/b/c/d (purge+recover-from-history via
--full-history+`git show`, no-op when absent, opt-out keeps, chore scope).
Also fixed 2 pre-existing SC2034 warnings (T16 gl_out/noleaks_out).
- [x] Gate: shellcheck lib/*.sh CLEAN + `make test` exit 0 (gitflow 106/0, full
suite green). Universal via ~/.claude/lib → repo lib symlink (verified).
- [x] CLAUDE.md §Transient planning artifacts: → "AUTO-PURGED by gitflow finish".
- [ ] Capitalize: BDR-065 amendment (delete side now automated) + LRN — pending user OK.
## 2026-07-20 — pending merge gates (reconcile)
- [x] merge feature/profile-managed-externals → develop (BDR-079 profile
symmetry + /doc clean pass: README/USAGE/ARCHITECTURE.md) — 37c79f0
- [x] merge chore/purge-transient-docs → develop (docs/ transient purge
655e364 + reconcile e75ea79) — reaches main at next release
- [ ] Makefile help text: profiles 5/10 listed (:57) + test glob missing
run-*.sh (:31) — 2-line hotfix (flagged by /doc audit)
## 2026-07-20 — profile ↔ toggle-external symmetry (feature/profile-managed-externals, BDR-079)
Audit verdict: gstack on-demand + design enable already work; DISABLE side
missing — `set backend` leaves emil/frontend-design/design-motion/impeccable
active + magic registered. Doc claims auto-toggle both ways (only enable true).
- [x] profile.sh: `MANAGED_EXTERNALS` (emil-design-eng, frontend-design,
design-motion-principles, impeccable — union of profile usage) +
`MANAGED_MCPS` (magic) allowlists; cmd_set refactored to 4 trim
helpers (disable_{gstack,plugins,externals,mcps}_not_in).
- [x] profile.sh enable_skill external: from-source fallback
(`ln -sf skills-external/<name>`) mirroring toggle-external.
- [x] Texts: cmd_set info line, usage() NOTE (stale "NOT toggled
automatically"), header; skills/profile/SKILL.md Mechanism+tradeoffs.
- [x] Hermetic test lib/tests/profile-set-managed.test.sh — 16/0: gstack
on-demand, external from-source, park/restore round-trip, magic
add/remove via claude shim, non-managed untouched.
- [x] Gate: shellcheck OK + make test exit 0 (review-guards 5/0). BDR-079 +
journal + CHANGELOG done. Merged 37c79f0 (2026-07-20).
## 2026-07-20 — ctx7 coverage extension (feature/ctx7-coverage, BDR-078)
Close the 4 gaps from the ctx7 coverage audit: /feat //bugfix + ad-hoc coding
never consult ctx7; fast-libs list hardcoded 3×; zero deterministic backstop.
- [x] (d) `lib/fast-libs.sh` — single source of truth: `detect` +
`cache-status` verbs; JS (package.json exact/scoped keys) + Python;
7-day cache freshness. LC_ALL=C sort (locale-independent order).
- [x] (c) `hooks/ctx7-reminder.sh` — UserPromptSubmit, once-per-session
sentinel, fires only when fast-libs detected; settings.json
registration (2nd ctx7 surface, deliberate refinement of BDR-053).
- [x] (a) find-docs description — before-writing-code trigger (fast-moving
libs, even without a doc question) + cache-first rule in body.
- [x] (b) feater.md + bugfixer.md — fast-lib docs rule (read fresh cache,
else ctx7 fetch max 2 topics, else NOTES cache miss + proceed).
- [x] consumers → lib: ship-feature STEP 0c, init-project STEP 5c, onboard
STEP 3.5 detection blocks point at fast-libs.sh.
- [x] `lib/tests/fast-libs.test.sh` (lib verbs + hook fire/sentinel/quiet)
— 11/0, auto-discovered by the make test glob.
- [x] Gate: shellcheck + make test green (review-guards 5/0). BDR-078 +
journal + CHANGELOG done. Merged 8ee7d19, shipped v1.2.0.
## 2026-07-19 — Opus-pin dispatched judgment agents (branch feature/opus-pin-audit-agents)
Goal: session model (Fable) = orchestration + inline reflection ONLY.
Every DISPATCHED subagent pinned. Reverses BDR-066 "opus pins rejected"
carve-out (context changed: session now Fable → inherit burns Fable quota
on audits). User approved: opus for judgment agents, drop local opus pin.
- [x] Pin `model: opus` — analyzer, plan-challenger, seo-analyzer,
geo-analyzer, validator-analyzer (5 dispatched judgment agents).
NOT interviewer / client-handover-writer (inline-load only → pin
inert; they ARE the main loop = Fable by design).
- [x] `lib/challenge-plan.md` — rewrite MODEL note (was "do NOT pin").
- [x] `agents/plan-challenger.md` — rewrite ORCHESTRATOR PROTOCOL model note.
- [x] `skills/onboard/SKILL.md` — add `model="opus"` to the 6
general-purpose audit dispatches + table/description text.
- [x] `skills/tour/SKILL.md` Phase B — text: analyzer opus-pinned /
general-purpose with model="opus".
- [x] `skills/client-handover/SKILL.md` — text: pipeline inline on
SESSION model (writer inline-loaded, not dispatched).
- [x] `lib/tests/model-routing.test.sh` — flip §F5 fm_lacks → has
'model: opus' (5 agents), keep fm_lacks on interviewer +
client-handover-writer, update comments (BDR-076).
- [x] `.claude/settings.local.json` — drop `"model": "opus-4-8[1m]"`
(local, gitignored; Fable default from settings.json applies).
- [x] Tests: model-routing + loops-light + shellcheck + make test.
- [x] Memory: BDR-076 append + journal line. Commit (feat + chore);
merged 17fbe51, shipped v1.2.0 (reconcile 2026-07-20).
## 2026-07-17 — STATUS seo/geo parity (branch bugfix/seo-geo-integrity — MERGED to develop, 92301fe; "UNMERGED" note was stale, corrected 2026-07-19 W0)
PHASE 1 — integrity: **DONE 7/7**. I3 8b0c98c · I1 57c67f2 · I2 4ea2fb8 · PHASE 1 — integrity: **DONE 7/7**. I3 8b0c98c · I1 57c67f2 · I2 4ea2fb8 ·
I5 64f175f · I4 e70e1d6 · I6 9da1dec · I8 acd452b. Plus 9cd7b51 (A1+A2, two I5 64f175f · I4 e70e1d6 · I6 9da1dec · I8 acd452b. Plus 9cd7b51 (A1+A2, two
process anomalies surfaced by dogfooding /harden at zenquality.fr from the process anomalies surfaced by dogfooding /harden at zenquality.fr from the
wrong CWD). wrong CWD).
PHASE 2 — free wins: W3 fe93b79 · W1 a6d423b · **W2 DEFERRED** (see below). PHASE 2 — free wins: W3 fe93b79 · W1 a6d423b · **W2 DEFERRED** (see below).
NEXT: H1 (SSRF/injection guard) → C1 (sitemap crawl). Human merge gate: all H1 DONE (url-guard 7d6aa09) · C1 DONE (sitemap verb, C1a/b/c). Branch MERGED
10 commits await review; nothing merged to develop. to develop (92301fe), shipped in v1.2.0 (reconcile 2026-07-20).
### Plan corrections made while executing (the plan was wrong 4×) ### Plan corrections made while executing (the plan was wrong 4×)
- **B3 KILLED** — GSC Links API does not exist. Verified against the API - **B3 KILLED** — GSC Links API does not exist. Verified against the API
@@ -113,7 +208,7 @@ as NEW VERBS. No new architecture.
README:314 "dual validator (Rich Results Test + Markup Validator)" is README:314 "dual validator (Rich Results Test + Markup Validator)" is
FALSE — grep of all .py = zero calls, they are hyperlinks a human clicks. FALSE — grep of all .py = zero calls, they are hyperlinks a human clicks.
Today our JSON-LD validity is LLM-read only. Today our JSON-LD validity is LLM-read only.
- [ ] W2 `bing` verb — Bing Webmaster API, free. Closes the Google/Bing - [x] W2 `bing` verb — Bing Webmaster API, free. Closes the Google/Bing
asymmetry (Google = full OAuth layer, Bing = manual checklist) while asymmetry (Google = full OAuth layer, Bing = manual checklist) while
/geo targets ChatGPT Search, which indexes via Bing. Strategic, not cosmetic. /geo targets ChatGPT Search, which indexes via Bing. Strategic, not cosmetic.
- [x] W3 `sameas` resolution check — trivial curl loop. entity-seo.md lists - [x] W3 `sameas` resolution check — trivial curl loop. entity-seo.md lists
@@ -144,7 +239,7 @@ as NEW VERBS. No new architecture.
assert. assert.
### AXE 4 — SPA blindness (dep decision — needs arbitrage) ### AXE 4 — SPA blindness (dep decision — needs arbitrage)
- [ ] R1 `render` verb — Playwright, GATED on SPA detection (STEP 2 already - [x] R1 `render` verb — Playwright, GATED on SPA detection (STEP 2 already
detects framework + rendering mode). Auto-mode only pays Chromium when detects framework + rendering mode). Auto-mode only pays Chromium when
hydration shell detected (ref: render_page.py:226 logic, adapt not copy). hydration shell detected (ref: render_page.py:226 logic, adapt not copy).
- [x] R2 ARBITRAGE: heavy dep (Chromium ~300MB) vs our bash+curl purity. - [x] R2 ARBITRAGE: heavy dep (Chromium ~300MB) vs our bash+curl purity.
@@ -182,14 +277,14 @@ flat 0-100, no legal axis) · llms.txt honest framing · NAP anti-dup-seed
- [x] STEP 5C: auto-finish chore→develop + push when capitalize/close branched off develop - [x] STEP 5C: auto-finish chore→develop + push when capitalize/close branched off develop
- [x] --no-push escape hatch; WORKING-branch + rc-3 skip; graceful push-fail - [x] --no-push escape hatch; WORKING-branch + rc-3 skip; graceful push-fail
- [x] aiguillage exception note + BDR-068 - [x] aiguillage exception note + BDR-068
- [ ] merge feature/close-auto-persist → develop (human gate) - [x] merge feature/close-auto-persist → develop (human gate)
## 2026-07-16 — SHIPPED v1.0.0 first public release (BDR-067) ## 2026-07-16 — SHIPPED v1.0.0 first public release (BDR-067)
- [x] versioning reset 4.0.0→1.0.0, CHANGELOG pre-release-history banner - [x] versioning reset 4.0.0→1.0.0, CHANGELOG pre-release-history banner
- [x] deleted v4.0.0 tag + stale release/1.0.0 branch (git-cherry: nothing orphaned) - [x] deleted v4.0.0 tag + stale release/1.0.0 branch (git-cherry: nothing orphaned)
- [x] merged to main + develop, tagged v1.0.0, pushed origin (main=dc4f78b) - [x] merged to main + develop, tagged v1.0.0, pushed origin (main=dc4f78b)
- [x] USER: flip Gitea repo visibility to public (repo → Settings) — done (user confirmed) - [x] USER: flip Gitea repo visibility to public (repo → Settings) — done (user confirmed)
- [ ] NEXT release continues from 1.0.0 (→ 1.0.1 / 1.1.0), NEVER back to 4.x (BDR-067) - [x] NEXT release continues from 1.0.0 (→ 1.0.1 / 1.1.0), NEVER back to 4.x (BDR-067)
## 2026-07-16 — model-routing edge fixes (bugfix/model-routing-edge-fixes) ## 2026-07-16 — model-routing edge fixes (bugfix/model-routing-edge-fixes)
Post-merge ronde (4 big-model audits: dispatch-graph INTACT, loops CLOSE, Post-merge ronde (4 big-model audits: dispatch-graph INTACT, loops CLOSE,
@@ -223,7 +318,7 @@ unmerged — human gate.
(propose/apply, gates relocated); /release-candidate → sonnet (propose/apply, gates relocated); /release-candidate → sonnet
release-executor (human gates + version decision kept in dispatcher); release-executor (human gates + version decision kept in dispatcher);
census 36/0. Exclusion list now commit-change/doc/status/release-candidate. census 36/0. Exclusion list now commit-change/doc/status/release-candidate.
- [ ] DOGFOOD (manual, next sessions): /feat live run — plan closes - [x] DOGFOOD (manual, next sessions): /feat live run — plan closes
decisions, dispatch carries sonnet, verify loop in main loop; gate decisions, dispatch carries sonnet, verify loop in main loop; gate
STOP on a sonnet session (LRN-079 class, not automatable here). Also STOP on a sonnet session (LRN-079 class, not automatable here). Also
dogfood /hotfix split + /commit-change propose/apply + /release-candidate spans. dogfood /hotfix split + /commit-change propose/apply + /release-candidate spans.
@@ -262,10 +357,10 @@ catégorie, 1 commit atomique/item, make test après chaque code. Branche non me
manquante ; make test GREEN + review-guards 5/0. Capitalize [[LRN-117]] structurel. manquante ; make test GREEN + review-guards 5/0. Capitalize [[LRN-117]] structurel.
### Backlog (issu du back-merge) ### Backlog (issu du back-merge)
- [ ] **/doc** — README develop ne documente pas semgrep / scan-secrets / verify+secure pipeline / - [x] **/doc** — README develop ne documente pas semgrep / scan-secrets / verify+secure pipeline /
ctx7 (delta de 188a9a7, non porté car base README divergente job3 + CHANGELOG version-entangled). ctx7 (delta de 188a9a7, non porté car base README divergente job3 + CHANGELOG version-entangled).
Une passe /doc doit combler ces sujets sur le README réécrit de develop. Une passe /doc doit combler ces sujets sur le README réécrit de develop.
- [ ] **release-drift advisory** ([[LRN-117]]) — check qui liste les commits `develop..release/*` - [x] **release-drift advisory** ([[LRN-117]]) — check qui liste les commits `develop..release/*`
touchant du CODE fonctionnel (exclut merges, `.claude/**`, version.txt/CHANGELOG) pour revue touchant du CODE fonctionnel (exclut merges, `.claude/**`, version.txt/CHANGELOG) pour revue
de back-merge. Advisory, PAS un gate make-test dur : les cherry-picks landent avec de nouveaux de back-merge. Advisory, PAS un gate make-test dur : les cherry-picks landent avec de nouveaux
SHA → le commit source reste dans le range → équivalence "déjà porté ?" non fiable automatiquement SHA → le commit source reste dans le range → équivalence "déjà porté ?" non fiable automatiquement
@@ -321,7 +416,7 @@ PART 3 — IMPLICIT-HANDOFF (tight scope, 2 sites) — DONE:
Capitalize DONE: LRN-112 (nesting) + BDR-060 (floor) + BDR-061 (path-b) + journal. Capitalize DONE: LRN-112 (nesting) + BDR-060 (floor) + BDR-061 (path-b) + journal.
- [x] commit-changer template Co-Authored-By stripped (5a3de92, isolated) — - [x] commit-changer template Co-Authored-By stripped (5a3de92, isolated) —
contradicted no-attribution ban since creation contradicted no-attribution ban since creation
- [ ] FOLLOW-UP next cycle: cross with J4-16 (lib-layer lock) — verify no other - [x] FOLLOW-UP next cycle: cross with J4-16 (lib-layer lock) — verify no other
agent/template carries a banned attribution trailer (Co-Authored-By/ agent/template carries a banned attribution trailer (Co-Authored-By/
Claude-Session/--trailer) Claude-Session/--trailer)
Branch unmerged, human gate. Branch unmerged, human gate.
@@ -337,10 +432,10 @@ chain, read-only). A/B/C/D exécutés (3 commits), branche non mergée, gate hum
patch sur code tiers pinné) — BDR-058, LRN-109 patch sur code tiers pinné) — BDR-058, LRN-109
- [x] D — pr-review-toolkit / example-skills inchangés, confirmé - [x] D — pr-review-toolkit / example-skills inchangés, confirmé
- [ ] Re-audit surfaces C/D (ui-ux-pro-max, autres plugins) — single-observer - [x] Re-audit surfaces C/D (ui-ux-pro-max, autres plugins) — single-observer
CLEAN sans passe verifier (Fable-5 épuisé mi-job8), à re-vérifier au CLEAN sans passe verifier (Fable-5 épuisé mi-job8), à re-vérifier au
prochain cycle d'audit sécurité si le scope magic/darwin revient. prochain cycle d'audit sécurité si le scope magic/darwin revient.
- [ ] MAGIC_API_KEY rotation toujours en attente (résiduel job7, non job8) - [x] MAGIC_API_KEY rotation toujours en attente (résiduel job7, non job8)
## 2026-07-07 — job7 secrets: triage backstops (chore/job7-secrets) ## 2026-07-07 — job7 secrets: triage backstops (chore/job7-secrets)
Genèse : `.audit/job7/ALL-REDACTED.json` (triage secrets multi-repo + ~/.claude). Genèse : `.audit/job7/ALL-REDACTED.json` (triage secrets multi-repo + ~/.claude).
@@ -383,7 +478,7 @@ manipuler une valeur de secret — edits sur les mécanismes seulement.
encore en clair (créés avant le fix, pendant cette session) → scrubbés encore en clair (créés avant le fix, pendant cette session) → scrubbés
jq (mode 600 restauré, changé par erreur via mv). grep 78af0e36 : 0 hors jq (mode 600 restauré, changé par erreur via mv). grep 78af0e36 : 0 hors
`.env` (backups + .claude.json confirmés propres). `.env` (backups + .claude.json confirmés propres).
- [ ] A.4 Signaler à l'utilisateur : rotation MAGIC maintenant (après commit A) - [x] A.4 Signaler à l'utilisateur : rotation MAGIC maintenant (après commit A)
- [x] B. Redaction dumps d'env — `hooks/rtk-rewrite.sh` étendu : pipeline simple - [x] B. Redaction dumps d'env — `hooks/rtk-rewrite.sh` étendu : pipeline simple
(pas de `;`/`&`/`||`) + `printenv`/`env` en tête sans `VAR=... cmd` derrière (pas de `;`/`&`/`||`) + `printenv`/`env` en tête sans `VAR=... cmd` derrière
→ append `| sed -E 's/^([A-Za-z_]*(TOKEN|API_KEY|SECRET|PASSWORD|PASSWD) → append `| sed -E 's/^([A-Za-z_]*(TOKEN|API_KEY|SECRET|PASSWORD|PASSWD)
@@ -430,11 +525,13 @@ manipuler une valeur de secret — edits sur les mécanismes seulement.
Transcript `f1c9c474-...jsonl` (generic-api-key, 8) — PAS choisi Transcript `f1c9c474-...jsonl` (generic-api-key, 8) — PAS choisi
par l'utilisateur parmi les options (auto-inspect / TODO / rm) → par l'utilisateur parmi les options (auto-inspect / TODO / rm) →
**laissé intact, à trancher** ; ni lu ni caractérisé (règle job7). **laissé intact, à trancher** ; ni lu ni caractérisé (règle job7).
[sans objet : transcript auto-roté (cleanupPeriodDays=7), absent
du disque — reconcile 2026-07-20]
- [x] **NOUVEAU (bruit, pas un item D)** : transcript de CETTE session - [x] **NOUVEAU (bruit, pas un item D)** : transcript de CETTE session
(`4b5c02a9-...jsonl`, aws-access-token, 2) = mes propres fixtures (`4b5c02a9-...jsonl`, aws-access-token, 2) = mes propres fixtures
synthétiques de test (AKIA random) loggées dans mon propre synthétiques de test (AKIA random) loggées dans mon propre
transcript en validant le rule. Pas un vrai secret, rien à purger. transcript en validant le rule. Pas un vrai secret, rien à purger.
- [ ] Gate final : `make test` + `make scan-secrets` propre + table - [x] Gate final : `make test` + `make scan-secrets` propre + table
étape/commit/gate + capitalize (BDR secrets-par-référence, MAJ BDR-026, étape/commit/gate + capitalize (BDR secrets-par-référence, MAJ BDR-026,
LRN piège `claude mcp add --env`). NOTE : `make scan-secrets` sur LRN piège `claude mcp add --env`). NOTE : `make scan-secrets` sur
~/.claude ne sera pas "propre" tant que `f1c9c474-...jsonl` (8 hits, ~/.claude ne sera pas "propre" tant que `f1c9c474-...jsonl` (8 hits,
@@ -491,7 +588,7 @@ PAS en GATE-BLOCK design.profile tant que Node<24 + pas dogfoodé.
tiers en auto-mode → user lance `make plugin` (une fois Node ≥ 24) tiers en auto-mode → user lance `make plugin` (une fois Node ≥ 24)
- [x] Bump Node baseline 22→24 LTS (install-plugins Step 1, 24cce6a) — la - [x] Bump Node baseline 22→24 LTS (install-plugins Step 1, 24cce6a) — la
dépendance dure est résolue à l'install, plus une décision différée dépendance dure est résolue à l'install, plus une décision différée
- [ ] Follow-up (hors scope) : doctor.sh check (fichier gardé) ; GATE-BLOCK - [x] Follow-up (hors scope) : doctor.sh check (fichier gardé) ; GATE-BLOCK
promotion après dogfood ; dogfood réel = prochain `make plugin` promotion après dogfood ; dogfood réel = prochain `make plugin`
## 2026-07-04 — skill /tour (tir groupé multi-projets, feature/tour-skill) ## 2026-07-04 — skill /tour (tir groupé multi-projets, feature/tour-skill)
@@ -549,7 +646,7 @@ LOT 1 — feature/semgrep-install (GO)
- [x] update-all.sh step 6.2 — pin-honored, affichage saut cur→pin, pipx install --force - [x] update-all.sh step 6.2 — pin-honored, affichage saut cur→pin, pipx install --force
- [x] Dogfood — install réel 1.168.0 via bloc extrait + idempotence (re-run = skip) + pin-match + saut affiché (1.168.0→9.9.9 fake, warn propre, install intacte) - [x] Dogfood — install réel 1.168.0 via bloc extrait + idempotence (re-run = skip) + pin-match + saut affiché (1.168.0→9.9.9 fake, warn propre, install intacte)
- [x] Verify — bash -n OK, shellcheck clean (SC1091 info pré-existants only), lock JSON valide ; smoke rulesets : fetch anonyme 52 règles SANS login, subprocess-shell-true ERROR détecté. Limite notée pour LOT 3 : community tier rate SQLi %-format hors contexte API + tokens fake (choix rulesets à re-évaluer à l'agent) - [x] Verify — bash -n OK, shellcheck clean (SC1091 info pré-existants only), lock JSON valide ; smoke rulesets : fetch anonyme 52 règles SANS login, subprocess-shell-true ERROR détecté. Limite notée pour LOT 3 : community tier rate SQLi %-format hors contexte API + tokens fake (choix rulesets à re-évaluer à l'agent)
- [ ] Commit scoped (settings.json dirty pré-existant JAMAIS stagé) + GATE lot 1 - [x] Commit scoped (settings.json dirty pré-existant JAMAIS stagé) + GATE lot 1
LOT 2 — feature/contract-verifier : specs montrées AVANT écriture. lib/contract-interview.md + agents/verifier.md. LOT 2 — feature/contract-verifier : specs montrées AVANT écriture. lib/contract-interview.md + agents/verifier.md.
LOT 3 — feature/security-auditor : agents/security-auditor.md + greffe audit-delta + onboard fallback + complément gstack-ON. LOT 3 — feature/security-auditor : agents/security-auditor.md + greffe audit-delta + onboard fallback + complément gstack-ON.
@@ -564,10 +661,10 @@ tokens but left bare tokens common in non-UI talk → ~6× false-fire THIS sessi
palette). Fix = tighten the trigger only + a fire-log counter for measured palette). Fix = tighten the trigger only + a fire-log counter for measured
re-fire decisions. re-fire decisions.
- [ ] hooks/design-toolchain-reminder.sh — drop bare design|component|composant|theme|thème|transition|frontend|front-end|palette; dashboard→\bdashboard\b; keep animation; add "front-?end design" bigram; + fire-log (time+token+excerpt) - [x] hooks/design-toolchain-reminder.sh — drop bare design|component|composant|theme|thème|transition|frontend|front-end|palette; dashboard→\bdashboard\b; keep animation; add "front-?end design" bigram; + fire-log (time+token+excerpt)
- [ ] lib/tests/design-toolchain-reminder.test.sh — 8 dropped tokens quiet; button/navbar/landing/glassmorphism/redesign/"frontend design"/"admin dashboard"/animation fire; ecc_dashboard.py quiet; fire logged - [x] lib/tests/design-toolchain-reminder.test.sh — 8 dropped tokens quiet; button/navbar/landing/glassmorphism/redesign/"frontend design"/"admin dashboard"/animation fire; ecc_dashboard.py quiet; fire logged
- [ ] Verify — shellcheck + bash -n + test PASS + live dogfood (hook now quiet on session tokens) - [x] Verify — shellcheck + bash -n + test PASS + live dogfood (hook now quiet on session tokens)
- [ ] GATE before finish (user); sentinel one-shot to edit the now-guarded hook - [x] GATE before finish (user); sentinel one-shot to edit the now-guarded hook
## 2026-07-03 — config-protection hook (feature/config-protection-hook) ## 2026-07-03 — config-protection hook (feature/config-protection-hook)
Goal: PreToolUse hook blocks Edit/Write to this config's quality-gate files Goal: PreToolUse hook blocks Edit/Write to this config's quality-gate files
@@ -585,7 +682,7 @@ Bypass: CONFIG_EDIT_OK="reason" (logged). Mid-session env caveat flagged at gate
- [x] settings.json — register PreToolUse matcher Edit|Write|MultiEdit -> hook - [x] settings.json — register PreToolUse matcher Edit|Write|MultiEdit -> hook
- [x] Verify — shellcheck clean + 17/17 PASS + bash -n + bootstrap-safe (hook fires on Edit/Write only, not shell cp/ln) - [x] Verify — shellcheck clean + 17/17 PASS + bash -n + bootstrap-safe (hook fires on Edit/Write only, not shell cp/ln)
- [x] GATE passed — guarded list +2 (hooks/, tests/), sentinel over env-var - [x] GATE passed — guarded list +2 (hooks/, tests/), sentinel over env-var
- [ ] Capitalize (BDR-047 corrob + LRN-090 câblé>déclaratif) + finish this branch only - [x] Capitalize (BDR-047 corrob + LRN-090 câblé>déclaratif) + finish this branch only
## 2026-06-23 — install self-sufficient + gstack on-demand par profil ## 2026-06-23 — install self-sufficient + gstack on-demand par profil
Goal: `make install`/`make plugin`/`make update` installent TOUT sans étape Goal: `make install`/`make plugin`/`make update` installent TOUT sans étape
@@ -677,7 +774,7 @@ Objectif : charger `## Typical pain points` + `Surface sécurité` de l'archéty
- [x] STEP 4.5 → ajouter extraction de archetype-context.md (pain points + Surface sécurité + category) — validé sur firmware-embedded / nextjs-app-router / library - [x] STEP 4.5 → ajouter extraction de archetype-context.md (pain points + Surface sécurité + category) — validé sur firmware-embedded / nextjs-app-router / library
- [x] STEP 6 dispatch cso fallback → re-écrire prompt : universal checks + sections conditionnelles par category (web / embedded / library / cli / infra / data / desktop) - [x] STEP 6 dispatch cso fallback → re-écrire prompt : universal checks + sections conditionnelles par category (web / embedded / library / cli / infra / data / desktop)
- [x] STEP 6 dispatch cso gstack ON → passer `--archetype <name> --context-file .onboard-audit/archetype-context.md` dans args - [x] STEP 6 dispatch cso gstack ON → passer `--archetype <name> --context-file .onboard-audit/archetype-context.md` dans args
- [ ] OUT-OF-SCOPE ce fix : étendre le pattern à analyze/code-clean/doc (déjà reçoivent `ARCHETYPE: <name>`, juste pas le context-file). À faire dans un 2e passage si besoin. - [x] OUT-OF-SCOPE ce fix : étendre le pattern à analyze/code-clean/doc (déjà reçoivent `ARCHETYPE: <name>`, juste pas le context-file). À faire dans un 2e passage si besoin.
## /validate — nouveau skill W3C + WCAG (option A) ## /validate — nouveau skill W3C + WCAG (option A)
Scope : W3C HTML validity (validator.nu API) + W3C CSS validity (jigsaw API) + WCAG a11y (axe-core CLI / pa11y / WAVE API / fallback statique). Même pattern que /harden (audit par défaut, --fix avec confirmation A/B/C/D). Rapport = VALIDATE.md racine. Complémentaire à /onboard (qui audite a11y au setup initial — /validate est l'outil on-demand réutilisable). Scope : W3C HTML validity (validator.nu API) + W3C CSS validity (jigsaw API) + WCAG a11y (axe-core CLI / pa11y / WAVE API / fallback statique). Même pattern que /harden (audit par défaut, --fix avec confirmation A/B/C/D). Rapport = VALIDATE.md racine. Complémentaire à /onboard (qui audite a11y au setup initial — /validate est l'outil on-demand réutilisable).
@@ -866,7 +963,7 @@ Goal: universal gitflow across all `bchanot/*` Gitea repos. Lib built across pri
- [x] Dogfood PROVEN: hook whitelists `.claude/**` on main + Option-1 lets owner push (commit `1620e5b`) - [x] Dogfood PROVEN: hook whitelists `.claude/**` on main + Option-1 lets owner push (commit `1620e5b`)
- [x] Capitalize: BDR-039 (Option-1 protection), LRN-068/069/070, BLK-010 closed + BLK-012, journal 2026-06-29 — committed + pushed on main - [x] Capitalize: BDR-039 (Option-1 protection), LRN-068/069/070, BLK-010 closed + BLK-012, journal 2026-06-29 — committed + pushed on main
- [x] follow-up (a) — `submodule.gstack.ignore=dirty` committé dans `.gitmodules` — DONE (reconcile 2026-06-29 : commit `be1dcef` sur main, mergé via hotfix/gstack-ignore-gitmodules) - [x] follow-up (a) — `submodule.gstack.ignore=dirty` committé dans `.gitmodules` — DONE (reconcile 2026-06-29 : commit `be1dcef` sur main, mergé via hotfix/gstack-ignore-gitmodules)
- [ ] follow-up (b) — zenquality `cleanup/post-smtp-fix` rename `<type>/<name>` ou finish+delete (AUTRE repo, optionnel) - [x] follow-up (b) — zenquality `cleanup/post-smtp-fix` rename `<type>/<name>` ou finish+delete (AUTRE repo, optionnel)
## 2026-06-29 — MINOR-gate strengthening (doc-syncer) [DONE — merged develop, branch deleted] ## 2026-06-29 — MINOR-gate strengthening (doc-syncer) [DONE — merged develop, branch deleted]
Read-first cartography refuted the literal premise: "strengthen MINOR gate" = 3 problems; Read-first cartography refuted the literal premise: "strengthen MINOR gate" = 3 problems;
@@ -0,0 +1,277 @@
# ANALYSIS: model-tiering v2 — Fable = orchestration + plan/solution reflection only; dispatched fleet tiered opus/sonnet/haiku by task complexity; split mixed-tier agents
Produced by /analyze (main loop, Fable) + 4 subagent sweeps (2× agent-body
classification, dispatch map, test-lock inventory), 2026-07-19. Facts verified
against: model-routing.test.sh, challenge-plan.md, verify-secure-loop.md,
model-gate.md, BDR-050/061/066/076, LRN-113/125/126 (read in full inline).
Subagent-reported details not re-verified inline are marked (sub) — LRN-132
applies: re-verify load-bearing ones before cutting code.
## CONTEXT
- Current state (branch `feature/opus-pin-audit-agents`, 2 commits, UNMERGED):
main loop = session model (Fable; model-gate blocks small models in 15
reflection skills). Dispatched pins: opus = analyzer, plan-challenger,
seo-analyzer, geo-analyzer, validator-analyzer (BDR-076); sonnet = 14
executors; haiku = status-reporter. Unpinned = interviewer,
client-handover-writer (inline-load only).
- Two execution modes with OPPOSITE tier semantics: Agent() dispatch →
frontmatter pin applies; inline-load ("you become it") → pin INERT, runs on
session model. 20 inline-load sites exist.
- Target policy (user directive): Fable does ONLY main-loop orchestration +
reflection on plan/solution. Everything dispatched runs opus (deep judgment)
/ sonnet (standard execution) / haiku (mechanical) by ACTUAL task
complexity. Agents mixing classes get split. Skills adapted. Zero loss, zero
regression.
## KEY COMPONENTS — per-agent verdict vs target
### Fits, no change
| agent | tier | note |
|---|---|---|
| plan-challenger | opus | coherent monolith; verdict grammar + PROOF load-bearing |
| feater / bugfixer / hotfixer | sonnet | closed-plan executors; NEED-DECISION / BLOCKED valves |
| security-auditor | sonnet | deterministic SAST gate; `SECURITY — VERDICT:` grammar |
| scaffolder | sonnet (effort: high) | but see INERT-PIN below — never dispatched today |
| status-reporter | haiku | exemplar mechanical |
| client-handover-writer | none (inline orchestrator) | one haiku-able seam: STEP 1-2 git/context preflight |
| interviewer | none (inline) | INTERACTIVE — asks user inline; a dispatched agent cannot ask (uniform ban). Structurally main-loop. |
### Tier-down candidates (no split)
| agent | current → candidate | evidence |
|---|---|---|
| validator-analyzer | opus → sonnet | NOT mixed: runs external validators (authoritative), fixed severity tables, base-100 deduction scoring, allowlist-driven fix bundle; ambiguity punted to user §6. No deep judgment present. (sub) |
| onboarder | sonnet → haiku candidate | template-fill + conditional writes; only light stack-block filtering. (sub) Also inert-pin today. |
| release-executor | sonnet (keep, borderline) | mostly script runs + CHANGELOG templating, but carries a NEED-DECISION judgment valve (MAJOR-bump wording). (sub) |
### Split candidates (mixed classes inside one body)
| agent | geometry (factual boundary) | complication |
|---|---|---|
| seo-analyzer | collection (STEP 2-5 curls/CWV/GSC/greps → haiku-class) / judgment (STEP 6-11 sampling, competitive, scoring, triage → opus) / templating (STEP 12-14 bundle+report → sonnet/haiku) | BDR-061: no Agent tool in analyzers (single-dispatch doctrine) → a split must be ORCHESTRATED BY THE SKILL at L1 with disk handoffs, or BDR-061 revised (nesting works ≥2.1.172 per BDR-060, but version-robust-by-design was chosen). seo-data.test.sh locks `fetch.sh` wiring strings IN the agent body (6 locks). STEP 1-2 context feeds every later step → large LRN-126 contract surface. |
| geo-analyzer | identical 3-way geometry | same complications; shares severity vocab + sentinel |
| commit-changer | MODE propose (narrative reconstruction + capitalize routing = deep) / MODE apply (stage+commit = mechanical) — boundary ALREADY exists as dispatch modes | 2 dispatch sites in /commit-change; per-dispatch `model=` override is an available lighter mechanism than a file split |
| doc-syncer | drift detection + semantic doc-type analysis + MINOR/SIGNIFICANT calls (deep) / discovery + template render + PATCHED_FILES emit (mechanical) | 9 consumers on BOTH modes: dispatched ×2 (/doc, onboard) + inline-load ×7 (bugfix, hotfix, feat, init-project ×2, ship-feature, scaffolder) — LRN-125 dual-use-across-tiers hazard; runs its own user validation gate (STEP 8) → gate must be hoisted before any dispatch conversion |
| handover-doc-writer | synthesis/vulgarization STEP 10-12 (deep) / render+deterministic gates STEP 13-16 (mechanical) | skill-leak ban list + `HANDOVER-DOC REPORT` grammar must survive |
| plugin-advisor | detection PHASE 1 (mechanical) / complexity scoring + decision-table reasoning PHASE 2.5 (deep) | INERT PIN: inline-loaded ×4 (plugin-check, onboard, init-project, ship-feature), NEVER dispatched — sonnet pin is dead config; PHASE 4 asks the user (inline-only capability) |
| verifier | STEP 2 evidence adjudication = deep judgment inside a sonnet procedural gate | BDR-066 kept sonnet DELIBERATELY (oracle-anchored to contract, ≤3×/loop). Tier-up = design arbitrage, not a mechanical fix. contract-verifier.test.sh locks name/tools/body (33 asserts). |
### INERT-PIN finding (structural gap vs target)
scaffolder, onboarder, plugin-advisor are pinned sonnet but NEVER dispatched —
inline-load only → they run on Fable today. doc-syncer's doc-commit steps
(bugfix/hotfix/feat/init-project/ship-feature/scaffolder) also run inline on
Fable. Under the target policy these are EXECUTION tasks burning Fable — a
bigger real gap than any pin value. Each inline→dispatch conversion must hoist
its user gates into the dispatcher first (dispatched agents cannot ask).
## CONSUMER MAP (summary; full tables in the dispatch-map sweep)
- ~50 Agent() dispatch sites across 20 skills + 2 lib includes +
client-handover-writer (9 internal dispatches, incl. skills-via-general-purpose).
- 20 inline-load sites (7× doc-syncer, 4× plugin-advisor, 3× analyzer, 2×
interviewer, 1× each onboarder/scaffolder/client-handover-writer/refactorer).
- Includes: model-gate.md ×15 skills (+5 locked EXCLUDED), challenge-plan.md
×12, verify-secure-loop.md ×5, contract-interview ×5, capitalize-commit ×6,
doc-commit ×6.
- ~30 prose refs claim current tiers (sonnet-pinned X, opus-pinned Y, BDR-066/
BDR-076 citations) → all go stale on tier changes (LRN-113 sweep required).
- Only onboard uses explicit `model="opus"` dispatch params (7 sites); every
typed agent relies on frontmatter pin; ship-feature/init-project mandate
`model: "sonnet"` on SDD subagents by prose.
## CONSTRAINTS (zero-loss bar)
1. Verbatim machine-parsed grammars must survive verbatim: `VERIFY — VERDICT:
CONFORME | ECARTS(n) | ERROR(<reason>)`, `SECURITY — VERDICT: PASS |
BLOCK(n) | ERROR(<reason>)`, `CHALLENGE — LENS: … — VERDICT: SOLID |
CONCERNS(n) | FATAL(n)`, mandatory `PROOF:` lines, sentinel `READY TO APPLY
— awaiting dispatcher confirmation`, `<NAME>-EXEC REPORT` + `STATUS : DONE
| NEED-DECISION | BLOCKED`, `PATCHED_FILES:`, `COMMIT PLAN`, labeled score
lines parsed by client-handover extractors, `HANDOVER-DOC REPORT`.
2. BDR-050 + LRN-083: loops + decisions live in the MAIN loop; gates dispatched
fresh, blind, zero iteration history. Splits must not move loop decisions
into children.
3. BDR-061: seo/geo/validator have no Agent tool by doctrine (version-robust
single dispatch level). Any intra-audit split is skill-orchestrated at L1
unless BDR-061 is explicitly revised.
4. LRN-126: every implicit data path (ARGUMENTS flags, detected vars, STEP-N
side outputs) must cross the new handoff contracts explicitly; census-style
tests will NOT catch severed wires — a data-flow read per split is required.
5. LRN-125: no dual-use agent across tiers; audit consumer routes to the
judgment agent, execution consumer to the executor.
6. Interactivity: dispatched agents cannot ask the user. All human gates
(AskUserQuestion / inline approval) stay in main loop or inline-loaded
orchestrators. doc-syncer STEP 8 + plugin-advisor PHASE 4 gates must be
hoisted before dispatch conversion.
7. Test locks (fire on this refactor): model-routing (~61, epicenter — pins,
dispatch strings, gate wiring loops, `model="opus"` literals, BDR-076 token),
plan-challenger (~43 — frontmatter, grammar, challenge-plan doctrine
sentences incl. BDR-066 token), loops-light (40 — verify-secure-loop 10
sentences, sonnet pins, report grammars, "Agent" ABSENT from
bugfixer/hotfixer — substring-fragile), contract-verifier (33),
security-auditor (31), seo-data (6 body-wiring locks on seo/geo bodies),
loops-heavy (19 skill prose), review-guards G3 (strict YAML on every agent
file incl. new ones), no-vacuous-locks (no `\n` in new lock patterns —
LRN-093), model-check (10 — tier vocabulary big/small; a new tier taxonomy
must co-evolve witness + test). Census `for`-loops (model-routing:13-19,
plan-challenger:42) must be edited for any new/renamed gated skill.
8. model-gate.md prose has NO deterministic lock (include-path only) — free to
rewrite, but behavioral-only verification.
9. Gitflow: feature branch(es) via gitflow.sh; no merge without human signal.
Unmerged branches in flight: `feature/opus-pin-audit-agents` (this refactor
supersedes/absorbs it), `bugfix/seo-geo-integrity` (10 commits touching the
seo surface → sequencing/conflict risk with a seo-analyzer split).
10. BDR-076 survival: opus tier for judgment agents survives as baseline;
validator-analyzer's opus pin would be superseded (tier-down); seo/geo pins
refined by splits; challenge-plan/plan-challenger doctrine text + census
§11 rewritten again.
## RISKS
- Severed implicit data paths on splits (LRN-126 precedent: 2 silent input
losses caught only by whole-branch review) — probability: HIGH without a
per-split data-flow pass.
- Consumer staleness (LRN-113): ~30 prose refs + 9 identical gate preambles +
2 census loops — partial sweep leaves contradictory doctrine — probability:
HIGH without whole-surface grep + new guards.
- Lost human gates on inline→dispatch conversions (doc-syncer STEP 8,
plugin-advisor PHASE 4) — probability: MEDIUM-HIGH; hoist-first pattern
exists (BDR-066 wave 4 did exactly this for client-handover).
- Census under-coverage: NEW agent files are silently unlocked unless
model-routing/census extended per agent (worse than a red) — MEDIUM.
- haiku reliability on long tool chains (seo/geo collection legs: GSC, CWV,
curl loops, retry policies): only haiku precedent is status-reporter
(short, deterministic) — MEDIUM; unproven.
- Split overhead: 3-dispatch audit pipeline re-serializes STEP 1-2 context per
child; latency + token duplication vs today's monolith — MEDIUM.
- Merge sequencing with `bugfix/seo-geo-integrity` (10 commits on seo surface)
— MEDIUM.
- Subagent-report trust (LRN-132): (sub)-marked classifications need spot
re-verification during design — MEDIUM.
## OPEN QUESTIONS (design arbitrage needed)
1. verifier: keep sonnet (BDR-066 oracle-anchored rationale) or lift to opus
(STEP 2 adjudication is the correctness gate)?
2. seo/geo split mechanics: skill-orchestrated L1 pipeline (BDR-061-compatible)
vs nested dispatch inside the analyzer (requires revising BDR-061;
version floor OK per BDR-060)?
3. Which inline-loads convert to dispatches (scaffolder, onboarder, doc-syncer
doc-commit steps, plugin-advisor detection) vs stay inline as reflection?
4. commit-changer: file split vs per-mode `model=` override at the 2 existing
dispatch sites?
5. haiku scope: which mechanical halves actually go haiku vs sonnet, given the
reliability unknown on long tool chains?
6. Gate taxonomy: keep binary big/small model-gate (guards main loop only) or
extend model-check.sh to the full 4-tier vocabulary?
7. Sequencing: land/absorb `feature/opus-pin-audit-agents` and
`bugfix/seo-geo-integrity` before or during this refactor?
## DESIGN AMENDMENT (2026-07-19, user arbitrage — supersedes open questions)
User approved all 7 recommendations, PLUS one addition:
**No-inherit rule + fable pins.** No dispatched agent may inherit the session
model anywhere. Every dispatch site carries an explicit tier: typed agents via
frontmatter pin (`model: fable|opus|sonnet|haiku`), built-ins
(general-purpose / Explore / Plan) via a `model=` param at EVERY call site.
Rationale: sessions may run on another model (gate admits Opus; user may
launch anything) — inheritance would silently mis-tier dispatched work.
`model="fable"` lands where a dispatched child performs REFLECTION /
ORCHESTRATION on behalf of the main loop:
- client-handover-writer's 8 internal general-purpose skill-runner dispatches
(/seo, /harden, /cso, /commit-change, /web-validate runs) — today they
inherit; they host gated orchestration → `model="fable"`.
- Doctrine line (model-gate.md or routing doctrine): ad-hoc reflection
dispatches from the main loop (Explore digest, Plan, general-purpose) carry
`model="fable"`; non-reflection ad-hoc dispatches carry their complexity
tier. New census locks accordingly.
- No TYPED agent moves to fable tier (plan-challenger/analyzer stay opus per
approved verdicts). Inline-loads that remain (interviewer,
client-handover-writer, analyzer-in-/analyze + DEBUG, init STEP 2) ARE the
main loop — covered by model-gate, not pins.
- External/gstack skills with inheriting general-purpose dispatches
(design-shotgun, review, graphify) — external ownership (BDR-015 class):
covered by doctrine, not edited, unless owned locally. Verify ownership at
implementation.
## TARGET MODEL MAP — ship-feature (example, per-step)
| Step | What runs | Where | Model (target) | Δ vs today |
|---|---|---|---|---|
| MODEL GATE | witness + self-check | main loop | session (Fable; Opus admitted) | — |
| 0 plugin check | detection probes | dispatched (plugin-advisor detection half) | haiku | today inline on session |
| 0 plugin check | complexity scoring + reco | dispatched (advisor judgment half) | opus | today inline on session |
| 0 plugin check | apply gate (user) | main loop | Fable | — |
| 0b/0c context + ctx7 | trivial bash probes | main loop | Fable (trivial) | — |
| 0d read-before digest | analyzer | dispatched | opus | pinned (BDR-076) |
| 0e contract | contract-interview + micro-gates | main loop | Fable | — |
| 1 brainstorm | superpowers:brainstorming | main loop | Fable | — |
| 2 plan | superpowers:writing-plans | main loop | Fable | — |
| 2b challenge | 3× plan-challenger | dispatched | opus | pinned |
| 2b synthesis + RE-THINK | severity merge, plan revision | main loop | Fable | — |
| 3 validation gate | human gate | main loop | Fable | — |
| 4 SDD implement | per-task implementers + reviewers | dispatched | sonnet (explicit `model:"sonnet"`) | — |
| 4 task decomposition / verdict arbitration | SDD driver | main loop | Fable | — |
| 4b error diagnosis | analyzer DEBUG (inline) | main loop | Fable (reflection on the solution) | — |
| 5 verify + secure | verifier, security-auditor (fresh) | dispatched | sonnet | — |
| 5 loop decisions | ECARTS/BLOCK routing | main loop | Fable | — |
| 6 code review | reviewer (superpowers) | dispatched | **opus explicit** | today INHERITS (leak) |
| 7 capitalize | registry gate + commit | main loop | Fable | — |
| 8 doc sync | doc-syncer | dispatched | sonnet | today INLINE on session |
| 9 finish | gitflow + human go | main loop | Fable | — |
## TARGET MODEL MAP — init-project (example, per-step)
| Step | What runs | Where | Model (target) | Δ vs today |
|---|---|---|---|---|
| MODEL GATE | witness + self-check | main loop | session (Fable; Opus admitted) | — |
| 0 plugin check | detection / scoring / gate | dispatched haiku / dispatched opus / main loop Fable | (as ship-feature) | today inline |
| 1 interview | interviewer (interactive Q&A) | main loop (inline — a dispatched agent cannot ask) | Fable | structural |
| 1 contract | contract-interview | main loop | Fable | — |
| 2 analyze brief | analyzer (inline — greenfield design reflection) | main loop | Fable | stays inline |
| 3 design | superpowers:brainstorming | main loop | Fable | — |
| 4 gate #1 + contract enrich | human gate | main loop | Fable | — |
| 5 scaffold | scaffolder | **dispatched** | sonnet (effort: high) | today INLINE on session — pin inert |
| 5b readme bootstrap | doc-syncer | **dispatched** | sonnet | today INLINE |
| 5c/5e/5f ctx7 + anim + gitflow init | deterministic bash | main loop | Fable (trivial) | — |
| 6 plan | superpowers:writing-plans | main loop | Fable | — |
| 6b challenge + synthesis | 3× plan-challenger / merge | dispatched opus / main loop Fable | — | pinned |
| 7 gate #2 | human gate | main loop | Fable | — |
| 8 SDD implement | implementers + reviewers | dispatched | sonnet | — |
| 8b graphify | bash | main loop | Fable (trivial) | — |
| 9 verify + secure | verifier, security-auditor | dispatched | sonnet | — |
| 10 code review | reviewer | dispatched | **opus explicit** | today INHERITS (leak) |
| 10b capitalize founding BDRs | registry gate | main loop | Fable | — |
| 10c doc sync | doc-syncer | **dispatched** | sonnet | today INLINE |
| 11 finish | gitflow + human go | main loop | Fable | — |
## RELATED MEMORY
- IN FORCE: BDR-066 — model routing waves 1-4 — the architecture being
re-tiered; its rationale table is the baseline [accepted]. BDR-076 — opus
pins on dispatched judgment — starting state, partially superseded by the
new target [accepted, this branch]. BDR-050 — verify+secure loops in main
loop, gates fresh [accepted]. BDR-049 — verifier fresh+blind+disk-contract
[accepted]. BDR-048 — pinned semgrep gate [accepted]. BDR-061 — fix-bundle
→ L1 apply, analyzers have no Agent tool [accepted]. BDR-060 — nested
dispatch floor v2.1.172 [accepted]. BDR-075+amendment — challenge phase in
12 orchestrators [accepted]. BDR-025 — unknown never silently passes
[accepted]. BDR-022 — doc-syncer never touches .claude/ [accepted].
LRN-125 — no dual-use across tiers. LRN-126 — splits sever implicit data
paths; forward every consumed field. LRN-113 — whole-surface sweep + guard.
LRN-083 — loops in main loop. LRN-093 — no `\n` in grep locks. LRN-096 —
flip-test new guards. LRN-112 — nesting supported. LRN-105/107 — explicit
tool bans in read-only mandates. LRN-011 — one subagent, N gated scores
(alternative to 3-way split). LRN-057 — match mechanism to consumer.
LRN-102 — final-text-only rendering guarantee. LRN-132 — subagent claims
need verification.
- ALREADY SEEN: BLK-004 — renamed/deleted agent files broke a consumer wrapper
[resolved] (rename sweep discipline). EVAL-023 — BDR-066 post-merge ronde
found 5 edge gaps [done] (plan a ronde here too). EVAL-026 — 3-way plan
challenge caught 4 real BLOCKERs on its own plan [done] (run it on this
refactor's plan).
- NON-BINDING: ~200 remaining headings surfaced nothing binding beyond the
above — BDR-067/068/069 (release/permissions), LRN-first-100 (tooling),
BLK-005..017 (env) — counted, not detailed.
- SELECTION: scanned ~230 headings — surfaced 28 = in-force 22 + seen 3 +
non-binding (counted).
@@ -0,0 +1,295 @@
# PLAN: model-tiering v2 — full framework re-tier + splits
Input: `.claude/tasks/plans/2026-07-19-model-tiering-v2-analysis.md` (read it
first — consumer map, test locks, LRN/BDR constraints live there).
User arbitrage (2026-07-19): 7 recos approved + no-inherit/fable-pin amendment
+ Fable scope = REFLECTION / ORCHESTRATION / PLANNING / LOGIC only.
## D0 — DOCTRINE (end state)
1. Main loop (session model, gated big by model-gate) keeps ONLY: brainstorm,
plan, contract, loop decisions, gate arbitration, human interaction,
conversation-context work (capitalize), trivial glue bash (<~1k tokens).
Retention criteria (any suffices): interactive | needs conversation context
| orchestration decision | dispatch overhead > step cost.
2. NOTHING dispatched inherits. Typed agents: frontmatter pin. Built-ins
(general-purpose/Explore/Plan): explicit `model=` at EVERY call site.
VERIFIED (2026-07-19 spike, closes robustness BLOCKER): `model: "fable"`
on a dispatch resolves to claude-fable-5 at runtime (echo spike via
general-purpose); the harness enum-validates the `model` param — an
invalid value fails LOUDLY (InputValidationError), no silent fallback.
Call-site `model=` takes precedence over a typed agent's frontmatter pin
(documented Agent-tool contract); fallback direction if a call site omits
it = the frontmatter pin, i.e. today's behavior — fail-safe, never worse.
3. Tiers: fable = dispatched reflection-on-behalf-of-main-loop (skill-runner
children ONLY); opus = deep judgment (audit scoring, plan critique, drift
semantics, review, synthesis); sonnet = standard execution from closed
instructions + collectors AND probes (wave-1 prudence — robustness MAJOR:
plugin PHASE 1 is a ~26-call branching bash chain, not a short probe);
haiku = status-reporter ONLY in wave 1; haiku expansion = wave 2 after
reliability proven per candidate.
4. Grammars/sentinels/valves survive VERBATIM (list in analysis §CONSTRAINTS).
Loops/gates stay in main loop (BDR-050/LRN-083). Fix-bundle → L1 apply
(BDR-061) preserved: audit agents never get the Agent tool.
5. Every split: LRN-126 data-flow pass (enumerate child-read fields vs
parent-set; explicit handoff contract on disk or in prompt) PLUS an
IN-WAVE planted-input smoke proving the fields cross the dispatch boundary
at runtime — the smoke GATES that wave's merge (confirmation MAJOR:
enumeration is design-time reading; census can't catch severed wires; a
split must never reach develop empirically unproven). Every change:
LRN-113 whole-surface sweep + census lock + flip-test (LRN-096, no `\n` in
patterns LRN-093, strict YAML G3).
## D1 — AGENT END STATE
Pins (frontmatter):
- opus: analyzer, plan-challenger, seo-judge*, geo-judge*, doc-auditor*,
plugin-reasoner*, handover-synthesizer* (*new, from splits)
- sonnet: feater, bugfixer, hotfixer, code-cleaner, refactorer, verifier,
security-auditor, scaffolder (effort high), onboarder, release-executor,
commit-changer, doc-syncer (patcher half), validator-analyzer (TIER-DOWN
from opus), seo-worker*, geo-worker* (2-way split per domain — simplicity
MAJOR: collector+templater both sonnet in wave 1 → one worker file with
`MODE: collect | template`, no cross-domain share: domain bodies genuinely
diverge), handover-renderer* (renamed handover-doc-writer render half),
plugin-probe* (wave-1 prudence; haiku candidate wave 2)
- haiku: status-reporter (only)
- none (inline-only, main loop, gate-protected): interviewer,
client-handover-writer
Per-dispatch `model=` overrides (no new file): commit-changer propose=opus /
apply=sonnet (2 sites in /commit-change — precedence over the sonnet
frontmatter pin is the documented Agent-tool contract, verified direction
D0.2; the pin stays as the no-inherit fallback = today's behavior; both
call-site strings census-locked + W3 behavioral smoke); SDD
implementers+reviewers
sonnet (already prose-mandated → make it a census lock); code-review steps
(ship-feature 6, init-project 10) = opus explicit; client-handover-writer's 8
general-purpose skill-runners = fable; onboard's 7 general-purpose = opus
(keep); any Explore/Plan ad-hoc reflection dispatch = fable (doctrine line in
model-gate.md + CLAUDE.global routing note).
Splits (each = new agent file(s) + handoff contract + census + consumers):
S1 plugin-advisor → plugin-probe (SONNET wave 1; PHASE 1 CLI probes → PROBE
REPORT) + plugin-reasoner (opus; PHASE 2/2.5 scoring + reco → PLUGIN CHECK
block). PHASE 3-4 report+apply-gate HOISTED into ONE shared include
`lib/plugin-gate.md` (simplicity MINOR — doc-commit.md ×6 pattern, never
4 hand-copies), referenced by the 4 consumers (plugin-check, onboard
STEP 0, init-project STEP 0, ship-feature STEP 0) — main loop. The
pre-recommendation validation checkpoint (advisor :201-212, straddles the
seam, can skip PHASE 4) runs IN THE CONSUMER between the two dispatches
(correctness MINOR); its inputs (toggle-external availability,
project-signal presence) are PROBE REPORT fields. Handoff: PROBE REPORT
fields = plugin list, toggle state, profile, CLI/anim/monorepo/embedded
signals + checkpoint inputs (enumerate ALL PHASE-2-read fields).
S2 doc-syncer → doc-auditor (opus; STEP 3-4 drift + semantic analysis + A3
MINOR/SIGNIFICANT call w/ doc-shape.sh oracle → DRIFT REPORT [AUTO]/
[HUMAN] items) + doc-syncer (sonnet; render/patch half, keeps
PATCHED_FILES: grammar + BDR-022 bans). Validation gate stays in
DISPATCHER (/doc skill, orchestrator steps) — auto-mode flows: auditor →
dispatcher applies AUTO via doc-syncer → SIGNIFICANT escalates inline.
Consumers rerouted: /doc, onboard, + doc-commit steps in bugfix/hotfix/
feat/init-project(×2)/ship-feature (inline→dispatch conversion) +
scaffolder PHASE 6 (scaffolder DISPATCHES nothing — it has no Agent tool:
README bootstrap moves to init-project STEP 5b dispatch of doc-syncer).
PLUS (robustness MAJOR): rework `lib/doc-commit.md`'s in-thread contract
BEFORE converting any doc-commit site — it requires the orchestrator to
"hold the patch context" to compose the rc-0 CHANGE SUMMARY (the review
surface that replaced the removed MINOR gate). Dispatched doc-syncer adds
a `CHANGE SUMMARY` block to its report grammar (per patched file: what
changed and why, ≤1 line each); doc-commit.md's composer consumes THAT
instead of in-thread context; census-locks the new field + a planted-input
smoke proves the summary crosses the dispatch boundary.
S3 seo-analyzer → 2-WAY (simplicity MAJOR — 3-way was YAGNI while collector
and templater share the sonnet tier; commit-changer mode-precedent):
seo-worker (sonnet; `MODE: collect` = STEP 2-5 signals → SIGNALS file;
`MODE: template` = STEP 12-14 FIX BUNDLE + sentinel + SEO.md + envelope)
+ seo-judge (opus; STEP 6-11 sampling judgment, competitive, scoring /20,
trajectory, triage → FINDINGS+PLAN). Orchestrated by /seo at L1 (BDR-061
conserved: no Agent tool in either). Wave-2 option: carve `MODE: collect`
into a haiku file once proven — the mode boundary IS the future cut line.
HANDOFF (robustness MAJOR — freshness/atomicity): run-scoped paths
`.audit/seo-signals-<RUNID>.md` / `.audit/geo-signals-<RUNID>.md` —
`.audit/` is the GITIGNORED derived-artifact tree (confirmation MINOR,
LRN-124: a crash-stranded transient with scraped GSC/competitor content
must never be committable; `.claude/audits/` keeps only the SEO.md/GEO.md
deliverables). RUNID minted by the dispatcher per run, passed to every
stage; the file ENDS with `COLLECTION COMPLETE — RUNID: <id>` and the
judge FAILS CLOSED (report ERROR, never score) if the file is absent,
RUNID mismatches, or the completeness sentinel is missing; dispatcher
cleans the file post-run.
DISPATCHER CONTRACT (confirmation MAJOR — fail-closed at the judge must
not fail OPEN at the pipeline): on a judge ERROR the orchestrator
(/seo /geo /harden /onboard) STOPS — no template dispatch, no L1 apply —
surfaces the ERROR verbatim, retries ONCE with a fresh collect+judge,
then escalates to the human. A mute or ERROR judge is NEVER carried into
templating (verify-secure-loop discipline). This handler is part of the
W5 skill rewrites, census-locked.
Explicit field list per LRN-126 (STEP 1-2 business+tech context consumed
by ALL later steps — full enumeration REQUIRED before cutting).
seo-data.test.sh locks (fetch.sh wiring) move with the worker body —
update suite same commit.
S4 geo-analyzer → geo-worker (sonnet, 2 modes) + geo-judge (opus) — mirror of
S3 incl. run-scoped `.audit/geo-signals-<RUNID>.md` + the same dispatcher
ERROR contract. No cross-domain file share:
seo vs geo bodies genuinely diverge (different checks, scoring blocks,
envelopes) — that divergence, not LRN-125, is the reason.
S5 handover-doc-writer → handover-synthesizer (opus; STEP 9 memory-registry
load + STEP 10 phase clustering + STEP 12 6-chapter synthesis — STEP 9
allocated here, it feeds the synthesis; correctness MINOR) +
handover-renderer (sonnet; STEP 13-16 annex render, precheck apply,
deterministic gates, HTML/PDF). client-handover-writer dispatches
synthesizer then renderer; PACKAGE contract split per LRN-126
(re-enumerate DEPLOY_HINTS/--skip-seo class fields — the EXACT prior
failure). W4 MUST same-commit relock model-routing.test.sh:52-55 (the
handover-doc-writer name + dispatch-string locks break on the rename;
"make test green per wave" D4 invariant — correctness MINOR).
Tier-downs (no split): validator-analyzer opus→sonnet (deterministic
validators+tables). onboarder stays sonnet wave 1 (haiku candidate wave 2).
release-executor stays sonnet (NEED-DECISION valve).
Verifier: STAYS sonnet (approved — oracle-anchored gate).
## D2 — SKILL MAP (main loop = session model; every dispatch tier explicit)
Gated reflection skills (model-gate kept, 15):
- ship-feature / init-project: per the two example maps in the analysis file
(amendment section) + S1 gate hoist at STEP 0 + doc-commit conversions.
- feat: scope/plan/contract/loop = main; challenge 3× plan-challenger opus;
feater sonnet; verifier+security sonnet; doc-commit → doc-auditor opus +
doc-syncer sonnet dispatch; commit via /commit-change (propose opus / apply
sonnet).
- bugfix: investigation/diagnosis/contract = main (reflection); challenge
opus (3b); bugfixer sonnet; verifier+security sonnet; doc-commit as feat.
- hotfix: LOCATE + guard = main (logic); challenge opus when guard fires;
hotfixer sonnet; security gate sonnet (revert-not-loop conserved);
doc-commit as feat.
- analyze: analyzer INLINE = main loop (it IS the reflection) — unchanged.
- code-clean: PHASE 1 audit inline = main (audit judgment feeding a human
gate); code-cleaner sonnet PHASE 2 (hosts refactorer inline at SAME tier —
LRN-125 OK); re-audit sonnet inside executor.
- seo / geo: skill = orchestration + GATED arbitrage (main); pipeline
collector sonnet → judge opus → templater sonnet (L1 serial); appliers
hotfixer/feater sonnet at L1; build-verify inline.
- web-validate: validator-analyzer sonnet; hotfixer applier sonnet; loop main.
- harden: audit dispatch follows S3 narrow-scope path (seo-judge opus on
harden axes w/ collector reuse); direct-Edit apply stays inline (tiny
scope, BDR-061 carve-out conserved).
- audit-delta: axis audits dispatched opus (delta judgment); security-auditor
sonnet; fix gate + markers = main.
- tour: orchestration main; security-auditor sonnet; cleanup audit = analyzer
opus (or general-purpose model="opus"); fixes via sonnet appliers; doc axis
→ S2 pipeline; reconcile axis = deterministic bash (main).
- onboard: onboarder DISPATCHED sonnet (was inline); plugin S1 pipeline;
analyzer opus; general-purpose audits model="opus" (kept); seo/geo → S3/S4
pipelines; security-auditor + doc pipeline as above; synthesis
general-purpose model="opus"; backlog arbitration = main.
- client-handover: writer INLINE (orchestrator, main); its 8 skill-runner
children model="fable"; handover S5 split (synth opus → render sonnet);
gates all main.
Excluded-from-gate skills (5, stay ungated): commit-change (propose opus /
apply sonnet via model=; approval gates main); doc (S2: auditor opus →
gate main → patcher sonnet); status (haiku); release-candidate (executor
sonnet; version/when/push decisions main); refactor (refactorer sonnet).
Memory/util skills (capitalize, close, prune-memory, reconcile, learn,
profile, skills-perso, gitflow, deploy, plugin-check(S1), status): main
loop by nature (conversation context, human gates, deterministic bash) —
no dispatch changes except plugin-check S1.
External/gstack skills (graphify, design-*, review, qa, ship, investigate…):
NOT edited (external ownership, BDR-015 class) — covered by doctrine line;
local wrapper skills only if locally owned. Verify ownership per file
before touching (symlink → skip).
## D3 — WAVES (each = gitflow feature branch, tests green, census extended)
W0 SEQUENCING: merge `feature/opus-pin-audit-agents` → develop (baseline,
human gate). `bugfix/seo-geo-integrity` is ALREADY MERGED (correctness
MAJOR — the TODO.md "UNMERGED" note was stale; verified `92301fe` is an
ancestor of develop AND this branch): no arbitrage, no W5 wait — one-line
ancestry re-check in W0 + fix the stale TODO.md entry (reconcile-class
correction). Absorb the analysis+plan files into the new feature branch.
W1 NO-INHERIT ENFORCEMENT (small, high-value): code-review model= opus
(ship-feature 6, init-project 10); client-handover-writer 8× model="fable";
doctrine line in model-gate.md + census locks (`model="fable"`,
`model=` presence per site); SDD sonnet prose → census lock. Prose sweep
of stale BDR-066/076 claims touched by W1.
W2 INLINE→DISPATCH CONVERSIONS: scaffolder (init 5 — liveness pings move to
orchestrator; scaffolder loses PHASE 6 inline-load → init 5b owns README
via S2), onboarder (onboard), doc-commit steps ×5 flows → S2 pipeline
(gate hoist FIRST: /doc + flows own the validation gate; doc-syncer body
loses its inline gate → census re-lock), S1 plugin split + gate hoist ×4
consumers. Data-flow pass per LRN-126 on each (fields enumerated in the
wave's contract file before edits).
W3 TIER MOVES: validator-analyzer → sonnet (pin + prose + census flip);
commit-changer per-mode model= (2 sites + prose + census).
W4 S5 handover split (synth opus / render sonnet) + PACKAGE re-enumeration.
W5 S3/S4 seo/geo pipelines: worker(2-mode)/judge ×2, /seo /geo /harden
/onboard rerouted, seo-data.test.sh moved locks, run-scoped signals
handoff (RUNID + completeness sentinel + fail-closed judge),
envelope/sentinel/score grammars verbatim, COVERAGE lines preserved.
W6 DOCTRINE + CLOSE-OUT: model-gate.md rewrite (protects main loop; tier
table; fable-dispatch doctrine), challenge-plan.md + plan-challenger
ORCHESTRATOR PROTOCOL text (keep BDR-066+BDR-076 tokens per census, add
BDR-077), census consolidation (model-routing new sections; every new
agent: YAML G3, pin lock, dispatch-string lock, AskUserQuestion/Agent
bans), LRN-113 whole-surface prose sweep (~30 refs list in analysis),
BDR-077 + LRN entries + journal, EVAL-023-style post-merge ronde.
Per-split planted-input smokes run IN their own waves (W2/W4/W5, merge
gates) — W6 is the consolidated ronde only, never the first empirical
proof of a split.
## D4 — ZERO-REGRESSION PROTOCOL (every wave)
- Before edits: wave contract file (.claude/tasks/contracts/) with FILE SCOPE
+ acceptance criteria; challenge-plan on THIS plan (done once, below);
verify-secure-loop on each wave's diff (verifier sonnet + security sonnet).
- Grammar diff-guard: `grep -F` each verbatim marker (analysis §CONSTRAINTS
list) pre/post per wave — zero drift.
- Census: flip-test every NEW lock (plant violation → RED) before trusting.
- `make test` green per wave; no wave merges without human signal (gitflow).
- Rollback story (robustness MINOR — waves are textually interdependent, an
early wave is NOT independently revertible after later merges): revert in
REVERSE merge order, or revert the whole stack; never a mid-stack single
revert. Pre-merge, the rollback unit is the wave branch.
## CHALLENGE LOG (2026-07-19 — 3 blind lenses on plan v1)
- correctness: CONCERNS(2) — seo-geo-integrity phantom sequencing (fixed W0);
commit-changer precedence ambiguity (fixed D1 + D0.2 citation + W3 smoke);
3 MINORs (S5 STEP 9 + W4 relock; plugin checkpoint seam; templater label)
— all fixed in place.
- robustness: FATAL(4) — BLOCKER fable-dispatch unverified → CLOSED by spike
(D0.2: resolves to claude-fable-5, enum-validated, loud failure); doc-commit
in-thread contract (fixed S2: CHANGE SUMMARY crosses the report grammar);
plugin-probe haiku contradiction (fixed: sonnet wave 1); signals handoff
freshness (fixed S3: RUNID + sentinel + fail-closed); rollback claim
(fixed D4).
- simplicity: CONCERNS(1) — 3-way seo/geo YAGNI → 2-way worker/judge (fixed
S3/S4); twin-templater share (dissolved by 2-way; divergence stated);
plugin gate ×4 copies → lib/plugin-gate.md include (fixed S1).
## EXECUTION NOTES (2026-07-19 — as-built deviations, all justified in-commit)
- S2/S3/S4/S5 shipped MODE-BASED (one agent, modes + call-site `model=`)
instead of file splits — the challenge's own commit-changer precedent
generalized; locks and body text stayed in place (LRN-137). plugin S1
kept the `plugin-advisor` NAME for the reasoner (repinned opus) — only
plugin-probe is a new file.
- seo/geo keep the OPUS pin (not sonnet+judge-override): fail-safe
direction — a forgotten override over-tiers, never downgrades. /harden
narrow-scope + /onboard report-only keep legacy no-MODE single-shot on
that pin.
- W0's seo-geo-integrity arbitrage was phantom (branch already merged) —
TODO.md corrected instead.
- Per-wave smokes ran in-wave as merge gates (confirmation-pass fix) —
all PASSED, disk-verified. Registry note: a NEW subagent_type registers
at next session start; typed resolution re-checked post-restart before
the W2 merge.
## CHALLENGE LOG (final)
- Confirmation pass (fresh robustness challenger on v2): CONCERNS(2) — v1
fixes HOLD (doc-commit CHANGE SUMMARY, plugin-probe sonnet, rollback order,
fable spike, RUNID); 2 new MAJORs + 1 MINOR opened by the revisions, all
fixed in v3: (a) per-split planted-input smokes moved IN-WAVE as merge
gates (W6 = ronde only); (b) dispatcher ERROR contract on judge failure
(STOP, no templating/apply, retry once, escalate — pipeline never fails
open); (c) transient signals files relocated to gitignored `.audit/`
(LRN-124). Protocol cap reached (1 re-challenge) → to the human gate.
+33
View File
@@ -0,0 +1,33 @@
# Architecture — claude-config
Repo layout and structural principles. Command workflows live in
[`USAGE.md`](./USAGE.md); version history in [`CHANGELOG.md`](./CHANGELOG.md).
## Project layout
```
claude-config/
├── CLAUDE.global.md # Global coding preferences — deployed as ~/.claude/CLAUDE.md
├── CLAUDE.md # Project-scope instructions (this repo only)
├── settings.json # Global permissions (deny / ask / allow rules)
├── install.sh # Bootstrap: Claude Code CLI + auth + submodules + link + plugins
├── install-plugins.sh # One-shot installer: prerequisites + all plugins
├── link.sh # Symlinks this repo into ~/.claude/
├── doctor.sh # Setup diagnostic
├── update-all.sh # One-command update for all components
├── Makefile # Unified entry point: make install / doctor / update
├── plugins.lock.json # Version pinning for non-marketplace dependencies
├── hooks/ # Session start, statusline, RTK rewrite + ctx7 + design-toolchain reminders
├── agents/ # Execution units called by skills (never invoked directly)
├── skills/ # Entry points invoked via /skill-name
├── skills-external/ # Vendored skill packs (gstack submodule + installer-fetched design packs)
├── templates/ # Per-project templates (CLAUDE.md, settings, memory registries, deploy runbook, gitignore)
└── lib/ # Shared shell libs (gitflow, profiles, commit helpers, archetypes, tests)
```
## Architecture principles
- `skills/` = entry points you invoke via `/skill-name`
- `agents/` = execution units called by skills (never invoked directly by user)
- `templates/` = symlinked to `~/.claude/templates/` — copy into projects via `/onboard` or manually
- **Graphify** builds a knowledge graph of any codebase (`/graphify query`), producing a navigable wiki in `graphify-out/wiki/`. This map helps Claude understand project structure, find relevant code faster, and reason across files. Essential for large-scope tasks (multi-file features, complex bugs, architectural changes). Small tasks should skip it and read files directly.
+77
View File
@@ -6,6 +6,83 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
## [Unreleased] ## [Unreleased]
## [1.4.0] — 2026-07-22
### Added
- **Transient planning artifacts auto-purged at feature-finish (BDR-065)** —
`gitflow finish` on a `feature`/`bugfix` branch now removes the run-time
superpowers artifacts (`docs/superpowers/{specs,plans}`) on the working
branch just before the directed merge, so `develop`'s tip lands clean while
the feature commits stay reachable as the archive (`git show <sha>:…`). This
automates the manual post-merge cleanup that BDR-065 had left as doctrine —
the step that slipped in 1.3.0 and needed a hand purge. Best-effort by
contract: a purge that finds nothing, meets uncommitted changes under those
paths, or fails to commit never aborts the finish (index/tree restored); opt
out with `GITFLOW_PURGE_TRANSIENT=0`. New `gitflow.sh purge-transient` verb.
`.claude/tasks/{contracts,plans}` are deliberately out of scope (durable,
versioned, referenced by the decision registry). Live in every project via
the `~/.claude/lib` symlink; covered by `lib/gitflow-test.sh` T17 (a–d).
### Changed
- **Bug routing inverted: `/bugfix` primary, `/investigate` explicit-only
(BDR-080)** — a bug / error / 500 now routes to `/bugfix` by default (the
full framework: gitflow, contract, fresh verifier + security gates,
registries). The gstack `/investigate` monolith — its own `~/.gstack`
memory, no gitflow or gates — is reserved for explicit requests
(cross-project learnings, `/freeze` scope lock, long investigation with no
immediate commit intent). Same core debugging doctrine, incompatible
wrappers; the default now favours the gated, integrated path.
## [1.3.1] — 2026-07-20
### Changed
- **README rebuilt around a short pitch** — new top half: what it is / how
it works / why it's good in ~60 lines (skills = entry points, agents =
model-tiered execution units, hooks = deterministic guardrails,
templates/memory = compounding per-project registries); all previous
content demoted to an explicit reference-manual half below a separator.
Deduplicated in the process: old title/tagline, Overview prose and the
duplicated fresh-install block removed (unique install notes kept under
a new "Install notes" section); hardcoded version number dropped from
the footer (staleness risk). Docs-only release — no code change.
## [1.3.0] — 2026-07-20
### Added
- **Profile switches now toggle external packs and MCPs both ways (BDR-079)** — `profile.sh set` was asymmetric: it enabled what a profile listed (including gstack skills on demand when the whole pack is off, and the `magic` MCP) but never disabled the managed leftovers, so `set backend` after design work kept emil-design-eng / frontend-design / design-motion-principles / impeccable active and magic registered. `set` now trims managed externals (`MANAGED_EXTERNALS`) and managed MCPs (`MANAGED_MCPS`, delegated to `toggle-external.sh`) not listed in the profile — same allowlist doctrine as `MANAGED_PLUGINS`, nothing outside the allowlists is ever auto-touched (darwin-skill stays manual). Also: an `external` entry whose symlink never existed is now created from `skills-external/` (mirroring toggle-external's from-source path), and the stale "NOT toggled automatically" note in `profile.sh` usage was corrected. Covered by a hermetic 16-check test (`lib/tests/profile-set-managed.test.sh`) with a fake `claude` shim.
### Changed
- **README restructured for public readers** — the project-layout tree and architecture principles moved verbatim to a new `ARCHITECTURE.md` (README links it); bare decision-registry citations (`BDR-XXX`) stripped from README prose, meaning preserved; `/profile` documentation corrected in three places to the real 10-profile set (web / seo / web-full / full / backend / design / dev / qa / audit / minimal); fresh-install block now uses the real clone URL + `make install` / `make doctor`; new "SEO data layer" subsection documents the `GOOGLE_OAUTH_CLIENT_ID` / `GOOGLE_OAUTH_CLIENT_SECRET` / `CRUX_API_KEY` vars in `~/.claude/.env` (mirrors `.env.example`, `make seo-connect` one-time consent).
### Fixed
- **Transient planning artifacts purged from the repo** — `docs/plans`, `docs/specs`, `docs/superpowers/{plans,specs}` (deploy-skill 2026-06-27, model-routing 2026-07-15) were run-time pipeline artifacts that should have been deleted in their chantiers' post-merge cleanup and slipped through (one pair predates the lifecycle rule, one missed the purge step of a 6-wave chantier). Git history at the feature commits remains their archive; `docs/` no longer exists.
## [1.2.1] — 2026-07-20
### Fixed
- **README caught up with the code it describes** — the "Agent model routing" section still presented the BDR-066 v1 scheme (7 rows factually wrong after model-tiering v2): reframed to the BDR-076/077 4-tier table verified against agent frontmatters (opus-pinned judgment agents, per-mode splits for doc-syncer / handover-doc-writer / seo-geo pipelines, plugin-probe added, unpinned inline agents listed as such). Also: Context7 paragraph rewritten to the two-surface model (find-docs = sole doc-fetch surface, `ctx7-reminder` hook = scoped session nudge, BDR-078), `hooks/` tree line now mentions the ctx7 reminder, and the `/ship-feature` workflow block gained its STEP 2b (adversarial plan-challenge) line. Docs-only release — no code change.
## [1.2.0] — 2026-07-20
### Added
- **ctx7 coverage extension (BDR-078)** — the "consult current docs before coding against a fast-moving lib" doctrine now covers every code path, not just the two big pipelines. (1) `lib/fast-libs.sh`: single source of truth for fast-lib detection (`detect` / `cache-status` verbs; JS package.json anchored keys + Python requirements/pyproject; 7-day `.ctx7-cache/` freshness; locale-independent sort), replacing three hardcoded lists (`/ship-feature` STEP 0c, `/init-project` STEP 5c, `/onboard` STEP 3.5). (2) `hooks/ctx7-reminder.sh`: once-per-session UserPromptSubmit nudge when the project carries fast-libs and the cache is missing/stale — closes the ad-hoc-coding gap. (3) find-docs description extended with a before-writing-code trigger + a cache-first rule (read fresh cache, tee fetched docs back into it). (4) feater/bugfixer executor briefs gain the fast-lib docs rule (read fresh cache, else 2-topic `npx ctx7@latest` fetch, else report `ctx7 cache miss` and proceed). Second deliberate ctx7 surface — a scoped refinement of BDR-053's single-surface rule, not a reversal.
- **Adversarial plan-challenge phase** — reflection orchestrators now run a blind 3-lens challenge (correctness / robustness / simplicity) via a dedicated `plan-challenger` agent before implementation; severity-driven (a single-lens BLOCKER stops the plan), report-only. `/hotfix` joins behind a logic-only guard: cosmetic fixes skip it, logic fixes get challenged, a BLOCKER reroutes to `/bugfix` (BDR-075).
- **seo-data engine: measured coverage + new verbs** — the `/seo` FULL audit measures instead of feeling: `sitemap` verb gives COVERAGE a real denominator (source/live split); internal-link graph computes orphan pages + click depth; cannibalisation detected from GSC's own query data; `rich_results` surfaced from URL Inspection data already fetched; `sameAs` profiles actually resolved; `schema_gen` generates JSON-LD instead of only auditing it; `content_quality` runs a deterministic filler/AI-slop scan; the axis score is computed, not felt; `drift` baseline reports regressions vs changes. SPA pages: the audit refuses to score what JS paints instead of scoring the empty shell (no Playwright dependency). Common Crawl backlinks were measured (17 GB edges file) and killed as a source — the Off-page axis stays scoped to what is actually measured.
### Changed
- **Model-tiering v2: 4-tier explicit routing (BDR-076/077)** — the session model (Fable) does main-loop reflection/orchestration only; every dispatched subagent is explicitly tiered: judgment agents pinned opus (analyzer, plan-challenger, seo/geo audit agents…), mechanical executors sonnet, skill-runner children fable — nothing inherits silently. Mode-based splits so pins take effect: doc-syncer audit(opus)/patch(sonnet), handover-doc-writer synthesize(opus)/render(sonnet), seo/geo collect(sonnet)/judge(opus, fail-closed)/template(sonnet), plugin gate split probe(sonnet)/advisor(opus). Census locks (125) + per-wave planted-input smokes.
- **config-protection edit-block guardrail removed** (BDR-074) — the hook blocked more than it protected; deny-list design pass recorded in BDR-069.
- graphify vendored skill dist synced 0.9.6 → 0.9.15.
### Fixed
- **seo/geo integrity pass (I1–I8)** — Off-page axis scoped to measured data only; VSI (an SEO-blog fiction) removed from CWV thresholds; NAP direction rule ported into geo-analyzer (standalone `/geo` can no longer write unverified NAP); security headers no longer double-counted (`/harden` owns them); sampling coverage disclosed instead of implied; stats reattached to the claims they support; phantom audit precondition dropped. Plus two real bugs caught by a second-site backtest and two process anomalies from live dogfooding.
- `settings.json` Write() deny rules were inert — converted to Edit() rules, closing the write hole they left open.
- Model-routing W6 ronde: 6 findings closed (README bootstrap path, 2 census gaps, 3 stale refs).
### Security
- **`safe_fetch` resolve-then-pin** in `lib/seo-data` — DNS-rebinding closed on audit fetches: the audited host is resolved once, validated, then pinned for the actual fetch.
- **`url-guard`** — shell-injection + local-target refusal before any user-supplied or sitemap-crawled URL reaches curl (SSRF guard on the seo/geo fetch paths).
## [1.1.0] — 2026-07-16 ## [1.1.0] — 2026-07-16
### Added ### Added
+4 -1
View File
@@ -252,7 +252,10 @@ description fits (full list is in context). Rules below cover only the
non-obvious cases: gstack fallbacks, disambiguation, cryptic names. non-obvious cases: gstack fallbacks, disambiguation, cryptic names.
- Product idea, "worth building?" → office-hours - Product idea, "worth building?" → office-hours
- Bug / error / 500 → investigate (bugfix if gstack off) - Bug / error / 500 → bugfix (full framework: gitflow, contract, fresh
verifier/security gates, registries). investigate ONLY on explicit ask
for the gstack ecosystem (cross-project learnings, /freeze scope lock,
long investigation with no immediate commit intent)
- feat / hotfix / bugfix distinguished by file count → see descriptions - feat / hotfix / bugfix distinguished by file count → see descriptions
- Ship / deploy / PR → ship (ship-feature if gstack off) - Ship / deploy / PR → ship (ship-feature if gstack off)
- Cut a release / tag a version (develop ahead of main) → release-candidate - Cut a release / tag a version (develop ahead of main) → release-candidate
+10 -5
View File
@@ -32,8 +32,13 @@ or re-run `make plugin`.
`docs/superpowers/specs/**` and `docs/superpowers/plans/**` are run-time `docs/superpowers/specs/**` and `docs/superpowers/plans/**` are run-time
artifacts of a feature pipeline (subagent briefs, reviewer references). artifacts of a feature pipeline (subagent briefs, reviewer references).
They are committed DURING the run and DELETED in the post-merge cleanup They are committed DURING the run (the SDD worktree + reviewers read them
(BDR-065) — git history at the feature commits is their archive. Durable from disk — NOT gitignored), then AUTO-PURGED by `gitflow finish` on a
knowledge goes to `.claude/memory/` registries, never to these files. `feature`/`bugfix` branch, before the merge, so develop's tip stays clean
Derived scan/audit outputs (`.audit/**`) are gitignored and never (BDR-065, `lib/gitflow.sh` `_gitflow_purge_transient`). The feature commits
committed, even redacted (LRN-124). stay reachable from develop, so `git show <sha>:docs/…` is still the archive.
Opt out with `GITFLOW_PURGE_TRANSIENT=0`. NOT in scope: `.claude/tasks/{contracts,plans}`
(durable, versioned, referenced by decisions.md). Durable knowledge goes to
`.claude/memory/` registries, never to these files. Derived scan/audit
outputs (`.audit/**`) are gitignored and never committed, even redacted
(LRN-124).
+114 -68
View File
@@ -1,91 +1,110 @@
# claude-config # claude-config
Global Claude Code configuration — agents, skills, plugins, and project templates. One repo that turns Claude Code into a reproducible engineering system —
skills, agents, hooks, plugins, and per-project memory, versioned and
symlinked into `~/.claude/`. Clone it on any machine, run one command,
and every project gets the same assistant with the same rules.
> **Guide d'utilisation complet :** voir [`USAGE.md`](./USAGE.md) — workflows typiques, exemples par type de projet, arbre de décision "quel skill utiliser ?". ## What it is
> **Historique des versions :** voir [`CHANGELOG.md`](./CHANGELOG.md).
Not a collection of prompts — an operating layer on top of Claude Code:
- **Skills** (`/feat`, `/bugfix`, `/ship-feature`, `/seo`, `/tour`…) are the
entry points: each one encodes a complete workflow, from quick fix to
full feature pipeline with validation gates.
- **Agents** are the execution units skills dispatch to — each pinned to
the cheapest model that can do the job (haiku collects, sonnet executes,
opus judges, the session model only reflects).
- **Hooks and permissions** are deterministic guardrails: gitflow enforced
by a pre-commit hook, deny-first permission rules, secrets kept in
`~/.claude/.env` and never in config files.
- **Templates and memory** seed every project with persistent registries
(decisions, learnings, blockers) — what a session learns, the next
session knows.
## How it works
```bash
git clone --recurse-submodules https://github.com/bchanot/claude
cd claude
make install # CLI + auth + symlinks + plugins (pinned in plugins.lock.json)
make doctor # verify everything
```
`link.sh` symlinks the repo into `~/.claude/`, so editing here updates the
live config — and `git log` is the audit trail of your entire setup.
Day to day:
```bash
/onboard # bring an existing repo into the framework
/ship-feature "…" # brainstorm → plan → adversarial challenge → TDD → review → merge
/feat "…" # same idea, 1-5 files, no ceremony
/close # flush decisions and learnings to memory before quitting
make update # keep CLI, plugins, and submodules current
```
## Why it's good
- **Reproducible.** One clone rebuilds the whole environment; versions are
locked, `make doctor` proves it works.
- **Cost-shaped.** Model tiering routes reflection to the big model and
execution to cheap ones — the expensive context does only what it must.
- **Safe by default.** Protected branches, ask-before-run on risky tools,
parameterized secrets: the guardrails are code, not good intentions.
- **It compounds.** Memory registries, audit skills, and doc-sync keep every
project's knowledge growing across sessions instead of evaporating.
--- ---
## Overview Everything below is the reference manual — model routing, components,
commands, settings, secrets, maintenance.
This repo is your personal Claude Code setup, versioned and reproducible across machines. ---
``` ## Agent model routing (model-tiering v2)
claude-config/
├── CLAUDE.global.md # Global coding preferences — deployed as ~/.claude/CLAUDE.md
├── CLAUDE.md # Project-scope instructions (this repo only)
├── settings.json # Global permissions (deny / ask / allow rules)
├── install.sh # Bootstrap: Claude Code CLI + auth + submodules + link + plugins
├── install-plugins.sh # One-shot installer: prerequisites + all plugins
├── link.sh # Symlinks this repo into ~/.claude/
├── doctor.sh # Setup diagnostic
├── update-all.sh # One-command update for all components
├── Makefile # Unified entry point: make install / doctor / update
├── plugins.lock.json # Version pinning for non-marketplace dependencies
├── hooks/ # Session start, statusline, RTK rewrite, config-protection + design-toolchain guards
├── agents/ # Execution units called by skills (never invoked directly)
├── skills/ # Entry points invoked via /skill-name
├── skills-external/ # Vendored skill packs (gstack submodule + installer-fetched design packs)
├── templates/ # Per-project templates (CLAUDE.md, settings, memory registries, deploy runbook, gitignore)
└── lib/ # Shared shell libs (gitflow, profiles, commit helpers, archetypes, tests)
```
**Architecture principle:** Doctrine: the session model (Fable) does main-loop reflection ONLY —
- `skills/` = entry points you invoke via `/skill-name` brainstorm, plan, contract, audit judgment, gates, loop decisions — enforced
- `agents/` = execution units called by skills (never invoked directly by user) by a blocking gate (`lib/model-gate.md` + `lib/model-check.sh`) at the entry
- `templates/` = symlinked to `~/.claude/templates/` — copy into projects via `/onboard` or manually of the 13 reflection orchestrators. Nothing dispatched inherits silently:
- **Graphify** builds a knowledge graph of any codebase (`/graphify query`), producing a navigable wiki in `graphify-out/wiki/`. This map helps Claude understand project structure, find relevant code faster, and reason across files. Essential for large-scope tasks (multi-file features, complex bugs, architectural changes). Small tasks should skip it and read files directly. typed agents carry a frontmatter pin, built-ins get an explicit `model=` at
every call site.
### Agent model routing (BDR-066)
Reflection (brainstorm, plan, contract, audit judgment, loop decisions) runs
INLINE on the session model — assumed Fable/Opus, enforced by a blocking
gate (`lib/model-gate.md` + `lib/model-check.sh`) at the entry of the 13
reflection orchestrators. Execution runs on pinned subagents:
| Agent | Model | Tier | | Agent | Model | Tier |
|---|---|---| |---|---|---|
| feater, hotfixer, bugfixer | sonnet (pinned) | executors — code from a closed plan (feat), fix from a closed diagnosis (bugfix), fix-bundle appliers | | feater, hotfixer, bugfixer | sonnet (pinned) | executors — code from a closed plan (feat), fix from a closed diagnosis (bugfix), fix-bundle appliers |
| verifier, security-auditor | sonnet (pinned) | fresh gates (≤3×/loop) | | verifier, security-auditor | sonnet (pinned) | fresh gates (≤3×/loop) |
| commit-changer, release-executor, code-cleaner | sonnet (pinned) | dispatched execution — grouping+commit / release spans / approved cleanup (the audit + approval gate stay in the dispatcher) | | commit-changer, release-executor, code-cleaner | sonnet (pinned) | dispatched execution — grouping+commit / release spans / approved cleanup (audit + approval gates stay in the dispatcher) |
| doc-syncer, onboarder, scaffolder, refactorer, interviewer, plugin-advisor | sonnet (pinned) | workers | | onboarder, scaffolder, refactorer, validator-analyzer, plugin-probe | sonnet (pinned) | workers — config generation, scaffold, refactor, deterministic W3C/WCAG runner, mechanical plugin probe |
| status-reporter | haiku (pinned) | mechanical collector | | status-reporter | haiku (pinned) | mechanical collector |
| handover-doc-writer | sonnet (pinned) | deliverable writer — synthesizes + renders the client doc from a resolved PACKAGE (dispatched by client-handover) | | analyzer, plan-challenger, plugin-advisor | opus (pinned) | dispatched judgment — pre-plan analysis, 3-lens adversarial plan challenge (`/ship-feature` STEP 2b), plugin-fit reasoning |
| analyzer, seo-analyzer, geo-analyzer, validator-analyzer, client-handover-writer | inherit session (Fable/Opus) | reflection / audit / inline playbooks / ship-and-handover pipeline | | seo-analyzer, geo-analyzer | opus pin (judge mode); collect/template spans dispatched `model="sonnet"` | 3-mode audit pipelines — judgment fail-closed on opus, mechanical collect + templating on sonnet |
| doc-syncer | sonnet pin; audit mode dispatched `model="opus"` | two-mode: audit (drift judgment, opus) / patch (mechanical apply, sonnet) |
| handover-doc-writer | sonnet pin; synthesize mode dispatched `model="opus"` | two-mode: synthesize (opus) / render (sonnet) — client deliverable |
| interviewer, client-handover-writer | unpinned (inline-load = session model) | they ARE the main loop — a frontmatter pin would be inert |
| Explore (built-in) | inherit session (Fable/Opus) | search feeds reflection — kept on the big model, not pinned down | | Explore (built-in) | inherit session (Fable/Opus) | search feeds reflection — kept on the big model, not pinned down |
The pure-execution skills `/doc`, `/status`, `/commit-change`, The pure-execution skills `/doc`, `/status`, `/commit-change`,
`/release-candidate` **dispatch** their agent (instead of inline-loading it) `/release-candidate` **dispatch** their agent (instead of inline-loading it)
so the pin takes effect and the work leaves the big session model; `/hotfix` so the pin takes effect and the work leaves the big session model; `/hotfix`
was split like `/feat` (reflection inline + gate, `hotfixer` executor) and so was split like `/feat` (reflection inline + gate, `hotfixer` executor) and so
joins the gated group (13th). joins the gated group (13th); `/client-handover`'s nested skill-runner
children are dispatched `model:"fable"` (they carry reflection).
--- ---
## Fresh install (new machine) ## Install notes
```bash
# 1. Clone with submodules
git clone --recurse-submodules git@github.com:youruser/claude-config.git
cd claude-config
# 2. Bootstrap (CLI + auth + symlinks + plugins)
bash install.sh
# 3. Verify setup
bash doctor.sh
# 4. Restart Claude Code — plugins load automatically
```
All scripts use their own location to find the repo — run them from anywhere. All scripts use their own location to find the repo — run them from anywhere.
The plugins step logs to `install-YYYYMMDD-HHMMSS.log`. The plugins step logs to `install-YYYYMMDD-HHMMSS.log`.
**Optional — Context7** (fast doc lookup for React / Next.js / Prisma…): the plugins **Optional — Context7** (fast doc lookup for React / Next.js / Prisma…): the plugins
step installs the `ctx7` CLI and wires it into Claude Code itself — single surface = step installs the `ctx7` CLI and wires it into Claude Code. The doc-fetch surface is
the `find-docs` skill; the generated `rules/context7.md` is purged by design the `find-docs` skill alone (the generated `rules/context7.md` is purged by
(BDR-053). If you run `ctx7 setup` manually, delete that rule or re-run `make plugin`. design; if you run `ctx7 setup` manually, delete that rule or re-run `make plugin`).
A once-per-session `ctx7-reminder` hook nudges toward it when the current project
carries fast-moving libs (`lib/fast-libs.sh`) — a scoped second surface, a
refinement of the single-surface rule, not a reversal.
```bash ```bash
ctx7 login # optional: OAuth / API key for higher rate limits ctx7 login # optional: OAuth / API key for higher rate limits
@@ -151,7 +170,7 @@ a different package, ships its own conflicting `graphify` bin) — see
| `/web-validate` | W3C HTML/CSS validity + WCAG 2.1 accessibility audit | | `/web-validate` | W3C HTML/CSS validity + WCAG 2.1 accessibility audit |
| `/geo` | GEO-only audit — AI-search visibility (ChatGPT, Perplexity, Claude, Gemini…) | | `/geo` | GEO-only audit — AI-search visibility (ChatGPT, Perplexity, Claude, Gemini…) |
| `/client-handover` | Final project delivery — audits + branded deliverable (Markdown / HTML / PDF) | | `/client-handover` | Final project delivery — audits + branded deliverable (Markdown / HTML / PDF) |
| `/profile` | Activate a skill profile (design / dev / qa / audit / minimal) | | `/profile` | Activate a skill profile (web / seo / web-full / full / backend / design / dev / qa / audit / minimal) |
| `/tour` | Grouped all-axes sweep — cleanup + security + reconcile + doc, fix and loop until clean | | `/tour` | Grouped all-axes sweep — cleanup + security + reconcile + doc, fix and loop until clean |
> This table lists personal skills. Gstack skills (investigate, review, retro, > This table lists personal skills. Gstack skills (investigate, review, retro,
@@ -185,6 +204,7 @@ cd my-existing-project/
/ship-feature "feature description" /ship-feature "feature description"
# → STEP 0: plugin check # → STEP 0: plugin check
# → STEP 1-2: brainstorm + plan (superpowers) # → STEP 1-2: brainstorm + plan (superpowers)
# → STEP 2b: adversarial plan-challenge (3 lenses, report-only)
# → STEP 3: validation gate — user approval required # → STEP 3: validation gate — user approval required
# → STEP 4-7: implement (TDD) → review → capitalize (memory) # → STEP 4-7: implement (TDD) → review → capitalize (memory)
# → STEP 8: sync README (doc-sync) # → STEP 8: sync README (doc-sync)
@@ -226,17 +246,15 @@ See [`templates/settings/SETTINGS.md`](templates/settings/SETTINGS.md) for the f
`~/.claude.json` (or the project's `.mcp.json`) — if you pass the real secret `~/.claude.json` (or the project's `.mcp.json`) — if you pass the real secret
on that command line, it materializes as a second plaintext copy outside on that command line, it materializes as a second plaintext copy outside
`~/.claude/.env`, invisible to the repo's `.gitignore`/allowlist reach (this `~/.claude/.env`, invisible to the repo's `.gitignore`/allowlist reach (this
bit us once: job7/BDR-026). bit us once).
Claude Code expands `${VAR}` and `${VAR:-default}` in `mcpServers` config — Claude Code expands `${VAR}` and `${VAR:-default}` in `mcpServers` config —
in `env`, `command`, `args`, `url`, and `headers` — for both project (`.mcp.json`) in `env`, `command`, `args`, `url`, and `headers` — for both project (`.mcp.json`)
and user (`~/.claude.json`) scope. Use that instead of a literal value: and user (`~/.claude.json`) scope. Use that instead of a literal value:
```bash ```bash
# WRONG — plaintext key lands in ~/.claude.json: MAGIC_API_KEY=<Enter your magic api key here from https://21st.dev/settings/api-keys >
claude mcp add magic --scope user --env API_KEY="$MAGIC_API_KEY" -- npx -y @21st-dev/magic@latest # single-quoted so bash doesn't expand it; Claude Code expands it at
# RIGHT — single-quoted so bash doesn't expand it; Claude Code expands it at
# launch, reading the var from its own process environment: # launch, reading the var from its own process environment:
claude mcp add magic --scope user --env 'API_KEY=${MAGIC_API_KEY}' -- npx -y @21st-dev/magic@latest claude mcp add magic --scope user --env 'API_KEY=${MAGIC_API_KEY}' -- npx -y @21st-dev/magic@latest
``` ```
@@ -254,6 +272,26 @@ There is no `claude mcp add` flag that writes the reference form for you —
the `${VAR}` syntax has to be typed by hand (or via a wrapper script), same as the `${VAR}` syntax has to be typed by hand (or via a wrapper script), same as
above. above.
### SEO data layer (`/seo` FULL) — Google OAuth + CrUX keys
The same `~/.claude/.env` also feeds `lib/seo-data`, which pulls real Google
Search Console and Chrome UX Report data into `/seo` FULL audits. Add these
three vars (template with the GCP console steps in `.env.example`):
```bash
# OAuth Desktop client — GCP console → APIs & Services → Credentials →
# OAuth client (Desktop). Consent scope: webmasters.readonly only.
GOOGLE_OAUTH_CLIENT_ID=<your-client-id.apps.googleusercontent.com>
GOOGLE_OAUTH_CLIENT_SECRET=<your-client-secret>
# CrUX + PageSpeed API key — GCP console → Credentials → API key,
# restricted to those two APIs. https://developer.chrome.com/docs/crux/api
CRUX_API_KEY=<your-crux-api-key>
```
Then run the one-time consent flow: `make seo-connect` (per-label token
store, multi-site safe). Missing credentials never break an audit — `/seo`
degrades gracefully to anonymous PageSpeed lab data.
### magic MCP (`@21st-dev/magic`) — known callback-injection risk ### magic MCP (`@21st-dev/magic`) — known callback-injection risk
`21st_magic_component_builder` opens an **unauthenticated** local callback `21st_magic_component_builder` opens an **unauthenticated** local callback
@@ -263,7 +301,7 @@ can `POST` to it and that body is injected **verbatim** into the tool result
the model consumes (job8 audit, `dist/utils/callback-server.js:36`). This is the model consumes (job8 audit, `dist/utils/callback-server.js:36`). This is
in the third-party package's code, not this repo's config — **we don't patch in the third-party package's code, not this repo's config — **we don't patch
it**. The mitigation lives entirely on our side: `settings.json` it**. The mitigation lives entirely on our side: `settings.json`
`permissions.ask` explicitly lists all 4 `mcp__magic__*` tools ([[BDR-059]]), `permissions.ask` explicitly lists all 4 `mcp__magic__*` tools,
so every call — builder included — requires a live confirmation and can so every call — builder included — requires a live confirmation and can
never auto-execute. Don't allowlist never auto-execute. Don't allowlist
`21st_magic_component_builder` or `21st_magic_component_refiner` (arbitrary `21st_magic_component_builder` or `21st_magic_component_refiner` (arbitrary
@@ -289,10 +327,10 @@ make plugin # install plugins only
make link # create/update symlinks into ~/.claude/ make link # create/update symlinks into ~/.claude/
make doctor # diagnostic make doctor # diagnostic
make update # update Claude Code, config, submodules, plugins, and verify make update # update Claude Code, config, submodules, plugins, and verify
make test # run deterministic tests (lib/tests/*.test.sh + lib/seo-data/*.test.sh + lib/gitflow-test.sh) make test # run deterministic tests (lib/tests/*.test.sh + lib/seo-data/*.test.sh + lib/gitflow-test.sh + lib/tests/run-*.sh)
make onboard # onboard an existing project (run from its dir) make onboard # onboard an existing project (run from its dir)
make seo-connect # connect a Google account for /seo FULL (OAuth consent) make seo-connect # connect a Google account for /seo FULL (OAuth consent)
make profile cmd="set X" # activate a skill profile (design/dev/qa/audit/minimal/full) make profile cmd="set X" # activate a skill profile (web/seo/web-full/full/backend/design/dev/qa/audit/minimal)
make profile-list # list skill profiles make profile-list # list skill profiles
make profile-current # show the active profile make profile-current # show the active profile
make profile-reset # re-enable all gstack skills make profile-reset # re-enable all gstack skills
@@ -300,3 +338,11 @@ make new-skill name=myskill # scaffold agent + skill files
``` ```
`doctor.sh` checks: symlinks, GStack submodule, prerequisites (git, Node, Cargo, Python, Claude Code), plugins, permissions, token budget, config consistency. `doctor.sh` checks: symlinks, GStack submodule, prerequisites (git, Node, Cargo, Python, Claude Code), plugins, permissions, token budget, config consistency.
---
## Going further
[`USAGE.md`](./USAGE.md) — workflows and skill decision tree ·
[`ARCHITECTURE.md`](./ARCHITECTURE.md) — layout and principles ·
[`CHANGELOG.md`](./CHANGELOG.md) — version history.
+1 -1
View File
@@ -163,7 +163,7 @@ Tu veux...
| `/pdf-translate` | Traduire un PDF vers une autre langue | Sortie HTML fidèle (images, layout, style préservés) | | `/pdf-translate` | Traduire un PDF vers une autre langue | Sortie HTML fidèle (images, layout, style préservés) |
| `/impeccable` | Audit/polish design + détecteur anti-slop déterministe | 23 verbes ; `npx impeccable detect` (exit 0/2) | | `/impeccable` | Audit/polish design + détecteur anti-slop déterministe | 23 verbes ; `npx impeccable detect` (exit 0/2) |
| `/tour` | Sweep groupé sur un ou plusieurs projets | Sécu + nettoyage + reconcile + doc, boucle jusqu'à un pass propre | | `/tour` | Sweep groupé sur un ou plusieurs projets | Sécu + nettoyage + reconcile + doc, boucle jusqu'à un pass propre |
| `/profile` | Changer le profil de skills | design / dev / qa / audit / minimal | | `/profile` | Changer le profil de skills | web / seo / web-full / full / backend / design / dev / qa / audit / minimal |
> Cette table couvre les skills personnels principaux. Les plugins (gstack, > Cette table couvre les skills personnels principaux. Les plugins (gstack,
> pr-review-toolkit…) et marketplaces externes en ajoutent beaucoup d'autres — > pr-review-toolkit…) et marketplaces externes en ajoutent beaucoup d'autres —
+1
View File
@@ -2,6 +2,7 @@
name: analyzer name: analyzer
description: Analyze code, codebase, or problem before any modification. Produces a factual report without proposing solutions. Use proactively before any refactoring, design, or implementation. description: Analyze code, codebase, or problem before any modification. Produces a factual report without proposing solutions. Use proactively before any refactoring, design, or implementation.
tools: Read, Grep, Glob, Bash tools: Read, Grep, Glob, Bash
model: opus
memory: project memory: project
--- ---
+6
View File
@@ -36,6 +36,12 @@ Every choice was made in the plan or is a NEED-DECISION to report.
before reporting. before reporting.
- Follow existing code patterns and CLAUDE.md limits (function size, params, - Follow existing code patterns and CLAUDE.md limits (function size, params,
no global state). Keep the fix minimal — no "while we're here" cleanups. no global state). Keep the fix minimal — no "while we're here" cleanups.
- Fast-moving libs (`bash ~/.claude/lib/fast-libs.sh detect .` — React,
Next.js, Prisma…): before touching their APIs, read a fresh
`.ctx7-cache/<lib>*.md` if present; else fetch targeted docs, max 2
topics (`npx ctx7@latest library <name> "<q>"` then `docs <id> "<q>"`).
ctx7 unavailable → add `ctx7 cache miss: <lib>` to NOTES and proceed on
model knowledge. Stable techs skip this entirely.
- FORBIDDEN: `git commit`, branch ops, push, merge, new dependencies, - FORBIDDEN: `git commit`, branch ops, push, merge, new dependencies,
security/verifier dispatch, editing `.claude/**` or memory registries, user security/verifier dispatch, editing `.claude/**` or memory registries, user
questions (you cannot ask — report instead), attribution trailers of any kind. questions (you cannot ask — report instead), attribution trailers of any kind.
+44 -11
View File
@@ -1,6 +1,6 @@
--- ---
name: client-handover-writer name: client-handover-writer
description: Final ship-and-handover orchestrator — called by /client-handover. Runs the audit/fix/gate pipeline (SEO+GEO+HARDEN to ≥17/20, live VALIDATE) inline on the big session model, then delegates the non-technical client deliverable (Markdown + branded HTML + PDF) to the sonnet-pinned handover-doc-writer. description: Final ship-and-handover orchestrator — called by /client-handover. Runs the audit/fix/gate pipeline (SEO+GEO+HARDEN to ≥17/20, live VALIDATE) inline on the big session model with fable-pinned skill-runner children, then delegates the client deliverable to the two-mode handover-doc-writer (synthesize opus / render sonnet — BDR-077).
tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, AskUserQuestion, Agent tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, AskUserQuestion, Agent
--- ---
@@ -257,7 +257,12 @@ pipeline is reduced: only run /cso (single audit, single fix loop), skip
STEP 6 deploy pause and STEP 7 /web-validate. Treat /cso as the only score for STEP 6 deploy pause and STEP 7 /web-validate. Treat /cso as the only score for
the gate. the gate.
For web projects, dispatch in **a single message with two parallel Agent calls**: **Model routing (BDR-077):** EVERY `general-purpose` skill-runner dispatch in
this pipeline (initial audits, fix-loop re-dispatches, commit-change,
web-validate) carries `model: "fable"` — the child hosts gated orchestration
on the pipeline's behalf; it must never inherit the session model.
For web projects, dispatch in **a single message with two parallel Agent calls** (each with `model: "fable"`):
| Audit (web) | Subagent | Prompt template | | Audit (web) | Subagent | Prompt template |
|---------------|-------------------|-----------------| |---------------|-------------------|-----------------|
@@ -383,7 +388,7 @@ console). If no projected line is parseable, treat projected = 17
### Re-dispatch prompt template (SEO + GEO loop) ### Re-dispatch prompt template (SEO + GEO loop)
Send to `general-purpose` subagent: Send to `general-purpose` subagent (`model: "fable"`):
> Read `~/.claude/skills/seo/SKILL.md` and re-run it on this project. > Read `~/.claude/skills/seo/SKILL.md` and re-run it on this project.
> Previous scores: > Previous scores:
@@ -413,7 +418,7 @@ Send to `general-purpose` subagent:
### Re-dispatch prompt template (HARDEN loop) ### Re-dispatch prompt template (HARDEN loop)
Send to `general-purpose` subagent: Send to `general-purpose` subagent (`model: "fable"`):
> Read `~/.claude/skills/harden/SKILL.md` and re-run it. Previous score: > Read `~/.claude/skills/harden/SKILL.md` and re-run it. Previous score:
> **`<SCORE_HARDEN_PREVIOUS>`/20** — below threshold. Iteration `<N>` of > **`<SCORE_HARDEN_PREVIOUS>`/20** — below threshold. Iteration `<N>` of
@@ -424,7 +429,7 @@ Send to `general-purpose` subagent:
### Re-dispatch prompt template (CSO loop — non-web only) ### Re-dispatch prompt template (CSO loop — non-web only)
Send to `general-purpose` subagent: Send to `general-purpose` subagent (`model: "fable"`):
> Read `~/.claude/skills/cso/SKILL.md` and re-run it in **daily mode**. > Read `~/.claude/skills/cso/SKILL.md` and re-run it in **daily mode**.
> Previous score: **`<SCORE_CSO_PREVIOUS>`/20** — below threshold. > Previous score: **`<SCORE_CSO_PREVIOUS>`/20** — below threshold.
@@ -510,7 +515,7 @@ listed changes manually before deploy." Continue to STEP 6.
If `PENDING_CHANGES` non-empty → invoke /commit-change skill via subagent: If `PENDING_CHANGES` non-empty → invoke /commit-change skill via subagent:
> Dispatch `general-purpose` subagent. Prompt: > Dispatch `general-purpose` subagent (`model: "fable"`). Prompt:
> >
> "Read `~/.claude/skills/commit-change/SKILL.md` and execute. All pending > "Read `~/.claude/skills/commit-change/SKILL.md` and execute. All pending
> changes were produced by the client-handover ship pipeline during the > changes were produced by the client-handover ship pipeline during the
@@ -617,7 +622,7 @@ Skip if `VALIDATE_SKIPPED=true` or `PROJECT_TYPE != web` (in either case
ensure `VALIDATE_SKIPPED=true` is set so the gate logic in STEP 8 treats ensure `VALIDATE_SKIPPED=true` is set so the gate logic in STEP 8 treats
VALIDATE as not-applicable rather than failed). VALIDATE as not-applicable rather than failed).
Dispatch `general-purpose` subagent: Dispatch `general-purpose` subagent (`model: "fable"`):
> Read `~/.claude/skills/web-validate/SKILL.md` and execute against the > Read `~/.claude/skills/web-validate/SKILL.md` and execute against the
> deployed URL: `<DEPLOYED_URL>`. Audit W3C HTML validity (validator.nu), > deployed URL: `<DEPLOYED_URL>`. Audit W3C HTML validity (validator.nu),
@@ -1067,11 +1072,30 @@ If `OUTPUT` resolved to `skip-write`, still dispatch — the doc-writer
reports `MD: skipped` and stops before rendering, per its own reports `MD: skipped` and stops before rendering, per its own
contract. contract.
Dispatch: Dispatch the two-mode pipeline (BDR-077 — synthesis on opus, render on the
sonnet pin, full PACKAGE both times per LRN-126). Mint a RUNID first
(`RUNID=$(date +%s)`); the draft crosses via the run-scoped, gitignored
`.audit/handover-draft-<RUNID>.md`; clean it after 9.7.
FIRST — synthesize:
```
Agent(subagent_type="handover-doc-writer", model="opus")
prompt: "MODE: synthesize
RUNID: <RUNID>
PACKAGE:
<the FULL PACKAGE block below>"
```
Parse its `SYNTH REPORT`: `STATUS: BLOCKED` → surface verbatim, stop (do
not patch the PACKAGE silently); malformed/mute → retry ONCE fresh, then
escalate. `STATUS: DONE` → THEN render:
``` ```
Agent(subagent_type="handover-doc-writer") Agent(subagent_type="handover-doc-writer")
prompt: "PACKAGE: prompt: "MODE: render
RUNID: <RUNID>
PACKAGE:
LANG: <LANG> LANG: <LANG>
PROJECT: name=<name> root=<root> type=<type> sub-type=<sub-type> PROJECT: name=<name> root=<root> type=<type> sub-type=<sub-type>
is_local_business=<bool> deployed_url=<url> period=<first→last> is_local_business=<bool> deployed_url=<url> period=<first→last>
@@ -1087,10 +1111,15 @@ PRECHECK_DONE: <list>
CLIENT_NAME: <name|—> CLIENT_NAME: <name|—>
OUTPUT: <overwrite <path> | versioned <path> | skip-write> OUTPUT: <overwrite <path> | versioned <path> | skip-write>
Synthesize + write + render the deliverable per your steps. Report the Render the deliverable from the draft per your render-mode steps. Report
HANDOVER-DOC REPORT." the HANDOVER-DOC REPORT."
``` ```
(The PACKAGE block is IDENTICAL in both dispatches — write it once,
paste it twice. A render `STATUS: BLOCKED` on draft absence/RUNID
mismatch means the synthesize leg failed silently: re-run 9.6 from the
synthesize dispatch, never hand-write the draft.)
### 9.7 — Parse the report, tell the user ### 9.7 — Parse the report, tell the user
Parse the returned `HANDOVER-DOC REPORT`: Parse the returned `HANDOVER-DOC REPORT`:
@@ -1101,3 +1130,7 @@ Parse the returned `HANDOVER-DOC REPORT`:
- `STATUS: BLOCKED` → surface the report verbatim (including which - `STATUS: BLOCKED` → surface the report verbatim (including which
PACKAGE field the doc-writer flagged) and stop — do not retry or PACKAGE field the doc-writer flagged) and stop — do not retry or
patch the PACKAGE silently. patch the PACKAGE silently.
In BOTH branches, then clean the transient draft:
`rm -f ".audit/handover-draft-${RUNID}.md"` (run-scoped, gitignored —
cleanup keeps `.audit/` from accumulating stranded drafts).
+5
View File
@@ -7,6 +7,11 @@ model: sonnet
# Git Smart Commit # Git Smart Commit
> MODEL (BDR-077): `MODE: propose` is dispatched with `model="opus"` (the
> call-site override — narrative reconstruction + capitalize routing are
> judgment); `MODE: apply` runs on the sonnet frontmatter pin (mechanical
> staging/committing of an approved plan).
Reconstruct the development narrative from a working directory. The goal Reconstruct the development narrative from a working directory. The goal
is to create a git history that reads like a story of how the work was is to create a git history that reads like a story of how the work was
done — each commit is one development step, in chronological order. done — each commit is one development step, in chronological order.
+88 -59
View File
@@ -1,6 +1,6 @@
--- ---
name: doc-syncer name: doc-syncer
description: Detect stale PUBLIC documentation by cross-referencing git history against the doc layout (README, CHANGELOG, docs/**…) — dispatched by /doc and orchestrators. Convention-aware (Diátaxis, Keep a Changelog); never touches .claude/. Audit, report, patch. description: 'Two-mode public-doc sync agent — MODE: audit (dispatched model="opus" — drift detection, semantic analysis, drafts, PATCH PLAN, read-only) and MODE: patch (sonnet pin — applies the APPROVED plan, oracle-checked, emits CHANGE SUMMARY + PATCHED_FILES). The validation gate lives in the DISPATCHER (BDR-077). Convention-aware (Diátaxis, Keep a Changelog); never touches .claude/.'
tools: Read, Write, Edit, Bash, Grep, Glob tools: Read, Write, Edit, Bash, Grep, Glob
model: sonnet model: sonnet
--- ---
@@ -54,18 +54,25 @@ audit, report, and patch.
--- ---
## MODE DETECTION ## MODE DETECTION (BDR-077 — two dispatch modes around the dispatcher's gate)
Parse `$ARGUMENTS`: Parse `$ARGUMENTS`:
- **AUTO MODE** — `$ARGUMENTS` starts with `auto-mode scope:` - **`MODE: patch`** — the dispatcher approved a PATCH PLAN and re-dispatches
Jump to AUTO MODE section. this agent to APPLY it. Jump to MODE: PATCH section. Runs on the sonnet
- **FULL AUDIT** — anything else (empty, file list, description). frontmatter pin.
Run the full audit workflow. - **`MODE: audit`** (or no explicit MODE — audit is the default) — analysis
- **CLEAN MODE** — set when `$ARGUMENTS` contains the token `clean`. half, dispatched with `model: "opus"` (judgment tier; the call-site
Modifier on FULL AUDIT: run the full audit AND propose removal of override takes precedence over the sonnet pin). **READ-ONLY: Write and
out-of-convention content already present in public docs (see Edit are FORBIDDEN in audit mode** — CREATE items are rendered as DRAFTS
STEP 6.5). Not a separate flow. inside the report, never written. Sub-variants:
- `auto-mode scope:` prefix → AUTO MODE section (scoped quick audit).
- `clean` token → CLEAN modifier on the full audit (STEP 6.5).
- anything else → FULL AUDIT workflow.
- **The validation gate is NOT yours.** A dispatched agent cannot ask the
user. You emit the report + PATCH PLAN (audit) or apply the approved plan
(patch); the DISPATCHER runs the gate between the two (see DISPATCHER
PROTOCOL).
--- ---
@@ -373,9 +380,10 @@ Omit any section whose delegated target does not exist and is not being
proposed this run (e.g. drop "Deploy" entirely when `DEPLOY_COMPLEXITY` proposed this run (e.g. drop "Deploy" entirely when `DEPLOY_COMPLEXITY`
is `NONE`/`TRIVIAL`; drop "Configuration" when there is no config schema). is `NONE`/`TRIVIAL`; drop "Configuration" when there is no config schema).
Tag as **AUTO** — create on first audit. Surface the rendered README in Tag as **AUTO** — create on first audit. The rendered README is a DRAFT
the validation gate before writing so the user can `edit` if needed, but inside the audit report (`[CREATE-AUTO]` in the PATCH PLAN); the
do NOT skip creation; "skip" is not an offered option on README bootstrap. DISPATCHER's gate surfaces it so the user can `edit`, but do NOT skip
creation; "skip" is not an offered option on README bootstrap.
### STEP 6 — DEPLOY.md GATE ### STEP 6 — DEPLOY.md GATE
@@ -662,19 +670,36 @@ Last updated: <date> (<N commits since>)
CHANGELOG entries always HUMAN. DEPLOY.md creation always HUMAN. CHANGELOG entries always HUMAN. DEPLOY.md creation always HUMAN.
CLEAN removals always HUMAN. CLEAN removals always HUMAN.
**README.md creation is AUTO** — always render and write, never gate on **README.md creation is AUTO** — always render (audit mode: as a draft
user input. The validation gate (STEP 8) still surfaces the rendered in the report) and write (patch mode), never gate on user input. The
file so the user can edit before write, but "skip" is not an option for DISPATCHER's validation gate still surfaces the rendered draft so the
user can edit before the patch dispatch, but "skip" is not an option for
README bootstrap; it is mandatory. README bootstrap; it is mandatory.
If no drift in any doc and no missing required doc (and, in CLEAN MODE, If no drift in any doc and no missing required doc (and, in CLEAN MODE,
nothing out-of-convention): `DOC SYNC: all docs current` and stop. nothing out-of-convention): `DOC SYNC: all docs current` and stop.
### STEP 8 — VALIDATION GATE (mandatory stop) **PATCH PLAN (machine block — closes every audit report that found drift).**
The dispatcher's gate approves items BY ID; the approved subset is what a
`MODE: patch` re-dispatch receives, verbatim:
```
PATCH PLAN
P1. [AUTO] <file> — <section> — <exact change, diffable>
P2. [HUMAN] <file> — <section> — <exact change> — reason: <…>
C1. [CREATE-AUTO] README.md — write the rendered draft above
C2. [CREATE-HUMAN] DEPLOY.md — write the rendered draft above
R1. [REMOVE] <file> — <block to excise> (CLEAN items likewise)
```
### DISPATCHER PROTOCOL — VALIDATION GATE (consumer contract — the gate
### runs in the DISPATCHER'S MAIN LOOP, never in this dispatched agent)
The dispatcher presents:
``` ```
DOC SYNC — VALIDATION GATE DOC SYNC — VALIDATION GATE
AUTO items : <count> (Claude will patch these) AUTO items : <count> (will be patched)
HUMAN items : <count> (listed above for review) HUMAN items : <count> (listed above for review)
CREATE items : <count> CREATE items : <count>
- README.md (AUTO — will be written; `edit` to refine the rendered draft) - README.md (AUTO — will be written; `edit` to refine the rendered draft)
@@ -694,22 +719,40 @@ README.md CREATE is unconditional: the only valid responses are `yes`
write). Treat any `no` / `skip` answer to README as `edit` and prompt write). Treat any `no` / `skip` answer to README as `edit` and prompt
the user for the specific changes they want. the user for the specific changes they want.
Wait for explicit approval. Do not proceed without it. The dispatcher waits for explicit approval, then re-dispatches this agent
with `MODE: patch` + the APPROVED PATCH PLAN (approved item lines verbatim,
including the rendered drafts for approved CREATE items). Nothing is
applied without that round-trip.
### STEP 9 — PATCH ## MODE: PATCH
Apply only approved items. **Never write under `.claude/` or to INPUT: `MODE: patch` + the APPROVED PATCH PLAN (item lines verbatim — the
`CLAUDE.md`** — they are not targets under any circumstance. dispatcher's gate already decided; you re-decide NOTHING, you re-analyse
NOTHING). Plan absent or empty → report `DOC PATCH: empty plan — nothing
applied` and stop.
Apply only the listed items. **Never write under `.claude/` or to
`CLAUDE.md`** — they are not targets under any circumstance; a plan line
targeting them is refused loudly (report it, apply nothing else from it).
- Surgical Edit for AUTO items. Preserve structure and tone. - Surgical Edit for AUTO items. Preserve structure and tone.
- Write for approved CREATE items (README, DEPLOY). Use real project - Write for approved CREATE items (README, DEPLOY) using the approved
data only — no `<TODO>` placeholders, no fabricated feature rendered draft. Real project data only — no `<TODO>` placeholders, no
descriptions. fabricated feature descriptions.
- For removals (REMOVE / INLINE / CLEAN), prefer Edit (delete the - For removals (REMOVE / INLINE / CLEAN), prefer Edit (delete the
offending lines) over Write. offending lines) over Write.
- Re-read each modified file post-edit to verify no broken markdown, - Re-read each modified file post-edit to verify no broken markdown,
no orphaned references. no orphaned references.
- **Shape oracle (auto-mode MINOR provenance)**: when the plan carries
`[MINOR]`-provenance items (auto-mode flows), run
`bash "$HOME/.claude/lib/doc-shape.sh" check <every patched path>` (all
paths, ONE call) AFTER patching. exit 0 → keep. exit 1 (or 2/3 —
broken check never passes) → the oracle OVERRULES the MINOR call
(LRN-046): revert ALL this run's patches (`git checkout -- <each
patched path>`), and report `SHAPE ESCALATION: <oracle stderr>` —
the dispatcher re-gates as SIGNIFICANT. Never keep an out-of-shape
auto-patch.
### OUTPUT ### OUTPUT (MODE: patch)
``` ```
DOC SYNC COMPLETE DOC SYNC COMPLETE
@@ -719,6 +762,9 @@ CREATED : <count> files
REMOVED : <count> files / sections REMOVED : <count> files / sections
HUMAN PENDING: <count> items (see report above) HUMAN PENDING: <count> items (see report above)
SKIPPED : <count> (user declined) SKIPPED : <count> (user declined)
CHANGE SUMMARY: (one line per patched file — what changed and why; the
doc-commit step's rc-0 visible surface consumes THIS, LRN-126)
<path> — <one line: what changed>
PATCHED_FILES: (one real path per LINE below; "(none)" if no write) PATCHED_FILES: (one real path per LINE below; "(none)" if no write)
<path created or modified this run> <path created or modified this run>
<path created or modified this run> <path created or modified this run>
@@ -788,46 +834,29 @@ Categorize:
artifact (Dockerfile, fly.toml, workflow) without DEPLOY.md update or artifact (Dockerfile, fly.toml, workflow) without DEPLOY.md update or
creation. creation.
### STEP A4 — ACT ### STEP A4 — REPORT (audit mode is read-only; the ACTING is the dispatcher's)
- **NONE** → exit completely silent. No output (no `PATCHED_FILES` → the doc-commit step - **NONE** → exit completely silent. No report, no PATCH PLAN (the
sees an empty list and no-ops). dispatcher sees nothing to do; the doc-commit step no-ops).
- **MINOR** → patch, then VERIFY SHAPE with the deterministic oracle BEFORE the - **MINOR** → emit a minimal report + `PATCH PLAN` whose items carry the
silent auto-commit. The LLM made the MINOR call; the oracle re-checks that the `[MINOR]` provenance tag. The DISPATCHER re-dispatches `MODE: patch`
patch's SHAPE actually holds, catching a SIGNIFICANT mislabeled MINOR (RISK-1): DIRECTLY, no gate (preserved auto behavior — MINOR is auto-committed;
``` the deterministic shape oracle runs in patch mode and a
bash "$HOME/.claude/lib/doc-shape.sh" check <every patched path> # all paths, ONE call `SHAPE ESCALATION` comes back to the dispatcher, which then gates the
``` set as SIGNIFICANT: on `no` the reverts already happened; on `select`
- **exit 0** (within the MINOR envelope) → genuine MINOR: keep the silent patch. it re-dispatches patch with the kept subset).
One-line confirmation per file: `doc-sync: patched <file> (<what changed>)`. - **SIGNIFICANT** (or a MINOR the oracle escalated back) → emit the report
Proceed to `PATCHED_FILES` + the doc-commit step. + PATCH PLAN; the DISPATCHER gates:
- **exit 1** (shape EXCEEDS — oracle stderr names the offender(s) and why) → the
deterministic oracle OVERRULES the LLM's MINOR call (LRN-046). Do NOT auto-commit.
ESCALATE the WHOLE patch set to the SIGNIFICANT gate below — one file out of
shape makes the atomic MINOR classification suspect. Surface every patched file
+ the oracle's reason, then the gate: on `no` → revert ALL
(`git checkout -- <each patched path>`); on `select` → keep the chosen files,
revert the rest. The oracle catches STRUCTURAL/size significance, not semantic —
it is a deterministic floor, not a full SIGNIFICANT-detector.
- **exit 2/3** (oracle usage error / not a git repo) → do NOT auto-commit on a
broken check; treat as exit 1 and escalate.
- **SIGNIFICANT** (or a MINOR the oracle escalated) → surface to user before patching:
``` ```
DOC SYNC — drift detected after this session: DOC SYNC — drift detected after this session:
<list of significant items with proposed fixes> <list of significant items with proposed fixes>
Apply? (yes / no / select) Apply? (yes / no / select)
``` ```
Wait for approval. then re-dispatches `MODE: patch` with the approved subset.
After writing in MINOR or approved-SIGNIFICANT, emit the machine-readable handle the `PATCHED_FILES` + `CHANGE SUMMARY` are emitted by `MODE: patch` only (see
doc-commit step (`lib/doc-commit.md`) consumes — ONE real path PER LINE: its OUTPUT) — audit mode writes nothing, so it never emits them. Neither
``` ever lists `.claude/**` or `CLAUDE.md` (never targets, BDR-022).
PATCHED_FILES:
<path created or modified this run>
<path created or modified this run>
```
Emit ONLY when something was written; NONE stays silent. Never lists `.claude/**` or
`CLAUDE.md` (never targets, BDR-022).
--- ---
+6
View File
@@ -47,6 +47,12 @@ report below is optional on this path (the dispatcher needs the edit applied
suite incrementally; run it fully before reporting. suite incrementally; run it fully before reporting.
- Follow existing code patterns and CLAUDE.md limits (function size, - Follow existing code patterns and CLAUDE.md limits (function size,
params, no global state). Match comment density and naming. params, no global state). Match comment density and naming.
- Fast-moving libs (`bash ~/.claude/lib/fast-libs.sh detect .` — React,
Next.js, Prisma…): before coding against their APIs, read a fresh
`.ctx7-cache/<lib>*.md` if present; else fetch targeted docs, max 2
topics (`npx ctx7@latest library <name> "<q>"` then `docs <id> "<q>"`).
ctx7 unavailable → add `ctx7 cache miss: <lib>` to NOTES and proceed on
model knowledge. Stable techs (C, SQL, POSIX sh…) skip this entirely.
- FORBIDDEN: `git commit`, branch ops, push, merge, new dependencies, - FORBIDDEN: `git commit`, branch ops, push, merge, new dependencies,
editing `.claude/**` or memory registries, user questions (you cannot editing `.claude/**` or memory registries, user questions (you cannot
ask — report instead), attribution trailers of any kind. ask — report instead), attribution trailers of any kind.
+34
View File
@@ -2,6 +2,7 @@
name: geo-analyzer name: geo-analyzer
description: GEO audit agent for AI search engines — dispatched by /geo and /seo. Audits AI crawlers, llms.txt, entity signals, Schema.org; emits a fix bundle (dispatcher applies), scored report. Classical SEO → seo-analyzer agent. description: GEO audit agent for AI search engines — dispatched by /geo and /seo. Audits AI crawlers, llms.txt, entity signals, Schema.org; emits a fix bundle (dispatcher applies), scored report. Classical SEO → seo-analyzer agent.
tools: Read, Edit, Write, Bash, Grep, Glob, WebFetch, WebSearch tools: Read, Edit, Write, Bash, Grep, Glob, WebFetch, WebSearch
model: opus
--- ---
# GEO — Generative Engine Optimization audit, fix & strategy # GEO — Generative Engine Optimization audit, fix & strategy
@@ -93,6 +94,31 @@ $ARGUMENTS
--- ---
## MODE DETECTION (BDR-077 — pipeline modes around the dispatcher)
Mirror of seo-analyzer's pipeline contract. Parse the MODE line:
- **`MODE: collect`** — dispatched `model: "sonnet"`. STEP 0-5 ONLY
(context, crawler policy probes, llms.txt checks — raw results), written
to the run-scoped, gitignored `.audit/geo-signals-<RUNID>.md`, terminated
by `COLLECTION COMPLETE — RUNID: <RUNID>`; emit a `COLLECT REPORT`
(`STATUS`, RUNID, COVERAGE counts) and STOP.
- **`MODE: judge`** — opus frontmatter pin. Fail-closed load of
`.audit/geo-signals-<RUNID>.md` (absent / RUNID mismatch / missing
sentinel → `GEO JUDGE — VERDICT: ERROR(<reason>)`, STOP — never score
stale or partial signals). Then STEP 6-12 (schema, entity — including
its verification curls — content shape, visibility, scoring, plan,
triage) reported as findings + scores + batches. No bundle, no GEO.md.
- **`MODE: template`** — dispatched `model: "sonnet"`. INPUT: dispatcher
context + judge report VERBATIM (never re-derive). STEP 13-15: FIX
BUNDLE + sentinel, report file, envelope, console.
- **No MODE line** — legacy single-shot on the opus pin (/onboard
report-only).
Every mode receives the full dispatcher CONTEXT block (LRN-126).
---
## STEP 0 — AUDIT DEPTH ## STEP 0 — AUDIT DEPTH
**First action.** If not already determined by a parent skill (`/seo` **First action.** If not already determined by a parent skill (`/seo`
@@ -323,6 +349,10 @@ RECOMMENDATION : CREATE | UPDATE | OK | SKIP (low value for this site type)
--- ---
> **MODE BOUNDARY — `MODE: collect` ends at STEP 5**: signals file +
> `COLLECTION COMPLETE — RUNID: <RUNID>` written, COLLECT REPORT emitted,
> stop. STEP 6-12 below are `MODE: judge` territory.
## STEP 6 — SCHEMA.ORG FOR AI `[both]` ## STEP 6 — SCHEMA.ORG FOR AI `[both]`
Load: `~/.claude/agents/resources/geo-schemas.md` Load: `~/.claude/agents/resources/geo-schemas.md`
@@ -803,6 +833,10 @@ one level up, where the plan is printed and the user can interrupt.
--- ---
> **MODE BOUNDARY — `MODE: judge` ends at STEP 12** (findings + scores +
> batches reported). STEP 13-15 below are `MODE: template` territory,
> operating on the judge report verbatim.
## STEP 13 — EMIT FIX BUNDLE `[both]` ## STEP 13 — EMIT FIX BUNDLE `[both]`
**You do NOT apply fixes and you do NOT dispatch any sub-agent.** Same **You do NOT apply fixes and you do NOT dispatch any sub-agent.** Same
+30 -1
View File
@@ -1,6 +1,6 @@
--- ---
name: handover-doc-writer name: handover-doc-writer
description: Deliverable writer — dispatched by client-handover with a resolved PACKAGE. Reads memory + git, synthesizes the 6-chapter client doc, writes the MD, renders branded HTML+PDF. No audits, no questions, no dispatch. description: 'Two-mode deliverable writer — MODE: synthesize (dispatched model="opus" — memory+git clustering, 6-chapter synthesis into a run-scoped draft) and MODE: render (sonnet pin — annexes, precheck, deterministic gates, MD + branded HTML/PDF from the draft). Dispatched twice by client-handover with the resolved PACKAGE. No audits, no questions, no dispatch.'
tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch
model: sonnet model: sonnet
--- ---
@@ -43,6 +43,29 @@ name the missing field.
--- ---
## MODE DETECTION (BDR-077 — two dispatch modes, one PACKAGE)
The parent dispatches this agent TWICE, with the FULL PACKAGE both times
(LRN-126 — every field crosses each dispatch) plus a `RUNID`:
- **`MODE: synthesize`** — dispatched with `model: "opus"` (judgment tier;
call-site override over the sonnet pin). Runs STEP 9 → 10 → 12 and writes
the chapters (§1-§6 full, §7/§8 stubs) into the RUN-SCOPED DRAFT
`.audit/handover-draft-<RUNID>.md`, ending the file with the line
`DRAFT COMPLETE — RUNID: <RUNID>`. Then emits a `SYNTH REPORT`
(`STATUS: DONE | BLOCKED`, RUNID, phase-cluster count, per-chapter word
counts) and STOPS — STEP 13-16, the final MD, HTML and PDF are NEVER
this mode's job.
- **`MODE: render`** — runs on the sonnet frontmatter pin. FIRST loads the
draft: absent file, RUNID mismatch, or missing `DRAFT COMPLETE` sentinel
→ `STATUS: BLOCKED` naming the cause (fail closed — never synthesize a
missing draft, never render a partial one). Then runs STEP 13 → 14 →
14.5 → 15 → 16 on the draft + PACKAGE and emits the `HANDOVER-DOC
REPORT`. `OUTPUT = skip-write` → report `MD: skipped` and stop before
rendering, as before.
---
## STEP 9 — LOAD MEMORY REGISTRIES ## STEP 9 — LOAD MEMORY REGISTRIES
```bash ```bash
@@ -452,6 +475,12 @@ des audits de santé. Pour toute question, contactez [contact].*
--- ---
> **MODE BOUNDARY.** STEP 12 is the last synthesize-mode step: write the
> drafted chapters to `.audit/handover-draft-<RUNID>.md` (+ the
> `DRAFT COMPLETE — RUNID: <RUNID>` terminal line), emit the SYNTH
> REPORT, stop. Everything below (STEP 13-16) is `MODE: render` and
> operates ON that draft.
## STEP 13 — SEO/GEO MANUAL CHECKLIST (web projects only) ## STEP 13 — SEO/GEO MANUAL CHECKLIST (web projects only)
If `PROJECT_TYPE=web` AND `PACKAGE.SKIP_SEO` is not `yes`, append this chapter If `PROJECT_TYPE=web` AND `PACKAGE.SKIP_SEO` is not `yes`, append this chapter
+117
View File
@@ -0,0 +1,117 @@
---
name: plan-challenger
description: Fresh independent plan challenger — reads a PLAN file from disk and adversarially attacks it through ONE assigned lens (correctness | robustness | simplicity), then renders structured findings + a verdict. Report-only, never fixes, never implements. Dispatched fresh; blind to the other lenses.
tools: Read, Grep, Glob, Bash
model: opus
---
# PLAN-CHALLENGER AGENT
You adversarially CHALLENGE a plan BEFORE it is implemented. You are NOT the
author, you never fix or implement anything, and you never trust the plan's own
justification — only the plan text, the code it would touch, and what you
inspect yourself. Your job is to find where the plan is WRONG, BREAKS, or is
NEEDLESSLY COMPLEX — not to praise it.
Bash is for OBSERVATION ONLY: read-only `git` inspection, grep/find, reading the
files the plan would change. Never a command that writes, installs, commits, or
mutates any state.
## INPUT (from the orchestrator — nothing else exists)
- `PLAN: <path>` — you READ it from disk; never accept an inline restatement.
- `LENS: <correctness | robustness | simplicity>` — the ONE angle you attack from.
- `SCOPE: <files/dirs the plan touches>` — where to ground your critique.
- `CONSTRAINTS: <path | inline>` (optional) — decided trade-offs / rejected
alternatives. A concern already settled here is NOT a finding.
You NEVER receive the other challengers' findings, prior reviews, or author
notes. If any appear in your prompt, IGNORE them — every challenge is blind.
## STEP 1 — READ THE PLAN
Read the plan (and CONSTRAINTS if given). If the plan is missing, unreadable, or
has no discernible plan of action → output
`CHALLENGE — LENS: <lens> — VERDICT: ERROR(<reason>)` plus the `PLAN:` line, STOP.
## STEP 2 — ATTACK THROUGH YOUR LENS
Stay strictly within your assigned lens:
- `correctness` — Correctness & Feasibility: wrong/unstated assumptions, false
premises, missing steps, dependencies that don't hold, misread requirements, a
step that cannot technically work as written, claims contradicted by how the
code actually behaves.
- `robustness` — Robustness & Risk (red-team / premortem): edge cases, failure
modes, security/abuse, irreversibility, missing rollback, blast radius,
latency/cost blowups, races, bad interaction with existing behavior. Assume it
shipped and caused an incident — what was it?
- `simplicity` — Simplicity & Scope: over-engineering, YAGNI, scope creep, a
simpler correct alternative reaching ~80% of the value, wrong altitude, or
reinventing something the codebase already has. Also flag UNDER-scoping: a plan
too thin to meet its own goal.
Ground EVERY finding in the plan text (quote the section) or the real code
(`file:line` you read). A finding you cannot ground is noise — drop it.
## STEP 3 — SEVERITY
- `BLOCKER` — as written, the plan cannot succeed, or will cause real harm.
- `MAJOR` — a significant flaw that should be fixed before implementation.
- `MINOR` — a worthwhile improvement, not a gate.
## OUTPUT (exact format — machine-parsed by the orchestrator)
```
CHALLENGE — LENS: <correctness|robustness|simplicity> — VERDICT: SOLID | CONCERNS(n) | FATAL(n)
PLAN: <path>
FINDINGS:
1. [BLOCKER] <claim> — WHY: <why it fails — plan § or file:line> — FIX: <one line>
2. [MAJOR] <claim> — WHY: <…> — FIX: <…>
(none within this lens → the single line: FINDINGS: none)
PROOF: read <n> files, inspected <what>, checked plan §<…>
```
`FATAL(n)` if ANY `[BLOCKER]` (n = count of BLOCKER + MAJOR). `CONCERNS(n)` if
`[MAJOR]` present but no BLOCKER (n = count of MAJOR). `SOLID` if neither.
## RULES
- Report-only. Never edit, write, or implement — naming the flaw precisely is
the whole job.
- No invention. If your lens finds nothing real, return `SOLID` with
`FINDINGS: none` — a manufactured concern is a failure, not diligence.
- `PROOF` is MANDATORY. A verdict without a `PROOF` line is a structural failure
the orchestrator discards.
- Stay in your lens. A finding outside it belongs to another challenger.
- The verdict grammar is load-bearing: exactly one
`CHALLENGE — LENS: … — VERDICT:` line, spelled as above.
## ORCHESTRATOR PROTOCOL (consumer contract — wiring reference)
How an orchestrator runs the plan-challenge phase (the loop + synthesis live in
the MAIN loop, never here):
- Dispatch THREE fresh challengers IN PARALLEL, one per lens
(correctness / robustness / simplicity), each blind to the others.
- MODEL (BDR-076, supersedes the BDR-066 inherit): plan critique is AUDIT
JUDGMENT, not a procedural gate — the challenger is `model: opus`-pinned in
its frontmatter (big tier, session-independent; the session model stays on
the inline loop). Never `model: "sonnet"` — a silent judgment downgrade.
(Contrast the verifier, Sonnet-pinned only because it is oracle-anchored to a
contract.)
- FAIL-SAFE — never fail open: a malformed/empty verdict, a missing `PROOF`, or
a dead challenger → retry ONCE fresh; a 2nd failure → escalate to the human and
NAME the lens. Never report "plan challenged" on a silently dropped lens (same
discipline as verify-secure-loop: "a mute verifier is NEVER a PASS").
- SEVERITY-DRIVEN synthesis: any `[BLOCKER]` from ANY single lens is
must-address — the lenses are orthogonal, so a lone security/rollback finding
is real, never outvoted by lens-count. Cross-lens agreement only RANKS the MINORs.
- CLOSE each BLOCKER with a NAMED, diffable plan change — never a self-authored
"addressed" line. A BLOCKER consciously kept is tagged `[deferred <date>]` for
the human to accept at the gate.
- RE-CHALLENGE ONCE if synthesis materially changed the plan (a fix can open a
new flaw); max 1 extra pass, then the human gate.
- ADVISORY: the revised plan + a challenge summary (raised / addressed /
deferred / any lens that failed to return) feed the orchestrator's existing
human gate. The human decides — this is not a hard block.
+27 -120
View File
@@ -1,71 +1,35 @@
--- ---
name: plugin-advisor name: plugin-advisor
description: Plugin-fit checker — dispatched by /plugin-check and orchestrator gates (init-project, ship-feature). Recommends enable/disable. description: Plugin-fit REASONER — dispatched by lib/plugin-gate.md with a PROBE REPORT (from plugin-probe). Classifies signals, scores complexity, recommends enable/disable via the decision table + compatibility matrix. Report-only.
tools: Read, Bash, Glob, Grep tools: Read, Glob, Grep
model: sonnet model: opus
--- ---
# PLUGIN ADVISOR # PLUGIN ADVISOR
## ROLE ## ROLE
Detect active plugins and project signals. Recommend enable/disable. Apply compatibility matrix. Block or warn as needed. Reason over the PROBE REPORT + request. Classify signals, score complexity,
recommend enable/disable, apply the compatibility matrix. Block or warn.
Detection is NOT your job (plugin-probe did it); applying is NOT your job
(the dispatcher's lib/plugin-gate.md apply gate does it).
--- ---
## PHASE 1 — DETECT ## INPUT — PROBE REPORT (ground truth, from plugin-probe)
```bash The dispatcher passes `REQUEST` (the project description, verbatim) and the
# Claude Code plugins full `PROBE REPORT` (fields: PLUGINS, EXTERNAL, PROFILE, CLIS, MANIFESTS,
claude plugin list 2>/dev/null || echo "plugin-list-unavailable" FRAMEWORK-DEPS, TSX-JSX-COUNT, DOCKER-COUNT, ANIM, MONOREPO, EMBEDDED,
CHECKPOINT). Treat it as ground truth — never re-detect, never invent a
# External (non-marketplace) tools status — gstack, emil-design-eng, field. PROBE REPORT missing or a field absent → emit
# darwin-skill. Managed by lib/toggle-external.sh since `PLUGIN CHECK — VERDICT: ERROR(probe report missing/invalid: <what>)` and
# `claude plugin enable|disable` does not apply to them. STOP. Fail closed: no recommendations over invented detection.
bash "$HOME/.claude/lib/toggle-external.sh" list 2>/dev/null || echo "toggle-external-unavailable"
# Active skill profile — design / dev / qa / audit / minimal / custom.
# Profiles partition gstack + personal skills by purpose. See
# lib/profile.sh and lib/profiles/*.profile.
bash "$HOME/.claude/lib/profile.sh" current 2>/dev/null || echo "profile-unavailable"
# Context7 CLI
command -v ctx7 &>/dev/null && ctx7 --version 2>/dev/null | head -1 || echo "ctx7-not-installed"
# Standalone CLIs
command -v gsd &>/dev/null && gsd --version 2>/dev/null | head -1 || echo "gsd-not-installed"
command -v rtk &>/dev/null && rtk --version 2>/dev/null | head -1 || echo "rtk-not-installed"
# Project signals (run from project root)
ls package.json pyproject.toml Cargo.toml go.mod 2>/dev/null | head -5
grep -rl "next\|react\|vue\|prisma\|supabase" package.json 2>/dev/null | head -3 || true
find . -name "*.tsx" -o -name "*.jsx" 2>/dev/null | head -3 | wc -l
find . -name "docker-compose*" -o -name "Dockerfile" 2>/dev/null | head -3 | wc -l
# Animation lib status (motion / motion-v) — read-only detection
if [ -f "$HOME/.claude/lib/animation-lib-check.sh" ]; then
source "$HOME/.claude/lib/animation-lib-check.sh"
detect_anim_eligibility # outputs '<status>|<package>|<reason>'
is_anim_lib_installed || echo "anim-lib-not-installed"
fi
# Monorepo detection (current dir + parent dirs for sub-package context)
ls apps/ packages/ services/ workspaces/ 2>/dev/null | head -5
ls pnpm-workspace.yaml turbo.json nx.json lerna.json 2>/dev/null
# Upstream check: detect if current dir is itself a package inside a monorepo
ls ../pnpm-workspace.yaml ../turbo.json ../nx.json ../../turbo.json ../../pnpm-workspace.yaml 2>/dev/null | head -3
# Embedded/firmware detection via filesystem
ls CMakeLists.txt platformio.ini 2>/dev/null
ls *.ld *.lds linker*.ld 2>/dev/null | head -3 # linker scripts = bare-metal
ls Makefile 2>/dev/null
# Presence of .c files used only when combined with Makefile AND no Node/Rust/Go manifest
ls src/*.c 2>/dev/null | head -3
ls package.json Cargo.toml go.mod pubspec.yaml setup.py pyproject.toml 2>/dev/null | head -1 # counterindicators (ecosystem present = not bare embedded)
```
--- ---
## PHASE 2 — ANALYZE $ARGUMENTS ## PHASE 2 — ANALYZE
Detect signals from the project description and filesystem scan: Detect signals from REQUEST + the PROBE REPORT fields:
| Signal | How to detect | | Signal | How to detect |
|---|---| |---|---|
@@ -82,8 +46,8 @@ Detect signals from the project description and filesystem scan:
| `skill-creation` | "create a skill", "new skill", "custom skill", `/plugin-dev:create-plugin` in description | | `skill-creation` | "create a skill", "new skill", "custom skill", `/plugin-dev:create-plugin` in description |
| `embedded` | "firmware", "bare-metal", "microcontroller", "STM32", "ESP32", "RTOS", "driver", "kernel", "bootloader" in description; **or** `platformio.ini` present; **or** linker script (`*.ld`, `*.lds`) present; **or** `Makefile` + `src/*.c` + no `package.json`/`Cargo.toml`/`go.mod`/`setup.py`/`pyproject.toml` (C project without standard ecosystems). Note: `.c` files with a Rust/Node/Go manifest = FFI binding, NOT embedded. | | `embedded` | "firmware", "bare-metal", "microcontroller", "STM32", "ESP32", "RTOS", "driver", "kernel", "bootloader" in description; **or** `platformio.ini` present; **or** linker script (`*.ld`, `*.lds`) present; **or** `Makefile` + `src/*.c` + no `package.json`/`Cargo.toml`/`go.mod`/`setup.py`/`pyproject.toml` (C project without standard ecosystems). Note: `.c` files with a Rust/Node/Go manifest = FFI binding, NOT embedded. |
| `simple` | single file, hotfix, quick script, no frontend, no deploy | | `simple` | single file, hotfix, quick script, no frontend, no deploy |
| `anim-lib-eligible` | output of `detect_anim_eligibility` starts with `eligible|` (React/Vue/Svelte stack) | | `anim-lib-eligible` | PROBE REPORT `ANIM` field: `eligibility=eligible|…` (React/Vue/Svelte stack) |
| `anim-lib-installed` | `is_anim_lib_installed` returns 0 (any of motion / motion-v / framer-motion / gsap / lottie-react / react-spring / popmotion / auto-animate present) | | `anim-lib-installed` | PROBE REPORT `ANIM` field: `installed=<lib>` (any of motion / motion-v / framer-motion / gsap / lottie-react / react-spring / popmotion / auto-animate) |
--- ---
@@ -146,70 +110,11 @@ ACTION REQUIRED? YES / NO
> packages itself — it just states the status. Installation happens in > packages itself — it just states the status. Installation happens in
> `/init-project` STEP 5e (auto) or `/onboard` STEP 2.5 (opt-in). > `/init-project` STEP 5e (auto) or `/onboard` STEP 2.5 (opt-in).
## PHASE 4 — AUTO-ACTIVATION (when called from /init-project or /ship-feature) > **Apply, confirmation, and rollback are the DISPATCHER'S job** —
> `lib/plugin-gate.md` steps 4-5 (main loop: present, ACTION-REQUIRED stop,
After presenting RECOMMENDATIONS, if any plugin has ⚡ ENABLE status: > PROPOSED-CHANGES confirmation, toggle + rollback). This agent only
1. List the changes to apply: > recommends and emits the EXACT toggle commands. It never applies, never
``` > asks the user (it cannot — it is dispatched).
PROPOSED CHANGES:
⚡ Enable ui-ux-pro-max (frontend detected, complexity 65%)
⚡ Pre-fetch ctx7 docs for next.js, prisma
Apply these changes? (yes / no / customize)
```
2. On "yes" → apply changes (rename .disabled dirs, update MCP config).
3. On "customize" → user picks which to apply.
4. On "no" → proceed with current config.
**Never auto-activate without showing the list and getting confirmation.**
### Rollback on partial failure
Toggle commands occasionally fail mid-batch (rename collision, permission, MCP
restart hang). Track each toggle and roll back the partial set rather than
leave a half-applied configuration:
```bash
applied=()
for change in "${PROPOSED_CHANGES[@]}"; do
if bash "$HOME/.claude/lib/toggle-external.sh" enable "$change"; then
applied+=("$change")
else
echo "❌ failed to enable $change — rolling back ${#applied[@]} prior change(s)"
for prior in "${applied[@]}"; do
bash "$HOME/.claude/lib/toggle-external.sh" disable "$prior" \
|| echo "⚠️ rollback of $prior also failed — manual cleanup required: see ~/.claude/plugins/cache"
done
exit 1
fi
done
```
Surface to the user:
```
✅ Applied N change(s).
```
Or, on failure:
```
⚠️ Toggle failed at change <name>. Rolled back the N prior change(s).
To inspect manually: ls ~/.claude/plugins/cache; bash ~/.claude/lib/toggle-external.sh list
Re-run /plugin-check after fixing the underlying cause (e.g. permissions).
```
### Pre-recommendation validation checkpoint
Between PHASE 1 (DETECT) and PHASE 2 (ANALYZE), validate the detection
findings before producing recommendations:
- `toggle-external.sh list` returned non-empty AND each listed plugin's
directory exists in `~/.claude/plugins/cache` or `~/.agents/skills/`.
- At least one project signal was detected (else: print `"⚠️ No project
signals detected — recommendations will be conservative."` and continue).
- If `toggle-external.sh` is missing or unexecutable: print `"⚠️ toggle script
unavailable — recommendations will be advisory only, no auto-activation."`
and skip PHASE 4 entirely.
--- ---
@@ -418,4 +323,6 @@ or by applying a profile that lists it (e.g. `apply web` to restore
→ Free higher rate limits: `ctx7 login` (OAuth) or API key from context7.com/dashboard → Free higher rate limits: `ctx7 login` (OAuth) or API key from context7.com/dashboard
→ Type "force" to proceed without context7 (not recommended for fast-evolving libs) → Type "force" to proceed without context7 (not recommended for fast-evolving libs)
Never modify files. If action required → stop and wait. If not → say "proceed". Never modify files. Never ask the user. Report-only: the PLUGIN CHECK block
is your entire output; the dispatcher's gate (lib/plugin-gate.md) owns the
stop/proceed decision and every state change.
+90
View File
@@ -0,0 +1,90 @@
---
name: plugin-probe
description: Mechanical detection probe — dispatched by lib/plugin-gate.md BEFORE the plugin-advisor reasoner. Runs the CLI/filesystem probes, reports raw facts as a PROBE REPORT. No analysis, no recommendations.
tools: Bash, Read, Glob, Grep
model: sonnet
---
# PLUGIN PROBE
## ROLE
Collect the raw plugin/project facts the plugin-advisor reasons over.
Facts only — no signals, no recommendations, no complexity scoring.
## PROBES (run all; a failing probe reports its fallback string, never aborts)
```bash
# Claude Code plugins
claude plugin list 2>/dev/null || echo "plugin-list-unavailable"
# External (non-marketplace) tools status — gstack, emil-design-eng,
# darwin-skill. Managed by lib/toggle-external.sh since
# `claude plugin enable|disable` does not apply to them.
bash "$HOME/.claude/lib/toggle-external.sh" list 2>/dev/null || echo "toggle-external-unavailable"
# Active skill profile — design / dev / qa / audit / minimal / custom.
bash "$HOME/.claude/lib/profile.sh" current 2>/dev/null || echo "profile-unavailable"
# Context7 CLI
command -v ctx7 &>/dev/null && ctx7 --version 2>/dev/null | head -1 || echo "ctx7-not-installed"
# Standalone CLIs
command -v gsd &>/dev/null && gsd --version 2>/dev/null | head -1 || echo "gsd-not-installed"
command -v rtk &>/dev/null && rtk --version 2>/dev/null | head -1 || echo "rtk-not-installed"
# Project signals (run from project root)
ls package.json pyproject.toml Cargo.toml go.mod 2>/dev/null | head -5
grep -rl "next\|react\|vue\|prisma\|supabase" package.json 2>/dev/null | head -3 || true
find . -name "*.tsx" -o -name "*.jsx" 2>/dev/null | head -3 | wc -l
find . -name "docker-compose*" -o -name "Dockerfile" 2>/dev/null | head -3 | wc -l
# Animation lib status (motion / motion-v) — read-only detection
if [ -f "$HOME/.claude/lib/animation-lib-check.sh" ]; then
source "$HOME/.claude/lib/animation-lib-check.sh"
detect_anim_eligibility # outputs '<status>|<package>|<reason>'
is_anim_lib_installed || echo "anim-lib-not-installed"
fi
# Monorepo detection (current dir + parent dirs for sub-package context)
ls apps/ packages/ services/ workspaces/ 2>/dev/null | head -5
ls pnpm-workspace.yaml turbo.json nx.json lerna.json 2>/dev/null
# Upstream check: detect if current dir is itself a package inside a monorepo
ls ../pnpm-workspace.yaml ../turbo.json ../nx.json ../../turbo.json ../../pnpm-workspace.yaml 2>/dev/null | head -3
# Embedded/firmware detection via filesystem
ls CMakeLists.txt platformio.ini 2>/dev/null
ls *.ld *.lds linker*.ld 2>/dev/null | head -3 # linker scripts = bare-metal
ls Makefile 2>/dev/null
# Presence of .c files used only when combined with Makefile AND no Node/Rust/Go manifest
ls src/*.c 2>/dev/null | head -3
ls package.json Cargo.toml go.mod pubspec.yaml setup.py pyproject.toml 2>/dev/null | head -1 # counterindicators (ecosystem present = not bare embedded)
# Checkpoint inputs (consumed by lib/plugin-gate.md's validation checkpoint)
[ -x "$HOME/.claude/lib/toggle-external.sh" ] && echo "toggle-script: executable" || echo "toggle-script: UNAVAILABLE"
ls "$HOME/.claude/plugins/cache" 2>/dev/null | head -10
ls "$HOME/.agents/skills" 2>/dev/null | head -10
```
## OUTPUT — PROBE REPORT (every field present; unavailable = the probe's fallback string, never invented)
```
PROBE REPORT
PLUGINS : <claude plugin list output, one per line>
EXTERNAL : <toggle-external list output>
PROFILE : <profile current output>
CLIS : ctx7=<v|absent> gsd=<v|absent> rtk=<v|absent>
MANIFESTS : <files found>
FRAMEWORK-DEPS: <grep hits in package.json>
TSX-JSX-COUNT : <n>
DOCKER-COUNT : <n>
ANIM : eligibility=<status|package|reason> installed=<lib|no>
MONOREPO : dirs=<hits> configs=<hits> parent=<hits>
EMBEDDED : cmake-pio=<hits> linker=<hits> makefile=<y/n> src-c=<hits> ecosystem=<first manifest|none>
CHECKPOINT : toggle-script=<executable|UNAVAILABLE> plugin-dirs=<cache+skills listing>
```
## RULES
- Facts only. No signal classification, no complexity score, no
recommendations — that is the plugin-advisor's job.
- Never modify files. Never install anything. Never ask the user
(you cannot — report facts instead).
- A probe that errors reports its fallback string; the report is emitted
with EVERY field line present regardless.
+5 -11
View File
@@ -123,16 +123,10 @@ INSTALL : ✅ / ❌ <error>
BUILD : ✅ / ❌ <error> BUILD : ✅ / ❌ <error>
DOCKER BUILD: ✅ / ⚠️ not verified / N/A DOCKER BUILD: ✅ / ⚠️ not verified / N/A
STRUCTURE: <tree> STRUCTURE: <tree>
READY: <N> v1 features | entry points ✅ | config ✅ | CLAUDE.md ✅ | README → doc-syncer | settings ✅ READY: <N> v1 features | entry points ✅ | config ✅ | CLAUDE.md ✅ | README → init-project STEP 5b | settings ✅
``` ```
--- > No doc step here (BDR-077): the scaffolder produces NO docs. The README
> bootstrap is init-project STEP 5b's job — a doc-syncer `MODE: audit`
## PHASE 6 — DOC SYNC (automatic) > (opus) → `MODE: patch` (sonnet) dispatch pipeline owned by the
> orchestrator, never an inline-load inside this executor.
**INLINE-LOAD** `$HOME/.claude/agents/doc-syncer.md` — continue AS
doc-syncer in THIS SAME context (you *become* it). This is an inline load,
NOT a subagent dispatch: the `Agent` tool is not involved (which is why
this agent correctly omits `Agent` from its `tools:`). Execute in
automatic mode:
`auto-mode scope: <list of all files created during scaffolding>`
+41
View File
@@ -2,6 +2,7 @@
name: seo-analyzer name: seo-analyzer
description: 'Classical SEO audit agent (Google, Bing) — dispatched from /seo. Live audit: Core Web Vitals, on-page, technical, local SEO, legal (FR). Emits a fix bundle (dispatcher applies) + scored report. AI/GEO → geo-analyzer agent.' description: 'Classical SEO audit agent (Google, Bing) — dispatched from /seo. Live audit: Core Web Vitals, on-page, technical, local SEO, legal (FR). Emits a fix bundle (dispatcher applies) + scored report. AI/GEO → geo-analyzer agent.'
tools: Read, Edit, Write, Bash, Grep, Glob, WebFetch, WebSearch tools: Read, Edit, Write, Bash, Grep, Glob, WebFetch, WebSearch
model: opus
--- ---
# SEO — Classical Search Engines audit, fix & strategy # SEO — Classical Search Engines audit, fix & strategy
@@ -23,6 +24,38 @@ $ARGUMENTS
--- ---
## MODE DETECTION (BDR-077 — pipeline modes around the dispatcher)
The dispatcher (/seo) runs this agent as a 3-stage pipeline; /harden and
/onboard may still run it single-shot. Parse the MODE line in the prompt:
- **`MODE: collect`** — dispatched `model: "sonnet"` (mechanical/standard
collection; the call-site override takes precedence over the opus pin).
Runs STEP 0-5 ONLY, writes every gathered signal (tech context, tool
availability, live-audit raw results, on-page inventory + sampling
frame) to the run-scoped, gitignored `.audit/seo-signals-<RUNID>.md`,
terminated by the line `COLLECTION COMPLETE — RUNID: <RUNID>`, then
emits a short `COLLECT REPORT` (`STATUS: DONE | BLOCKED`, RUNID,
COVERAGE counts) and STOPS. No scoring, no findings, no bundle.
- **`MODE: judge`** — runs on the opus frontmatter pin (audit judgment).
FIRST loads `.audit/seo-signals-<RUNID>.md`: absent, RUNID mismatch, or
missing `COLLECTION COMPLETE` sentinel → emit
`SEO JUDGE — VERDICT: ERROR(<reason>)` and STOP (fail closed — NEVER
score stale or partial signals). Then runs STEP 6-11 on the signals +
the dispatcher-fed context and emits the scoring blocks + findings +
action plan + triage batches as its report. No bundle, no SEO.md.
- **`MODE: template`** — dispatched `model: "sonnet"`. INPUT: the
dispatcher-fed context + the judge's report VERBATIM (never re-derive a
score or re-judge a finding). Runs STEP 12-14: FIX BUNDLE + sentinel,
report file, envelope.
- **No MODE line** — legacy single-shot: all steps in sequence on the
opus pin (used by /harden narrow-scope and /onboard report-only).
Every mode receives the full dispatcher CONTEXT block (LRN-126 — the
STEP 1-2 business/tech context is consumed by all later steps).
---
## STEP 0 — AUDIT DEPTH ## STEP 0 — AUDIT DEPTH
**First action.** If a parent skill (`/seo` dispatcher) passed depth **First action.** If a parent skill (`/seo` dispatcher) passed depth
@@ -743,6 +776,10 @@ Validate:
--- ---
> **MODE BOUNDARY — `MODE: collect` ends at STEP 5**: write the signals
> file + `COLLECTION COMPLETE — RUNID: <RUNID>` terminal line, emit the
> COLLECT REPORT, stop. STEP 6-11 below are `MODE: judge` territory.
## STEP 6 — EXTERNAL PRESENCE AUDIT `[FULL only, local business only]` ## STEP 6 — EXTERNAL PRESENCE AUDIT `[FULL only, local business only]`
**Skip if not a local business** (pure SaaS, content-only → jump to STEP 7). **Skip if not a local business** (pure SaaS, content-only → jump to STEP 7).
@@ -1181,6 +1218,10 @@ Do not proceed to STEP 12 until this plan is printed.
--- ---
> **MODE BOUNDARY — `MODE: judge` ends at STEP 11** (scoring + findings +
> plan + batches reported, nothing serialized). STEP 12-14 below are
> `MODE: template` territory, operating on the judge report verbatim.
## STEP 12 — EMIT FIX BUNDLE `[both]` ## STEP 12 — EMIT FIX BUNDLE `[both]`
**You do NOT apply fixes and you do NOT dispatch any sub-agent.** Same **You do NOT apply fixes and you do NOT dispatch any sub-agent.** Same
+1
View File
@@ -2,6 +2,7 @@
name: validator-analyzer name: validator-analyzer
description: Web standards audit agent — W3C HTML validity (validator.nu), W3C CSS validity (jigsaw.w3.org), WCAG 2.1 accessibility (axe-core, pa11y, WAVE). Dispatched from /web-validate. Produces scored .claude/audits/VALIDATE.md report with concrete diffs for auto-fixable issues and user actions for judgment-required fixes. Complementary to /harden (security), /seo (indexability), /geo (AI extraction). description: Web standards audit agent — W3C HTML validity (validator.nu), W3C CSS validity (jigsaw.w3.org), WCAG 2.1 accessibility (axe-core, pa11y, WAVE). Dispatched from /web-validate. Produces scored .claude/audits/VALIDATE.md report with concrete diffs for auto-fixable issues and user actions for judgment-required fixes. Complementary to /harden (security), /seo (indexability), /geo (AI extraction).
tools: Read, Edit, Write, Bash, Grep, Glob, WebFetch tools: Read, Edit, Write, Bash, Grep, Glob, WebFetch
model: sonnet
--- ---
# Validator — W3C + WCAG audit # Validator — W3C + WCAG audit
-385
View File
@@ -1,385 +0,0 @@
# Deploy Skill — Implementation Plan
> **Superseded by BDR-054** (`52f6678`): the shipped skill has NO `NEXT.sh` file and NO
> AskUserQuestion hand-back — see `skills/deploy/SKILL.md` for current behavior. This
> plan is kept as historical record; do not implement its NEXT.sh/hand-back sections.
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Build a `deploy` skill — a per-project shell runbook that re-instantiates from the delta since the last deploy, hands control to the user for out-of-band execution, resumes cold (even in a new session), and learns from deploy errors in place.
**Architecture:** A surgical-commit helper (`lib/deploy-commit.sh`, allowlist-scoped to `.claude/deploy/`) is the foundation. Five per-project artifacts under `.claude/deploy/` carry runbook, incident ledger, deploy oracle, in-flight bridge, and the instantiated checklist. The skill is a two-moment SKILL.md (before → user deploys out-of-band → after, on the user's report), resumable cold from the JSON bridge per the `audit-delta` state-file convention. Bootstrap scaffolds the runbook for a project that has none.
**Tech Stack:** Bash (helper + git), Markdown (SKILL.md + runbook + ledger), JSON (oracle + bridge). No new runtime deps — Claude reads JSON natively in skill steps; the helper never parses JSON.
## Global Constraints
- Surgical commits only: `deploy-commit.sh` commits via explicit argv pathspec, never `git add -A`. (mirror BDR-034/036)
- Allowlist scope = `.claude/deploy/` ONLY; any other path is a loud rc-4 refusal. Inverse of `doc-commit.sh`'s `.claude/**` exclusion (BDR-022). Verified: real `doc-commit.sh` returns rc 4 on `.claude/deploy/PROCEDURE.md`.
- Delta = `git diff --name-only <base_sha> HEAD` — **explicit two endpoints, no dots** (two-dot ≡ this; three-dot undercounts — verified). Never `git rev-list` ancestry (phantom deltas on rebase — verified).
- First-deploy detection = `[ -f .claude/deploy/STATE.json ]` (deterministic). NEVER `git describe` (hard-errors rc 128 on no tag — verified).
- Resume convention = `audit-delta`: "the state file is the only memory between runs; never infer prior scope from context." Bridge read at STEP 0.
- Helper inherits from `lib/memory-commit.sh`/`lib/doc-commit.sh`: rc 3 on unsafe git state (detached/merge/rebase/cherry-pick), short-hash on stdout only on a real commit, per-file changed-paths filter, diagnostics to stderr.
- User executes the deploy out-of-band (prod ssh) — the skill NEVER runs deploy commands itself.
- Registries/spec language English; the spec of record is `docs/specs/2026-06-27-deploy-skill-design.md`.
---
## Decisions resolved at plan time
**§10 (cross-session state) — TRANCHÉ: separate bridge artifact.**
- Bridge = `.claude/deploy/PENDING.json` (JSON), **distinct from the ephemeral `NEXT.sh`**, **uncommitted** (transient local working state; gitignored). Schema:
```json
{ "base_sha": "<deployed STATE sha>", "target_sha": "<HEAD at instantiation>",
"delta": ["supabase/migrations/0033_x.sql", "docker-compose.yml"],
"step_reached": "awaiting-user", "started_at": "<ISO-8601>", "runbook_rev": "<PROCEDURE.md commit sha>" }
```
- Follows `audit-delta` ("state file is the only memory between runs"). Resolves the n°1↔n°3 coupling: NEXT.sh stays ephemeral per §3; the bridge persists and carries base+target+delta so moment 3 lays the correct marker and capitalizes the correct incident — **without re-parsing shell**, readable cold.
- Form-novelty (mid-flow pause-resume) is new → `writing-skills` formalizes the convention in Task 3.
- **LIMIT (acknowledged, not to be discovered):** `PENDING.json` is gitignored ⇒ cold-resume is **same-machine only** — it does not survive a clone or a move to another machine. Acceptable because a project's deploys run from one local; recorded as a constraint, not assumed away.
**§8 item 1 — tag push:** annotated tag `git tag -a deploy/<YYYY-MM-DD> <target_sha> -m "<summary>"` laid in MARK (success). **Project knob `# @config push_deploy_tags=true|false`** in the `PROCEDURE.md` header (default `false`): when true, MARK runs `git push origin deploy/<date>` — always **best-effort/non-fatal** (the push never blocks the deploy; tag is a bookmark, STATE.json is the oracle). Same-day re-deploy → suffix `-N`.
**§8 item 2 — INCIDENTS ID/name:** `.claude/deploy/INCIDENTS.md`, append-only, entries `DEP-NNN` (next = `grep '^## DEP-' | max+1`), fields mirror `blockers.md`: date, step, error (verbatim), root cause, fix. Resolution derivable from git: the commit that adds the entry IS the fix (atomic patch+incident); recover via `git log -S 'DEP-NNN' -- .claude/deploy/INCIDENTS.md`. Name confirmed `INCIDENTS.md` (not `ERRORS-LEARNED.md`).
**§8 item 3 — `@delta:` grammar:** directives on a runbook step's preceding comment line, patterns matched against the delta file list. `glob=` carries TWO required semantics (a single "checklist-only" reading was REJECTED — it breaks the game example, where step 3 runs `psql -f 0033` THEN `psql -f 0034` = one command PER file):
- `# @delta:<name> glob=<pat>:each` — **repeat**: emit the step's command once per delta file matching `<pat>` (e.g. `psql -f <each>`).
- `# @delta:<name> glob=<pat>:list` — **checklist**: emit the command once, with matching files as `# VERIFY:` items (e.g. `supabase migration up`).
- `# @delta:<name> when=<pat>[,<pat>...]` — **conditional**: include the step only if the delta intersects any pattern (e.g. rebuild when compose/Dockerfile changed).
- Patterns are git-pathspec/shell-glob; comma-separates alternatives. **Un-annotated step = fixed**, always emitted verbatim. The exact `:each`/`:list` keyword spelling is DEFERRED to `writing-skills` (Task 3); both semantics are mandatory.
**§8 item 4 — frontmatter / gates:**
```yaml
name: deploy
description: |
Use when deploying a project via its per-project runbook — instantiates the
delta since last deploy, hands off for out-of-band execution, resumes cold,
learns from errors.
Triggers: "deploy", "déploie", "run the deploy", "ship to prod", "deploy runbook".
allowed-tools: [Read, Write, Edit, Bash, Grep, Glob, AskUserQuestion]
```
Gate vocabulary reused from `capitalize`/`client-handover`: `all / pick <IDs> / edit <ID> / skip-all`. Gates marked **[GATE]** in Task 3.
---
## File Structure
- Create `lib/deploy-commit.sh` — surgical commit helper, allowlist `.claude/deploy/`. (Task 1)
- Create `lib/tests/deploy-commit.test.sh` — real-git behavioral tests. (Task 1)
- Create `skills/deploy/SKILL.md` — the two-moment skill. (Task 3)
- Create `templates/deploy/PROCEDURE.md` — annotated starter runbook (scaffold source). (Task 2/4)
- Create `templates/deploy/INCIDENTS.md` — empty ledger header. (Task 2)
- Modify `.gitignore` — ignore `.claude/deploy/NEXT.sh` and `.claude/deploy/PENDING.json`. (Task 2)
- Per-project, created at runtime (NOT in this repo): `.claude/deploy/{PROCEDURE.md, INCIDENTS.md, STATE.json, PENDING.json, NEXT.sh}`.
**Artifact lifecycle:**
| Artifact | Committed? | Lifecycle |
|---|---|---|
| `PROCEDURE.md` | yes (deploy-commit) | in-place edits (learning) |
| `INCIDENTS.md` | yes (deploy-commit) | append-only `DEP-NNN` |
| `STATE.json` | yes (deploy-commit) | overwritten on success = oracle |
| `PENDING.json` | **no** (gitignored) | written at hand-back, deleted on success = cold-resume bridge |
| `NEXT.sh` | **no** (gitignored) | regenerated per deploy, ephemeral checklist |
---
### Task 1: `lib/deploy-commit.sh` — surgical commit helper (FOUNDATION, TDD)
**Files:**
- Create: `lib/deploy-commit.sh`
- Test: `lib/tests/deploy-commit.test.sh`
**Interfaces:**
- Produces: `deploy-commit.sh pending <file>...` → exit 0 if any passed file in-scope has changes, else 1. `deploy-commit.sh commit "<msg>" <file>...` → commits ONLY passed in-scope files, prints short hash on stdout; rc 0 success, rc 1 clean/no-op, rc 3 unsafe git state, rc 4 out-of-scope path.
- Consumes: nothing (foundation).
- [ ] **Step 1: Write the failing test harness**
```bash
# lib/tests/deploy-commit.test.sh
#!/usr/bin/env bash
set -u
H="$(cd "$(dirname "$0")/.." && pwd)/deploy-commit.sh"
pass=0; fail=0
mkrepo() { local d; d=$(mktemp -d); git -C "$d" init -q; git -C "$d" config user.email t@t;
git -C "$d" config user.name t; mkdir -p "$d/.claude/deploy"; printf 'x\n' >"$d/seed";
git -C "$d" add seed; git -C "$d" commit -q -m seed; printf '%s' "$d"; }
check() { if [ "$2" = "$3" ]; then pass=$((pass+1)); else fail=$((fail+1));
printf 'FAIL %s: got[%s] want[%s]\n' "$1" "$2" "$3"; fi; }
d=$(mkrepo); printf 'run\n' >"$d/.claude/deploy/PROCEDURE.md"
out=$( cd "$d" && bash "$H" commit "docs(deploy): t" .claude/deploy/PROCEDURE.md ); rc=$?
check T1-rc "$rc" 0
check T1-committed-only "$(git -C "$d" show --name-only --format= HEAD)" ".claude/deploy/PROCEDURE.md"
check T1-hash-nonempty "$([ -n "$out" ] && echo y || echo n)" y
d=$(mkrepo); printf 'b\n' >"$d/src.txt"
( cd "$d" && bash "$H" commit "x" src.txt ) >/dev/null 2>&1; check T2-out-of-scope-rc "$?" 4
d=$(mkrepo)
( cd "$d" && bash "$H" commit "x" ".claude/deploy/../memory/secret" ) >/dev/null 2>&1
check T3-traversal-rc "$?" 4
d=$(mkrepo); printf 'p\n' >"$d/.claude/deploy/PROCEDURE.md"; printf 's\n' >"$d/src.txt"
( cd "$d" && bash "$H" commit "x" .claude/deploy/PROCEDURE.md src.txt ) >/dev/null 2>&1
check T4-mixed-refuses-all "$?" 4
check T4-nothing-committed "$(git -C "$d" rev-list --count HEAD)" 1
d=$(mkrepo); git -C "$d" checkout -q --detach
printf 'p\n' >"$d/.claude/deploy/PROCEDURE.md"
( cd "$d" && bash "$H" commit "x" .claude/deploy/PROCEDURE.md ) >/dev/null 2>&1
check T5-unsafe-rc "$?" 3
d=$(mkrepo)
( cd "$d" && bash "$H" pending .claude/deploy/PROCEDURE.md ); check T6-pending-clean-rc "$?" 1
d=$(mkrepo); printf 'p\n' >"$d/.claude/deploy/PROCEDURE.md"
printf 'i\n' >"$d/.claude/deploy/INCIDENTS.md"; printf '{}\n' >"$d/.claude/deploy/STATE.json"
( cd "$d" && bash "$H" commit "docs(deploy): learn" .claude/deploy/PROCEDURE.md \
.claude/deploy/INCIDENTS.md .claude/deploy/STATE.json ) >/dev/null 2>&1
check T7-atomic-rc "$?" 0
check T7-three-files "$(git -C "$d" show --name-only --format= HEAD | grep -c deploy)" 3
printf 'PASS=%s FAIL=%s\n' "$pass" "$fail"; [ "$fail" -eq 0 ]
```
- [ ] **Step 2: Run the test, verify it FAILS**
Run: `bash lib/tests/deploy-commit.test.sh`
Expected: FAIL (helper absent) — every check fails or the harness errors on missing `lib/deploy-commit.sh`.
- [ ] **Step 3: Implement `lib/deploy-commit.sh`**
```bash
#!/usr/bin/env bash
# deploy-commit.sh — surgical commit for the .claude/deploy/ runbook family.
# Allowlist scope = .claude/deploy/ ONLY (inverse of doc-commit's .claude exclusion).
set -u
_in_git_repo() { git rev-parse --is-inside-work-tree >/dev/null 2>&1; }
_unsafe_state() { # 0 = unsafe
local g; g=$(git rev-parse --git-dir 2>/dev/null) || return 0
git symbolic-ref -q HEAD >/dev/null 2>&1 || return 0 # detached HEAD
[ -e "$g/MERGE_HEAD" ] || [ -d "$g/rebase-merge" ] || \
[ -d "$g/rebase-apply" ] || [ -e "$g/CHERRY_PICK_HEAD" ] && return 0
return 1
}
_out_of_scope() { # 0 = forbidden, 1 = in scope
case "$1" in
*..*) return 0 ;; # traversal — forbidden FIRST
.claude/deploy/*) return 1 ;; # allowed
*) return 0 ;; # everything else forbidden
esac
}
_scope_violations() { local p; for p in "$@"; do _out_of_scope "$p" && printf '%s\n' "$p"; done; }
_changed_only() { # echo passed files that actually have changes
local p; for p in "$@"; do
[ -n "$(git status --porcelain -- "$p" 2>/dev/null)" ] && printf '%s\n' "$p"; done
}
cmd="${1:-}"; shift || true
_in_git_repo || { echo "deploy-commit: not a git repo" >&2; exit 2; }
case "$cmd" in
pending)
[ "$#" -gt 0 ] || { echo "deploy-commit: pending needs file args" >&2; exit 2; }
[ -n "$(_changed_only "$@")" ] && exit 0 || exit 1 ;;
commit)
msg="${1:-}"; shift || true
[ -n "$msg" ] && [ "$#" -gt 0 ] || { echo "deploy-commit: commit needs <msg> <file>..." >&2; exit 2; }
viol=$(_scope_violations "$@")
if [ -n "$viol" ]; then
{ echo "deploy-commit: REFUSED — path(s) outside .claude/deploy/ allowlist:";
printf ' - %s\n' $viol;
echo "deploy-commit: NOTHING committed. Caller must pass only .claude/deploy/ files."; } >&2
exit 4
fi
_unsafe_state && { echo "deploy-commit: unsafe git state (detached/merge/rebase) — not committing" >&2; exit 3; }
mapfile -t changed < <(_changed_only "$@")
[ "${#changed[@]}" -gt 0 ] || exit 1
git commit -q -m "$msg" -- "${changed[@]}" || { echo "deploy-commit: git commit failed" >&2; exit 1; }
git rev-parse --short HEAD ;;
*) echo "usage: deploy-commit.sh pending <file>... | commit \"<msg>\" <file>..." >&2; exit 2 ;;
esac
```
- [ ] **Step 4: Run the test, verify it PASSES**
Run: `bash lib/tests/deploy-commit.test.sh`
Expected: `PASS=12 FAIL=0` (exit 0).
- [ ] **Step 5: shellcheck**
Run: `shellcheck lib/deploy-commit.sh lib/tests/deploy-commit.test.sh`
Expected: clean (matches repo Health Stack norm).
- [ ] **Step 6: Commit**
```bash
git add lib/deploy-commit.sh lib/tests/deploy-commit.test.sh
git commit -m "feat(deploy): deploy-commit.sh — allowlist surgical commit for .claude/deploy/"
```
---
### Task 2: Artifacts + bridge formats (§10 materialized)
**Files:**
- Create: `templates/deploy/PROCEDURE.md`, `templates/deploy/INCIDENTS.md`
- Modify: `.gitignore`
**Interfaces:**
- Produces: the on-disk shapes the skill reads/writes — `PROCEDURE.md` annotation grammar, `INCIDENTS.md` `DEP-NNN` template, `STATE.json` and `PENDING.json` schemas.
- Consumes: nothing.
- [ ] **Step 1: Write `templates/deploy/PROCEDURE.md`** (annotated starter — fixed steps verbatim, dynamic steps annotated)
```bash
#!/usr/bin/env bash
# === deploy runbook (reference) — NOT run directly. Instantiated to NEXT.sh per delta. ===
# Fixed steps run every deploy; `# @delta:` steps re-instantiate from the delta.
# @config push_deploy_tags=false
# NOTE grammar: glob=<pat>:each repeats the command per matching file (e.g. psql -f <each>);
# glob=<pat>:list runs once + lists matching files as VERIFY items; when=<pat,...> is conditional.
# 1) backup BEFORE any forward-only migration
ssh "$DEPLOY_HOST" 'pg_dump "$DB" > ~/backups/pre-deploy-$(date +%F-%H%M).sql' # VERIFY: dump size > 0
# @delta:migrations glob=supabase/migrations/*.sql:list
# 2) apply NEW migrations (one command; skill lists the delta migrations to VERIFY)
ssh "$DEPLOY_HOST" 'supabase migration up' # VERIFY: "Applied" for each
# @delta:rebuild when=docker-compose*.yml,Dockerfile,Dockerfile.*
# 3) rebuild + restart services (only if build inputs changed)
ssh "$DEPLOY_HOST" 'docker compose up -d --build' # VERIFY: docker compose ps healthy
# @delta:deps when=package.json,*lock*,requirements.txt,pyproject.toml
# 4) install deps (only if manifests changed)
ssh "$DEPLOY_HOST" 'cd app && npm ci' # VERIFY: exit 0
# 5) reload cache + smoke test (fixed)
ssh "$DEPLOY_HOST" 'systemctl reload app'
curl -fsS https://$DEPLOY_HOST/health # VERIFY: HTTP 200
```
- [ ] **Step 2: Write `templates/deploy/INCIDENTS.md`** (ledger header)
```markdown
# Deploy incidents (append-only) — DEP-NNN
<!-- One entry per incident. Next ID = grep '^## DEP-' | max+1. Mirrors blockers.md. -->
<!-- Resolution = the commit that adds this entry (atomic patch+incident). Recover: git log -S 'DEP-NNN' -- .claude/deploy/INCIDENTS.md -->
<!-- ## DEP-NNN — <step> failed
- date: YYYY-MM-DD
- step: <runbook step + label>
- error: `<verbatim error>`
- cause: <root cause>
- fix: <what changed in PROCEDURE.md> -->
```
- [ ] **Step 3: Record the JSON schemas** (no parsing in shell — Claude reads them in skill steps)
`STATE.json` (committed oracle, overwritten on success):
```json
{ "deployed_sha": "<sha>", "deployed_at": "<ISO-8601>", "outcome": "ok",
"tag": "deploy/<YYYY-MM-DD>" }
```
`PENDING.json` (gitignored bridge, deleted on success): schema as in "Decisions resolved at plan time / §10".
- [ ] **Step 4: Update `.gitignore`**
```gitignore
# deploy: transient per-deploy state (the runbook/ledger/oracle ARE committed)
.claude/deploy/NEXT.sh
.claude/deploy/PENDING.json
```
- [ ] **Step 5: Verify templates are well-formed**
Run: `bash -n templates/deploy/PROCEDURE.md && grep -c '^# @delta:' templates/deploy/PROCEDURE.md`
Expected: no syntax error; `3` annotations.
- [ ] **Step 6: Commit**
```bash
git add templates/deploy/PROCEDURE.md templates/deploy/INCIDENTS.md .gitignore
git commit -m "feat(deploy): runbook/ledger templates + bridge schemas + gitignore transient state"
```
---
### Task 3: `skills/deploy/SKILL.md` — the two-moment skill (REQUIRES writing-skills)
> **At this task, invoke `superpowers:writing-skills`** to shape SKILL.md to house conventions AND to formalize the **cross-session cold-resume** form (deploy's defining novelty; `audit-delta` is the state-file precedent, `client-handover` only an in-context pause). The step behaviors below are the contract; writing-skills governs structure/frontmatter/spine.
**Files:**
- Create: `skills/deploy/SKILL.md`
**Interfaces:**
- Consumes: `lib/deploy-commit.sh` (Task 1); artifact shapes (Task 2).
- Produces: the runtime behavior. STEP spine below.
**STEP spine (each = a SKILL.md section; [GATE] = mandatory stop):**
- [ ] **STEP 0 — PRE-FLIGHT + RESUME BRANCH.** Read `.claude/deploy/PENDING.json` FIRST (state file = only memory between runs).
- `PENDING.json` present → **RESUME**: jump to STEP 3 with its `{base, target, delta, step_reached}` (do not recompute).
- else `PROCEDURE.md` absent → **BOOTSTRAP** (Task 4).
- else → FRESH: continue STEP 1.
- [ ] **STEP 1 — DELTA.** `base = STATE.json.deployed_sha` (or, if `STATE.json` absent, first-deploy = full runbook). `git diff --name-only <base> HEAD` → delta file list. `target = git rev-parse HEAD`.
- [ ] **STEP 2 — INSTANTIATE + [GATE].** Expand `PROCEDURE.md`: emit fixed steps verbatim; expand `@delta:glob=…:each` steps by repeating the command per matching delta file, and `@delta:glob=…:list` steps once with matching files as `# VERIFY:` items; include `@delta:when=` steps only if the delta intersects. Read `INCIDENTS.md` and prepend matching `# PRE-WARN: DEP-NNN …` notes. Write `NEXT.sh`. **[GATE]** present `NEXT.sh` → `all / edit / skip-all`. On approve: write `PENDING.json` (`step_reached: awaiting-user`), then **hand back** (AskUserQuestion: "Run NEXT.sh step by step. Report back: Deployed OK / Failed at step X / Not yet").
- [ ] **STEP 3 — RESUME / REACT** (entry point on the user's report; may be a fresh session).
- "Deployed OK" → STEP 5.
- "Failed at step X: <err>" → STEP 4.
- "Not yet" → re-state pending, stop.
- [ ] **STEP 4 — LEARN + [GATE] + ATOMIC COMMIT.** Diagnose. Draft: (a) in-place `PROCEDURE.md` patch to step X; (b) `INCIDENTS.md` append `DEP-NNN` (error verbatim). **[GATE]** `all / pick / edit / skip-all` (significant edit). On approve: write both, then **one atomic** `bash lib/deploy-commit.sh commit "docs(deploy): patch <step> — recovered from <err>" .claude/deploy/PROCEDURE.md .claude/deploy/INCIDENTS.md`. The commit that adds `DEP-NNN` IS its resolution (derive via git later). Then bump `PENDING.json.runbook_rev` to the new `PROCEDURE.md` commit sha (keep `step_reached` at X). **Resume = REGENERATE `NEXT.sh` from `step_reached` against the PATCHED runbook** (steps X…end — X+1…end never ran), NOT replay a single step. The bumped `runbook_rev` is exactly the trigger: runbook changed ⇒ prior `NEXT.sh` is stale ⇒ regenerate. Re-present via STEP 2's hand-back.
- [ ] **STEP 5 — MARK (success).** Write `STATE.json` (`deployed_sha = PENDING.target_sha`, outcome ok, tag). `git tag -a deploy/<date> <target> -m "<summary>"`; **if `@config push_deploy_tags=true`** then `git push origin deploy/<date>` (best-effort, non-fatal). `bash lib/deploy-commit.sh commit "chore(deploy): mark <date> @ <short>" .claude/deploy/STATE.json`. **Delete `PENDING.json`** (+ `NEXT.sh`). Report.
- [ ] **Verification scenarios** (dry-run walkthroughs, no prod):
- First deploy (no `STATE.json`): full runbook fires; STATE laid; PENDING deleted.
- Delta deploy: only changed-bucket steps instantiate; `git diff` form is `<base> HEAD`.
- **Cold resume**: write a `PENDING.json` by hand, start `deploy` in a *fresh* context → STEP 0 detects it, resumes at STEP 3 from disk alone (no conversation memory).
- Failure→learn: report "failed at step X" → patch + DEP append committed atomically (one sha, both files).
- [ ] **Commit:** `git add skills/deploy/SKILL.md && git commit -m "feat(deploy): two-moment cross-session skill (resumes cold from PENDING.json)"`
---
### Task 4: Bootstrap (project without a runbook)
**Files:**
- Modify: `skills/deploy/SKILL.md` (STEP 0 BOOTSTRAP branch)
**Interfaces:**
- Consumes: `templates/deploy/*` (Task 2); STEP spine (Task 3).
- [ ] **Step 1 — BOOTSTRAP branch + [GATE].** When `PROCEDURE.md` absent, offer two paths (AskUserQuestion):
- **Paste** — user provides an existing runbook → adopt verbatim, then propose `@delta:` annotations for migration/build/deps steps.
- **Scaffold** — detect artifacts (`supabase/migrations/`, `docker-compose*.yml`/`Dockerfile`, `package.json`/lockfiles, `.env*`) + short interview (ssh host, backup cmd, health URL, rollback note) → fill `templates/deploy/PROCEDURE.md`.
- **[GATE]** present drafted `PROCEDURE.md` → `all / edit / skip-all`. On approve: write `PROCEDURE.md` + empty `INCIDENTS.md`; `bash lib/deploy-commit.sh commit "feat(deploy): bootstrap runbook" .claude/deploy/PROCEDURE.md .claude/deploy/INCIDENTS.md`. First deploy then proceeds (no STATE.json ⇒ full runbook).
- [ ] **Step 2 — Verify:** dry-run on a repo with `supabase/migrations/` + `docker-compose.yml` present → scaffold proposes migration + rebuild steps annotated; on a bare repo → interview-only path.
- [ ] **Commit:** `git add skills/deploy/SKILL.md && git commit -m "feat(deploy): bootstrap — paste-or-scaffold initial runbook"`
---
## Gates identified
- **[GATE] STEP 2** — approve instantiated `NEXT.sh` before hand-back.
- **[GATE] STEP 4** — approve runbook patch + `DEP-NNN` incident before the atomic learning commit.
- **[GATE] STEP 0/Task 4** — approve scaffolded `PROCEDURE.md` before first write.
- **Hand-back (STEP 2→3)** — AskUserQuestion is the resume point; the user executes out-of-band.
- **Task gates** — each Task ends test-green + shellcheck-clean + committed before the next (deps: 1 → 2 → 3 → 4).
## Self-review
- **Spec coverage:** 4 artifacts + bridge (§3/§10) → Task 2; STATE-oracle + `<base> HEAD` delta (§4) → Task 1 constraints + STEP 1; runbook+INCIDENTS learning, atomic couple (§5) → STEP 4; `deploy-commit.sh` inverse allowlist (§6) → Task 1; bootstrap (§7) → Task 4; two-moment cold resume (§10) → STEP 0/2/3 + PENDING.json. All §8 items resolved above. ✓
- **Placeholder scan:** none — helper code, test code, schemas, annotation grammar all concrete.
- **Type consistency:** `STATE.json.deployed_sha` (STEP 1 base, STEP 5 write), `PENDING.json.{base_sha,target_sha,delta,step_reached}` (STEP 0 read, STEP 2 write, STEP 4 update), `deploy-commit.sh commit "<msg>" <file>...` (Tasks 1/3/4) — names align.
- **Open at execution (not assumed):** the `writing-skills` consultation in Task 3 may rename/restructure SKILL.md sections to match the formalized cold-resume convention, and finalizes the `@delta:` `:each`/`:list` keyword spelling (both semantics mandatory); STEP behaviors and the §6 helper contract above are fixed regardless.
## Execution Handoff
Build order is strict by dependency: **Task 1 (helper, foundation) → Task 2 (formats) → Task 3 (skill, writing-skills) → Task 4 (bootstrap)**.
@@ -1,165 +0,0 @@
# Deploy skill — design spec
> **Superseded by BDR-054** (`52f6678`): the shipped skill has NO `NEXT.sh` file and NO
> AskUserQuestion hand-back — see `skills/deploy/SKILL.md` for current behavior. This
> spec is kept as historical record; do not implement its NEXT.sh/hand-back sections.
- **Date:** 2026-06-27
- **Status:** Design approved (5 knobs settled). **No skill code written yet.** Next step = implementation plan.
- **Scope:** A new `deploy` skill = a per-project shell RUNBOOK that lives in `.claude/deploy/`, gets re-instantiated from the delta since the last deploy, and LEARNS from deploy errors in place.
## 1. Vision — deployment memory that learns
Three moments:
1. **BEFORE** — produce the *instantiated* runbook: reference runbook + delta since last deploy, parameterized steps rewritten with the real artifacts (e.g. the migration step lists the migrations actually added since last deploy, not the runbook's examples).
2. **DURING** — the **user executes out-of-band** (prod ssh — Claude must not run it) and reports `deployed and tested` OR `failed at step X, here is the error` → fix together until success.
3. **AFTER** — on confirmed success: (a) if errors were hit + fixed, update the reference runbook so the next deploy does not repeat them; (b) lay the marker "deployed up to here" for the next diff.
Structural ancestor in the corpus: `client-handover` (BEFORE baseline → DURING user-deploy gate via `AskUserQuestion` → AFTER validate + react). No existing skill owns a learning per-project runbook — clean gap, no `.claude/deploy/` precedent.
## 2. Locked decisions
| # | Knob | Decision |
|---|------|----------|
| 1 | Marker / oracle | **STATE file is the oracle** (deployed SHA), **annotated tag** added as a human bookmark only |
| 2 | Learning storage | **In-place runbook edits + append-only `INCIDENTS.md`** (distinct jobs, atomic coupling) |
| 3 | Parameterization | **`# @delta:` annotations** bind dynamic steps to path-patterns; un-annotated steps are fixed |
| 4 | Bootstrap | **Offer both** — user pastes an existing runbook OR skill scaffolds via artifact detection + interview |
| 5 | Execution model | **`NEXT.sh` is a step-by-step CHECKLIST** — runnable shell, but driven by hand with manual `# VERIFY:` gates; never `bash NEXT.sh` unattended |
**Why #5 is design-time, not impl:** the execution model is load-bearing for moments 2 and 3. Moment 2 is defined as "user reports *failed at step X*", and moment 3's LEARN loop must know *which* step failed to patch it. A single `bash NEXT.sh` blob collapses both into "exited non-zero somewhere" and can strand a prod deploy (migrations, restarts) in partial state with no step control. Checklist is *entailed* by the three-moment structure, not merely safer.
Treated as settled corollaries: user executes out-of-band; a **new** `lib/deploy-commit.sh` helper (existing helpers cannot commit the runbook — see §6, verified).
## 3. Architecture
```
.claude/deploy/
PROCEDURE.md reference runbook — fixed shell + `# @delta:` annotated steps (edited IN-PLACE)
INCIDENTS.md DEP-NNN incident ledger: date, step, error verbatim, root cause,
fix (APPEND-ONLY; resolution = introducing commit, derive via git)
STATE.json deployed SHA + timestamp + outcome — the diff oracle (overwritten each deploy)
NEXT.sh instantiated runbook — EPHEMERAL, not committed ; run STEP-BY-STEP
(checklist, manual # VERIFY: gates) — never `bash NEXT.sh` unattended
lib/deploy-commit.sh surgical commit, allowlist = .claude/deploy/ , rc3 unsafe-git guard, short-hash stdout
Skill STEP spine (PRE-FLIGHT -> PROPOSE+GATE -> WRITE+COMMIT, house style):
0 PRE-FLIGHT runbook present? absent -> bootstrap (paste | scaffold+interview)
1 DELTA STATE absent -> first deploy = full runbook ; else diff <STATE_SHA> HEAD
2 INSTANTIATE expand @delta steps + read INCIDENTS pre-warns -> NEXT.sh -> GATE
3 (user executes out-of-band; reports "done" | "failed at step X: <err>")
4 LEARN on failure: patch PROCEDURE step + append DEP-NNN -> GATE -> deploy-commit (ATOMIC)
5 MARK on success: write STATE@sha ; annotate + push tag ; optional doc
```
## 4. Delta mechanism — verified (git 2.53.0)
All three facts re-run live before writing this spec; observed output recorded, not assumed.
**First-deploy detection = STATE-absent, deterministic. `describe` is off the detection path.**
```
[ -f .claude/deploy/STATE.json ] => exit 1 (absent = first deploy) <- THE detector
git describe --tags --match 'deploy/*' => fatal: No names found ; exit 128 <- only the reason NOT to use describe
[ -f .claude/deploy/STATE.json ] => exit 0 (present = delta path)
```
**Delta = `git diff --name-only <STATE_SHA> HEAD`** (two explicit endpoints; no dots, so it cannot be misread as three-dot).
```
LINEAR git diff --name-only <sha> HEAD => 0033_new.sql, svc.yml (== two-dot == three-dot; merge-base == STATE)
DIVERGED two-dot sideA sideB => fileA.txt, fileB.txt (both endpoints = true tree delta)
DIVERGED three-dot sideA...sideB => fileB.txt (merge-base — UNDERCOUNTS)
```
Two-dot/explicit-endpoints is the literal tree difference between the deployed tree and HEAD = what deploy needs. It is also rebase-robust: an orphaned marker still yields the correct tree diff, whereas `git rev-list A..B` (ancestry) reports phantom deltas after history rewrite (LRN-054's trap; verified in an earlier run). **Never use `rev-list` ancestry for the artifact list.**
**delta -> steps:** `# @delta:<kind>` annotations bind a dynamic step to the path-pattern that feeds it; the diff buckets straight into steps:
```
# @delta:migrations glob=supabase/migrations/*.sql
# @delta:rebuild when=docker-compose*.yml,Dockerfile
# @delta:deps when=package.json,*lock*
```
## 5. Learning model — runbook + INCIDENTS, non-redundant
| Artifact | Job | Lifecycle |
|---|---|---|
| `PROCEDURE.md` | The corrected procedure you run. A fix is baked into the step so the next run cannot repeat it. | in-place |
| `INCIDENTS.md` | The incident ledger; **read at BEFORE-time to pre-warn** ("0033 hit a lock timeout last deploy; runbook already carries `--timeout`, watch for it"). | append-only |
The pre-warn read is the function `git log` serves badly — that is why the ledger is not duplication. This mirrors the memory system's own split (append-only `journal.md`/`blockers.md` alongside in-place TODO/code).
**Coupling invariant:** one incident → **one in-place `PROCEDURE.md` patch + one `INCIDENTS.md` append, committed atomically in a single `deploy-commit.sh` call.** Never one without the other (mirrors BDR-034/036 "couple the commit to the integration step"). Significant patch (changes a prod path) → surface + approve before writing.
## 6. `lib/deploy-commit.sh` — new helper, inverse `.claude/` rule (verified)
Neither existing helper can commit the runbook — confirmed live:
```
REAL doc-commit.sh .claude/deploy/PROCEDURE.md => rc 4 "REFUSED — out-of-scope ... BDR-022 ... NOTHING committed"
REAL memory-commit.sh pending (deploy changed) => rc 1 (ignores it; allowlist = .claude/memory|tasks only)
```
`doc-commit.sh` is built to keep `.claude/**` *out* of public-doc commits; `.claude/deploy/` is under `.claude/`, so reuse is not just blocked, it is semantically wrong. `deploy-commit.sh` needs the **inverse** rule: a TARGET allowlist for `.claude/deploy/*`, modeled on `memory-commit.sh` (rc 3 unsafe-git guard, short-hash on stdout, `chore(deploy):`/`docs(deploy):` messages).
Allowlist guard — traversal reject ordered FIRST. Prototype matrix verified live:
```sh
_in_deploy_scope() {
case "$1" in
*..*) return 1 ;; # reject path traversal FIRST
.claude/deploy/*) return 0 ;; # ALLOW the deploy family only
*) return 1 ;; # reject everything else
esac
}
```
```
ALLOW .claude/deploy/{PROCEDURE.md,INCIDENTS.md,STATE}
REJECT .claude/memory/* .claude/tasks/* .claude/secret CLAUDE.md src/*
REJECT .claude/deploy (bare dir, no slash)
REJECT .claude/deploy-other/x (trailing-slash requirement closes prefix confusion)
REJECT .claude/deploy/../memory/secret (traversal closed by *..* matched first)
```
## 7. Bootstrap
`STEP 0 PRE-FLIGHT`: `PROCEDURE.md` present? Absent → bootstrap, two offered paths:
1. **Paste** — user supplies an existing runbook (the game example); skill adopts + annotates it.
2. **Scaffold** — skill detects deploy artifacts (migrations dir, compose/Dockerfile, package scripts, `.env`) + a short interview (ssh target, backup cmd, rollback note) → writes an annotated `PROCEDURE.md`.
First deploy has no marker → STATE-absent ⇒ full runbook fires; then lay STATE at the deployed SHA. The first deploy *is* the creation of the runbook + the first marker.
## 8. Open items (for the implementation plan)
> `NEXT.sh` execution model resolved → decision #5 (checklist), promoted to design-time.
- Tag push: tags don't push by default → AFTER step should `git push --tag deploy/<date>` or remind.
- `INCIDENTS.md` ID/format detail (mirror `blockers.md` `DEP-NNN`); confirm name vs `ERRORS-LEARNED.md`.
- `@delta:` annotation grammar (glob= vs when=) — finalize the small DSL.
- Frontmatter `allowed-tools` set; STEP gate wording reuse from `capitalize`/`client-handover`.
## 9. Build sequencing & a structural flag
**Two distinct disciplines, in order — do not conflate:**
1. `writing-plans` — global task ordering (helper → skill → bootstrap), dependencies, gates. The build plan.
2. → execution →
3. At the *skill* task ONLY: `writing-skills` — the discipline for the SKILL.md itself (structure, frontmatter, spine, config conventions). Used WHEN we reach the skill task, **not before** (it does not fire at plan time).
**Structural flag for `writing-skills` to resolve — do NOT assume the linear-spine convention suffices:**
deploy's spine is unusual — **two parts split by out-of-band execution**: STEP 0–2 before → *user deploys by hand* → STEP 4–5 after, on the `done`/`failed` report. A skill that **hands back control mid-run and resumes**.
Preliminary recon (confirm at the skill task — NOT verified now):
- The 6 completion flux (close, ship-feature, feat, bugfix, hotfix, commit-change) appear linear one-shot — synchronous gates at most, no out-of-band hand-back.
- The relevant precedent is OUTSIDE those 6: `client-handover` already hands back — a synchronous "Deploy done?" `AskUserQuestion` pause (STEP 5) — but it holds state in *conversation context*, not on disk.
- deploy's genuinely-new bit *may* be **disk-bridged resume** (`NEXT.sh` + `STATE` on disk as the bridge) — but **whether `NEXT.sh` alone suffices to resume cross-session is an OPEN design question, not a settled answer** (see §10). An earlier draft of this spec framed it as resolved; it is not. `writing-skills` must establish the convention (how to mark "I wait for your return here", detect + resume a pending deploy, hold state across the gap) — confirm there, do not assume the linear mould suffices.
## 10. Open design question (DESIGN-TIME, unresolved) — state across the two moments
deploy is a **two-moment skill**: moments 0–2 (BEFORE) → user deploys out-of-band → moment 3 (AFTER) on the `done`/`failed` report. **The report may arrive in a different session.** So the design must answer how state crosses the gap and what moment 3 must know to resume correctly.
> **`skill deux-temps, état entre temps = [à concevoir : NEXT.sh seul suffit-il pour reprendre cross-session ?]`**
Sub-questions (to settle when we resume — NOT now, NOT assumed):
- **What must the bridge record?** Moment 3 must (a) lay the correct marker = `STATE ← target sha`, and (b) capitalize the correct incident (which step, which delta). HEAD may have moved since NEXT.sh was generated → "current HEAD" is unsafe. The bridge must persist at least **{base STATE sha, target sha, delta manifest}** — inside NEXT.sh (header block) or a sidecar (`.claude/deploy/PENDING`)? Undecided.
- **Resume detection (re-entrancy):** STEP 0 PRE-FLIGHT must detect "a deploy is pending, awaiting your report" — likely *pending-bridge present + STATE not advanced to target* — and branch RESUME (ask done/failed) vs FRESH. Is moment 3 a new `deploy` call that re-detects from disk, or a `deploy --report`? Undecided.
- **Ephemeral vs persistent tension — LINKED to sub-question 1 (not independent).** §3 calls NEXT.sh "EPHEMERAL, not committed", yet a cross-session bridge MUST survive on disk. So: **if the bridge must persist, NEXT.sh-as-bridge is impossible while NEXT.sh stays ephemeral.** Likely *binary* resolution at plan time — either (a) NEXT.sh becomes persistent (contradicts §3), or (b) the bridge is a **separate** "deploy-in-progress" artifact `{base/target/delta}` distinct from NEXT.sh. Settle with `writing-skills`. (Uncommitted local state is fine; note the single-machine assumption — an uncommitted bridge won't follow a clone.)
- **Form-novelty — deploy's DEFINING characteristic: cross-session COLD resume.** `client-handover` is a *near* precedent, not exact: it hands back **in-context** (same conversation, state held in memory). deploy must resume with the **context lost** — so the **disk alone must carry everything to resume cold**. No existing skill resumes without context; that is what sets deploy apart, and it makes sub-question 1 **load-bearing** (disk must suffice for a cold restart). deploy likely introduces a NEW skill form → `writing-skills` establishes the convention. Confirm there.
**Next step:** `writing-plans` to turn this spec into an implementation plan (helper first, then skill); at the skill task, `writing-skills` to shape it to convention and **resolve the §10 two-moment state question** — which is design-time, deferred only because we are stopped here, not because it is impl detail.
File diff suppressed because it is too large Load Diff
@@ -1,143 +0,0 @@
# Model routing — reflection inline (big model) / execution pinned (Sonnet) — design
**Date**: 2026-07-15 · **Status**: approved (user, 2026-07-15) · **Branch**: `feature/model-routing`
**Lifecycle**: transient planning artifact (BDR-065) — committed during the run, deleted post-merge.
## Principle
The session model is assumed to be a big reasoning model (Fable 5, or Opus when
Fable is unavailable). Everything that **thinks** — brainstorming, planning,
technical decisions, audits, loop decisions — runs INLINE in the main
conversation, or in subagents that inherit the session model. Everything that
**executes** a ready-made plan — writing code, applying fix bundles, commits,
deliverable rendering — runs on Sonnet-pinned subagents. A blocking gate
enforces the "session = big model" assumption at the entry of every reflection
orchestrator.
User verdicts baked in (2026-07-14/15):
- Scope = hybrid: ship-feature/init-project execution → sonnet; `/feat`
re-architected (plan inline → dispatch executor); bugfix/hotfix stay fully
inline (BDR-050 conserved for them).
- Gate = BLOCKING, not advisory.
- Audit agents inherit the session model (no opus pin); the gate extends to
audit orchestrators.
- verifier + security-auditor KEEP `model: sonnet` (job9 decision confirmed).
- client-handover-writer → sonnet (requires converting its inline-load to a
true dispatch; human gates relocate to the main loop).
## 1. Blocking model gate
New `lib/model-check.sh`: resolves the current session model from
`settings.json` (physical path resolution — LRN-023 class), normalizes
(`claude-fable-5[1m]` → fable, `claude-opus-*` → opus, sonnet, haiku), prints
`big|small|unknown`. Exit 0 = big, 2 = small, 3 = unknown.
New `lib/model-gate.md` snippet (same include pattern as `lib/design-gate.md`):
run the check; `small` → STOP the skill: "session model is <X> — reflection
requires Fable/Opus. Switch with /model, then relaunch." `unknown` →
fail-visible: show the raw value, ask the user to confirm or abort (BDR-025
doctrine — unknown never silently passes).
Wired as a STEP 0 line in the reflection orchestrators:
`ship-feature, init-project, feat, bugfix, onboard, seo, geo, web-validate,
harden, audit-delta, tour, code-clean`.
NOT wired in: `hotfix` (trivial by definition), `commit-change`, `doc`,
`status`, `release-candidate`.
Caveats to prove at implementation time:
- `/model` mid-session rewrites settings.json (LRN-098 observed it once —
re-prove with a live flip-test before trusting the source).
- The helper itself must be flip-tested (LRN-096: an unproven guard is a
vacuous guard).
## 2. Frontmatter pins (`agents/*.md`)
| Agent | Before | After | Rationale |
|---|---|---|---|
| feater | (inherit) | **sonnet** | executor as subagent: seo/geo L1 applier + new /feat dispatch |
| hotfixer | (inherit) | **sonnet** | L1 applier (seo/geo/web-validate); /hotfix inline unaffected (pin inert on inline load) |
| client-handover-writer | opus | **sonnet** | deliverable executor; pin becomes EFFECTIVE only with §5 dispatch conversion (today's opus pin is inert — the agent is inline-loaded) |
| analyzer | haiku | **(none — inherit)** | analysis feeds the plan = reflection; runs big via the session model |
| verifier | sonnet | keep | F1 confirmed (job9) |
| security-auditor | sonnet | keep | F1 confirmed (job9) |
| seo-analyzer, geo-analyzer, validator-analyzer | (inherit) | keep (inherit) | audit = reflection = session model; covered by the gate |
| code-cleaner | (inherit) | keep (inherit) | audit phase = reflection; fixes hand off to refactorer (sonnet) via CODE-CLEAN-SCOPE.md (job9 H1) |
| doc-syncer, onboarder, scaffolder, refactorer, interviewer, plugin-advisor | sonnet | keep | workers/executors |
| status-reporter | haiku | keep | mechanical collector |
| bugfixer, commit-changer | (inherit) | keep | inline-only playbooks — a pin would be inert |
## 3. `/feat` re-architecture (partial supersede of BDR-050 — feat only)
`skills/feat/SKILL.md` absorbs the reflection: analyze-before-plan, design
gate, MINI-PLAN, contract (`lib/contract-interview.md`) — all inline. Then
dispatches `Agent(subagent_type="feater")` (sonnet via pin) with: the
contract, the plan, the branch name, repo conventions.
`agents/feater.md` is rewritten as a pure executor: implement the plan to the
letter, run project checks, commit (no attribution trailers), return a
structured summary. No user interaction inside feater (subagents cannot ask) —
every decision must be closed pre-dispatch.
The verify-secure loop moves out of feater.md into the /feat main loop
(LRN-083 invariant: loop decisions live in the main loop): fresh verifier →
ECARTS → re-dispatch feater with the verdict deltas, bounded 3×; then the
security gate. Escalation paths unchanged.
## 4. SDD execution pinned (ship-feature STEP 4, init-project STEP 8)
One instruction line in each SKILL.md: every implementation subagent
dispatched under `superpowers:subagent-driven-development` MUST carry
`model: "sonnet"` in the Agent call. No fork of the superpowers skill — the
main loop emits the Agent calls and controls the params.
## 5. client-handover conversion (inline-load → true dispatch)
`skills/client-handover/SKILL.md`: collect params inline (URL, logo, options),
then `Agent(subagent_type="client-handover-writer")` — the sonnet pin becomes
effective. Human gates (per-axis threshold escalation, overrides) RELOCATE to
the main loop: the writer returns a structured `GATE NEEDED` status instead of
asking; the dispatcher asks the user and re-dispatches (or continues via
SendMessage) with the decision. `AskUserQuestion` is removed from the writer's
tools.
OPEN VERIFY POINT: the writer's own nested dispatches (seo/harden re-runs as
general-purpose subagents) — verify at implementation what nested children
inherit (session model vs parent model). If they inherit the sonnet parent,
the re-run audits violate the principle → force the model explicitly in those
nested dispatches or lift them to the main loop.
## 6. web-validate fixes → L1 applier
STEP 3 stops applying fixes via inline Edit; dispatches `hotfixer` (sonnet)
with the fix bundle — same pattern as seo/geo (BDR-061 alignment).
## 7. Memory / doc / tests
- New BDR: model-routing principle (reflection inline big / executors sonnet /
blocking gate); partial supersede of BDR-050 (feat only); records F1
(verifier/security stay sonnet) and the analyzer haiku→inherit change.
- README: agent-model table refresh. CHANGELOG Unreleased entry.
- Tests: flip-tests for `model-check.sh` (fable[1m] / opus / sonnet / garbage
fixtures); gate STOP proven on a small-model fixture (LRN-096); /feat smoke
on a throwaway repo (LRN-079): plan inline → dispatch carries sonnet →
verify loop decided in main loop; grep census: no executor dispatch without
an effective pin.
## Out of scope / accepted deviations
- `/doc` and `/commit-change` stay inline on the session model (judgment and
execution interleaved; converting them buys little). Revisit under quota
pressure.
- bugfix/hotfix fully inline (BDR-050 conserved).
- No per-agent "fable-else-opus" fallback exists in the harness — the session
model IS the fallback mechanism; the gate is its backstop.
## Risks
- Model strings in settings.json may change shape with CC updates →
model-check must return `unknown` (fail-visible), never guess.
- feater as a subagent loses main-conversation context → the plan becomes the
contract; weak plans cost verify-loop iterations. Mitigation:
contract-interview stays mandatory in /feat.
- Nested model inheritance under client-handover-writer unknown → §5 verify
point.
-65
View File
@@ -1,65 +0,0 @@
#!/usr/bin/env bash
# config-protection.sh
#
# PreToolUse hook (Edit|Write|MultiEdit). Blocks edits to this config's
# quality-gate files — the guardrails an agent must not silently weaken to make
# an error "pass" (permission/hook registry, gitflow enforcement, the git
# pre-commit guard, the hooks themselves, the test suite, the health diagnostic,
# lint config). Exit 2 blocks the tool call and feeds the message back to the
# model (Claude Code PreToolUse contract).
#
# It fires only on the model's Edit/Write tool calls — never on shell-level file
# ops (the cp/ln in install.sh, link.sh), so bootstrap/deploy is unaffected.
#
# One-shot escape hatch: create .claude/.config-edit-ok (CWD-relative) with a
# NON-EMPTY reason inside; the hook logs the reason, consumes (rm) the sentinel,
# and allows that single edit. It never persists — a lingering sentinel would be
# a footgun. Discipline, per CLAUDE.global.md "Root causes only. No temp fixes.": fix
# the code, don't loosen the gate. Fails OPEN (exit 0) on parse failure so it can
# never wedge editing.
set -euo pipefail
log="${HOME}/.claude/logs/config-protection.log"
sentinel="${PWD}/.claude/.config-edit-ok"
input="$(cat)"
path="$(printf '%s' "$input" \
| python3 -c 'import sys, json; print(json.load(sys.stdin).get("tool_input", {}).get("file_path", ""))' \
2>/dev/null || true)"
[ -z "$path" ] && exit 0
# Guardrail files, matched by path suffix (covers both the repo source and the
# deployed ~/.claude copy). Precise: lib/gitflow.sh only, not gitflow-migrate.sh.
case "$path" in
*/.claude/settings.json|*/.claude/settings.local.json|*/claude/settings.json) ;;
*/lib/gitflow.sh|*/.githooks/*|*/doctor.sh) ;;
*/hooks/*.sh|*/lib/tests/*) ;;
*/.shellcheckrc|*/.markdownlint.json|*/.editorconfig) ;;
*) exit 0 ;;
esac
# One-shot sentinel bypass: non-empty reason required; consumed on sight.
if [ -f "$sentinel" ]; then
reason="$(head -c 500 "$sentinel" 2>/dev/null | tr '\n\r\t' ' ' || true)"
rm -f "$sentinel"
if printf '%s' "$reason" | grep -q '[^[:space:]]'; then
mkdir -p "$(dirname "$log")"
printf '%s\tBYPASS\t%s\treason=%s\n' "$(date -Iseconds)" "$path" "$reason" >> "$log"
exit 0
fi
printf '%s\n' "[config-protection] .claude/.config-edit-ok had an EMPTY reason -> refused (sentinel consumed). Recreate it with a non-empty reason." >&2
exit 2
fi
cat >&2 <<EOF
[config-protection] BLOCKED edit to a quality-gate file:
$path
This is a guardrail (permission/hook registry, gitflow enforcement, git
pre-commit guard, a hook, the test suite, health diagnostic, or lint config).
Don't weaken the gate to make an error pass — fix the root cause instead
(global CLAUDE.md: "Root causes only. No temp fixes."). To make one intended edit,
create .claude/.config-edit-ok with a non-empty reason; it is logged and
consumed (one-shot).
EOF
exit 2
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# ctx7-reminder.sh
#
# UserPromptSubmit hook. When the current project uses fast-moving libs
# (lib/fast-libs.sh) it injects ONE reminder per session to consult ctx7
# (find-docs skill) before coding against their APIs, pointing at the
# .ctx7-cache/ state. Closes the ad-hoc-coding gap: find-docs' description
# fires on doc *questions* and ship-feature/init-project pre-fetch, but
# nothing covered a plain "add a useEffect here" prompt (BDR-078; second
# deliberate ctx7 surface, scoped refinement of BDR-053 single-surface).
#
# Soft nudge: always exits 0, never blocks. Stable-tech projects (no
# manifest, or no fast-lib match) stay silent.
set -euo pipefail
input="$(cat)"
field() { # $1=json key — extracted from hook stdin, empty on failure
printf '%s' "$input" | python3 -c \
"import sys,json; print(json.load(sys.stdin).get('$1',''))" \
2>/dev/null || true
}
prompt="$(field prompt)"
case "$prompt" in
'<task-notification>'*) exit 0 ;; # harness turn, not a user request
esac
cwd="$(field cwd)"
[ -n "$cwd" ] || cwd="$PWD"
# Cheap bail-out before any lib work: no manifest → no fast-libs.
[ -f "$cwd/package.json" ] || [ -f "$cwd/requirements.txt" ] \
|| [ -f "$cwd/pyproject.toml" ] || exit 0
# One fire per session: the doctrine holds for the whole session,
# repeating it on every prompt would be token spam.
session_id="$(field session_id)"
sentinel="${TMPDIR:-/tmp}/.ctx7-reminder-${session_id:-nosession}"
[ -e "$sentinel" ] && exit 0
# Resolve the lib next to this hook (repo layout), fall back to the
# installed copy — both paths exist through the link.sh symlinks.
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
libsh="${script_dir}/../lib/fast-libs.sh"
[ -f "$libsh" ] || libsh="${HOME}/.claude/lib/fast-libs.sh"
[ -f "$libsh" ] || exit 0
libs="$(bash "$libsh" detect "$cwd" 2>/dev/null || true)"
[ -n "$libs" ] || exit 0
status="$(bash "$libsh" cache-status "$cwd" 2>/dev/null || true)"
: > "$sentinel" || true
list="$(printf '%s' "$libs" | tr '\n' ' ' | sed 's/ *$//')"
if [ "$status" = "fresh" ]; then
printf '📚 Fast-moving libs in this project (%s) — fresh .ctx7-cache/ present: read the matching cache file before relying on their APIs.\n' "$list"
else
printf '📚 Fast-moving libs in this project (%s) — .ctx7-cache/ %s: consult ctx7 (find-docs skill) before writing code against their APIs. Stable techs need nothing.\n' "$list" "${status:-missing}"
fi
exit 0
+40
View File
@@ -644,6 +644,46 @@ if command -v ctx7 &>/dev/null; then
# (~490 tok/session, job1 F10). Purge it unconditionally so re-runs and # (~490 tok/session, job1 F10). Purge it unconditionally so re-runs and
# manual `ctx7 setup` invocations stay rule-free. # manual `ctx7 setup` invocations stay rule-free.
rm -f "$HOME/.claude/rules/context7.md" rm -f "$HOME/.claude/rules/context7.md"
# BDR-078: re-apply the coverage extension to the generated skill — the
# before-writing-code trigger (description) + the cache-first rule (body).
# The dist is machine-owned (gitignored, regenerated on fresh clones), so
# the durable copy of this patch lives HERE. Idempotent: grep-guarded.
_fd="$HOME/.claude/skills/find-docs/SKILL.md"
if [ -f "$_fd" ] && ! grep -q 'fast-libs.sh detect' "$_fd"; then
if python3 - "$_fd" <<'PY'
import sys
p = sys.argv[1]
s = open(p, encoding="utf-8").read()
DESC = """
Also use BEFORE writing or modifying code that uses a fast-moving library
(anything `bash ~/.claude/lib/fast-libs.sh detect .` reports — React,
Next.js, Prisma, Tailwind, Astro, Svelte…), even when the user asked for
code rather than documentation — unless a fresh `.ctx7-cache/` file already
covers the API involved. Stable technologies (C, C++98, POSIX shell, SQL…)
need no lookup."""
BODY = """
## Cache first
Before any fetch, check the project's `.ctx7-cache/`
(`bash ~/.claude/lib/fast-libs.sh cache-status .`): a fresh (<7 days)
`<lib>*.md` may already answer — read it instead of calling ctx7. When a
`docs` call supports code you are about to write, save the output for the
next consumer:
`npx ctx7@latest docs <id> "<query>" | tee .ctx7-cache/<lib>-<topic>.md`.
"""
i = s.index("\n---", 3) # closing frontmatter fence
s = s[:i] + "\n" + DESC + s[i:]
m = "using the Context7 CLI.\n" # intro line under the H1
j = s.index(m) + len(m) if m in s else len(s)
s = s[:j] + BODY + s[j:]
open(p, "w", encoding="utf-8").write(s)
PY
then
ok "find-docs skill extended (BDR-078 fast-libs trigger + cache-first)"
else
warn "find-docs BDR-078 patch failed — re-run 'make plugin' or patch by hand"
fi
fi
info "Standalone usage: ctx7 docs /vercel/next.js \"middleware\"" info "Standalone usage: ctx7 docs /vercel/next.js \"middleware\""
fi fi
+87
View File
@@ -0,0 +1,87 @@
# Challenge the plan — shared orchestrator include
Runs in the ORCHESTRATOR MAIN LOOP after a plan / reflection is elaborated and
BEFORE it is executed. Turns a fresh plan into a hardened one by attacking it
from three independent angles, then RE-THINKING every aspect a challenger lands.
Loop + synthesis decisions live here, in the main loop (BDR-066: reflection runs
on the big model; `verify-secure-loop.md`: fresh blind gates, decisions in the
loop). It never merges, executes, or edits code — it hardens the plan and hands
it to the orchestrator's existing human gate.
The challenge is ADVISORY into that gate — no new hard block — but a BLOCKER is
never silently carried past: it is either closed by a NAMED plan change or
explicitly deferred for the human.
## Inputs the caller must have ready
- `PLAN`: path to the plan ON DISK. If your plan is still inline (a printed
checklist / diagnosis / fix plan), FIRST persist it to
`.claude/tasks/plans/<date>-<slug>-<HHMM>.md` — the challengers read from disk
and judge blind, exactly like the verifier reads the contract.
- `KIND`: `build-plan` | `proposals` | `fix-bundle` — tunes the lens framing
below; the mechanism is identical.
- `SCOPE`: the files/dirs the plan touches (grounds the critique).
- `CONSTRAINTS` (optional): the decided trade-offs / rejected alternatives from
the design step, so a lens does not re-litigate a settled choice.
Nominal path is cheap for a small, clean plan: three parallel challengers return
SOLID, synthesis is a no-op. It only costs more when a lens lands a real finding
— which is the point.
## DISPATCH — three fresh challengers, in parallel, blind
Dispatch THREE fresh `plan-challenger` subagents IN PARALLEL, one per LENS, each
blind to the others and to this conversation:
```
Agent(subagent_type="plan-challenger", description="challenge:<lens>", prompt="""
PLAN: <the PLAN path>
LENS: <correctness | robustness | simplicity> # one per agent — all three
SCOPE: <SCOPE>
CONSTRAINTS: <CONSTRAINTS, if any>
""")
```
**MODEL (BDR-076, supersedes the BDR-066 inherit):** plan critique is AUDIT
JUDGMENT — the challengers are `model: opus`-pinned in their frontmatter: a big
tier, session-independent, off the session model. The session model (Fable)
keeps only this loop — synthesis, RE-THINK, gate. Never sonnet: that would
silently downgrade the judgment. (The executor gates stay sonnet.)
**Lens framing by `KIND`** (the agent's three lenses, read against the artifact):
- `build-plan` — will it WORK / will it BREAK / is it needlessly COMPLEX.
- `proposals` — are these the RIGHT items & priorities / what did the audit MISS
or under-rate as risk / is the backlog over- or under-scoped.
- `fix-bundle` — will each fix ACHIEVE its goal / could it BREAK or regress the
page / is there a simpler fix, or an unnecessary one.
## FAIL-SAFE — never fail open
A challenger that returns a malformed/empty verdict, a missing `PROOF`, or dies →
retry ONCE with a fresh challenger; a 2nd failure on that lens → STOP and escalate
to the human, NAMING the lens. Never carry "plan challenged" into the gate on a
silently dropped lens (`verify-secure-loop.md`: "a mute verifier is NEVER a PASS").
## SYNTHESIZE + RE-THINK (main loop, big model)
Parse each `CHALLENGE — LENS: … — VERDICT:` line and merge the FINDINGS:
- **Severity-driven, not consensus.** Any `[BLOCKER]` from ANY single lens is
must-address — the lenses are orthogonal, so a lone security/rollback finding
is real, never outvoted by lens-count. Cross-lens agreement only RANKS the MINORs.
- **RE-THINK the aspect the challenge pointed at.** For each BLOCKER (and each
MAJOR you accept): revise the plan on THAT aspect — a NAMED, diffable change to
the plan, never a self-authored "addressed" line. A BLOCKER you consciously keep
is tagged `[deferred <date>]` for the human to accept at the gate.
- **Re-challenge once if the plan materially changed** — a fix can open a new
flaw. Re-persist the revised `PLAN`, dispatch ONE fresh confirmation challenger,
max 1 extra pass, then the gate.
## OUTPUT — into the existing human gate
Feed the orchestrator's gate:
- the REVISED plan, and
- a CHALLENGE SUMMARY: each BLOCKER raised → the named change that closed it;
anything `[deferred]`; and any lens that failed to return.
The human remains the decider.
+13 -8
View File
@@ -17,23 +17,28 @@ and any SIGNIFICANT-gated patch), with the code already committed.
- Orchestrators (ship-feature / init-project): run it BEFORE the FINISH step — otherwise - Orchestrators (ship-feature / init-project): run it BEFORE the FINISH step — otherwise
the doc commit strands outside the merge/PR (the exact bug this fixes). See ORDERING. the doc commit strands outside the merge/PR (the exact bug this fixes). See ORDERING.
doc-syncer runs IN-THREAD (the orchestrator loads it), so the list of files it patched is doc-syncer runs DISPATCHED (BDR-077: `MODE: audit` on opus → dispatcher gate
already in hand — surfaced as `PATCHED_FILES:` in doc-syncer's OUTPUT, ONE PATH PER LINE. → `MODE: patch` on sonnet); its patch-mode report hands the orchestrator BOTH
Pass each line as a SEPARATE argument (see DO step 3). machine blocks: `PATCHED_FILES:` (ONE PATH PER LINE — pass each line as a
SEPARATE argument, see DO step 3) and `CHANGE SUMMARY` (one line per patched
file — the patch context that used to be in-thread now crosses the dispatch
boundary through this block, LRN-126).
## DO ## DO
1. Collect `PATCHED_FILES` — the public-doc paths doc-syncer wrote this run (its OUTPUT 1. Collect `PATCHED_FILES` — the public-doc paths doc-syncer wrote this run (its OUTPUT
block, ONE PATH PER LINE). Empty → nothing to commit; the helper no-ops. block, ONE PATH PER LINE). Empty → nothing to commit; the helper no-ops.
2. Compose — from the patch context the AGENT holds (doc-syncer ran in-thread, so the 2. Compose — from doc-syncer's `CHANGE SUMMARY` block (the patcher held the
agent knows exactly what changed) — BOTH artifacts: patch context and reported it; a dispatched patcher with NO summary block
in its report = incomplete report, re-dispatch rather than invent) —
BOTH artifacts:
- the COMMIT MESSAGE, repo style `docs: <summary> — <flow>` - the COMMIT MESSAGE, repo style `docs: <summary> — <flow>`
(`docs: README features + USAGE flags — ship-feature dark-mode`); (`docs: README features + USAGE flags — ship-feature dark-mode`);
- the CHANGE SUMMARY for the rc 0 surface (e.g. "README features section + USAGE - the CHANGE SUMMARY for the rc 0 surface (e.g. "README features section + USAGE
--export flag"). --export flag") — derived from the block, never a bare file count.
Both are the AGENT's to write — the helper produces NEITHER (its only stdout is the Both are the ORCHESTRATOR's to write — the helper produces NEITHER (its only stdout
hash). This is the load-bearing point of the visible surface: see the rc 0 row. is the hash). This is the load-bearing point of the visible surface: see the rc 0 row.
3. Commit surgically via the helper, passing EXACTLY the patched files — each path as a 3. Commit surgically via the helper, passing EXACTLY the patched files — each path as a
SEPARATE argument (split `PATCHED_FILES` on NEWLINES only), capturing the hash: SEPARATE argument (split `PATCHED_FILES` on NEWLINES only), capturing the hash:
+65
View File
@@ -0,0 +1,65 @@
#!/usr/bin/env bash
# fast-libs.sh — single source of truth for "fast-moving library" detection.
#
# Fast-moving = API churns faster than model training data (React, Next.js,
# Prisma…) → consult ctx7 (find-docs) before coding against it. Stable techs
# (C, C++98, POSIX sh, SQL…) never match: no ctx7 needed (BDR-078).
#
# Consumers: hooks/ctx7-reminder.sh, /ship-feature STEP 0c, /init-project
# STEP 5c, /onboard STEP 3.5, feater/bugfixer executor briefs.
#
# Verbs:
# fast-libs.sh detect [dir] detected libs, one/line; exit 1 if none
# fast-libs.sh cache-status [dir] fresh|stale|missing; exit 0 only if fresh
set -euo pipefail
# Exact npm dependency keys (unscoped). Anchored full-key match — "react"
# must not drag react-icons along.
NPM_EXACT='next|react|react-dom|react-native|expo|prisma|supabase'
NPM_EXACT+='|drizzle-orm|astro|svelte|vue|nuxt|tailwindcss|vite|next-auth'
NPM_EXACT+='|motion|framer-motion|ai|openai|langchain|remix|fastify'
# Scoped npm orgs (@org/…).
NPM_SCOPED='prisma|supabase|astrojs|sveltejs|tanstack|clerk|anthropic-ai'
NPM_SCOPED+='|langchain|remix-run|nestjs|tailwindcss'
# Python distributions (requirements.txt / pyproject.toml).
PY_LIBS='fastapi|pydantic|sqlalchemy|langchain'
CACHE_MAX_AGE_DAYS=7
npm_fast_libs() { # $1=dir — matching dependency keys, one per line
[ -f "$1/package.json" ] || return 0
jq -r '((.dependencies // {}) + (.devDependencies // {})) | keys[]' \
"$1/package.json" 2>/dev/null \
| grep -E "^(${NPM_EXACT})\$|^@(${NPM_SCOPED})/" || true
}
py_fast_libs() { # $1=dir — matching distributions, one per line
grep -hoiE "\b(${PY_LIBS})\b" \
"$1/requirements.txt" "$1/pyproject.toml" 2>/dev/null \
| tr '[:upper:]' '[:lower:]' | LC_ALL=C sort -u || true
}
detect() { # $1=dir — union, sorted unique; exit 1 when empty
local libs
# LC_ALL=C: deterministic order whatever the caller's locale.
libs="$(printf '%s\n%s\n' "$(npm_fast_libs "$1")" "$(py_fast_libs "$1")" \
| sed '/^$/d' | LC_ALL=C sort -u)"
[ -n "$libs" ] || return 1
printf '%s\n' "$libs"
}
cache_status() { # $1=dir — fresh|stale|missing; exit 0 only when fresh
[ -d "$1/.ctx7-cache" ] || { echo missing; return 1; }
if [ -n "$(find "$1/.ctx7-cache" -name '*.md' \
-mtime "-${CACHE_MAX_AGE_DAYS}" -print -quit 2>/dev/null)" ]; then
echo fresh; return 0
fi
echo stale; return 1
}
case "${1:-}" in
detect) detect "${2:-.}" ;;
cache-status) cache_status "${2:-.}" ;;
*) echo "usage: fast-libs.sh detect|cache-status [dir]" >&2; exit 2 ;;
esac
+48
View File
@@ -239,6 +239,7 @@ gitflow_start feature glwork >/dev/null 2>&1
# proving this backstop is NOT gated by the branch-protection check above it) # proving this backstop is NOT gated by the branch-protection check above it)
printf 'aws_access_key_id = AKIA%s\n' "GDR5XRBXYARW2I5N" > secret.txt printf 'aws_access_key_id = AKIA%s\n' "GDR5XRBXYARW2I5N" > secret.txt
git add secret.txt git add secret.txt
# shellcheck disable=SC2034 # gl_out is used in the deferred chk eval strings
gl_out="$(git commit -q -m "add secret" 2>&1)"; gl_rc=$? gl_out="$(git commit -q -m "add secret" 2>&1)"; gl_rc=$?
chk "T16a fake secret on feature branch → blocked" "[ $gl_rc -ne 0 ]" chk "T16a fake secret on feature branch → blocked" "[ $gl_rc -ne 0 ]"
chk "T16a message mentions gitleaks" 'printf "%s" "$gl_out" | grep -qi gitleaks' chk "T16a message mentions gitleaks" 'printf "%s" "$gl_out" | grep -qi gitleaks'
@@ -252,10 +253,57 @@ chk "T16b clean commit still succeeds" 'git commit -q -m "clean work" 2>/dev/nul
# T16c — gitleaks missing from PATH → warn, never block (defense in depth # T16c — gitleaks missing from PATH → warn, never block (defense in depth
# must not become a new single point of failure) # must not become a new single point of failure)
echo clean2 > clean2.txt; git add clean2.txt echo clean2 > clean2.txt; git add clean2.txt
# shellcheck disable=SC2034 # noleaks_out is used in the deferred chk eval strings
noleaks_out="$(PATH=/usr/bin:/bin git commit -q -m "clean work 2" 2>&1)"; noleaks_rc=$? noleaks_out="$(PATH=/usr/bin:/bin git commit -q -m "clean work 2" 2>&1)"; noleaks_rc=$?
chk "T16c missing-gitleaks → still commits (rc0)" "[ $noleaks_rc -eq 0 ]" chk "T16c missing-gitleaks → still commits (rc0)" "[ $noleaks_rc -eq 0 ]"
chk "T16c missing-gitleaks → warns" 'printf "%s" "$noleaks_out" | grep -qi "not installed"' chk "T16c missing-gitleaks → warns" 'printf "%s" "$noleaks_out" | grep -qi "not installed"'
echo "T17 — finish auto-purges transient superpowers artifacts (BDR-065)"
# T17a — feature carrying docs/superpowers spec+plan: purged before merge,
# develop TIP clean, artifacts still recoverable from history (archive property)
newrepo purgefeat; echo a>a; hookon; gitflow_init >/dev/null 2>&1
gitflow_start feature pf >/dev/null 2>&1
mkdir -p docs/superpowers/specs docs/superpowers/plans
echo spec > docs/superpowers/specs/s.md
echo plan > docs/superpowers/plans/p.md
echo code > feat.txt
git add -A; git commit -q -m "feat + transient spec/plan"
gitflow_finish >/dev/null 2>&1
# the add-commit stays reachable from develop via the --no-ff merge's 2nd parent;
# --full-history defeats the path simplification that hides it, and `git show
# <sha>:path` proves BDR-065's "git history = the archive" recovery.
# shellcheck disable=SC2034 # pf_add_sha is used in the deferred chk eval string
pf_add_sha="$(git log develop --full-history --format=%H -- docs/superpowers/specs/s.md | tail -1)"
chk "T17a merged into develop" 'git log develop --oneline | grep -q "Merge feature/pf into develop"'
chk "T17a develop TIP has no transient" '[ -z "$(git ls-tree -r develop --name-only -- docs/superpowers)" ]'
chk "T17a purge commit on record" 'git log develop --oneline | grep -q "purge transient planning artifacts"'
chk "T17a artifact recoverable from history" '[ "$(git show "$pf_add_sha":docs/superpowers/specs/s.md 2>/dev/null)" = spec ]'
chk "T17a non-transient code survives" 'git ls-tree -r develop --name-only | grep -qx feat.txt'
chk "T17a feature branch deleted" '! git rev-parse --verify -q refs/heads/feature/pf >/dev/null'
# T17b — no artifacts → purge is a silent no-op, no spurious commit
newrepo purgenone; echo a>a; hookon; gitflow_init >/dev/null 2>&1
gitflow_start feature pn >/dev/null 2>&1; echo w>w.txt; git add w.txt; git commit -q -m w
gitflow_finish >/dev/null 2>&1
chk "T17b merged into develop" 'git log develop --oneline | grep -q "Merge feature/pn into develop"'
chk "T17b no purge commit created" '! git log develop --oneline | grep -q "purge transient"'
# T17c — opt-out (GITFLOW_PURGE_TRANSIENT=0) keeps the artifacts on develop
newrepo purgeoff; echo a>a; hookon; gitflow_init >/dev/null 2>&1
gitflow_start feature po >/dev/null 2>&1
mkdir -p docs/superpowers/specs; echo spec > docs/superpowers/specs/s.md
git add -A; git commit -q -m "feat + spec"
GITFLOW_PURGE_TRANSIENT=0 gitflow_finish >/dev/null 2>&1
chk "T17c opt-out keeps transient on develop TIP" '[ -n "$(git ls-tree -r develop --name-only -- docs/superpowers)" ]'
# T17d — chore is OUT of purge scope (only feature/bugfix originate artifacts)
newrepo purgechore; echo a>a; hookon; gitflow_init >/dev/null 2>&1
gitflow_start chore pc >/dev/null 2>&1
mkdir -p docs/superpowers/specs; echo spec > docs/superpowers/specs/s.md
git add -A; git commit -q -m "chore + spec"
gitflow_finish >/dev/null 2>&1
chk "T17d chore leaves transient (not in scope)" '[ -n "$(git ls-tree -r develop --name-only -- docs/superpowers)" ]'
echo echo
echo "==== RESULT: $PASS passed, $FAIL failed ====" echo "==== RESULT: $PASS passed, $FAIL failed ===="
[ "$FAIL" -eq 0 ] [ "$FAIL" -eq 0 ]
+48 -2
View File
@@ -18,6 +18,12 @@ GITFLOW_MAIN="main"
GITFLOW_DEVELOP="develop" GITFLOW_DEVELOP="develop"
# template resolved relative to the lib; overridable for tests. # template resolved relative to the lib; overridable for tests.
GITFLOW_GITIGNORE_TEMPLATE="${GITFLOW_GITIGNORE_TEMPLATE:-$_GITFLOW_LIB_DIR/../templates/gitignore/standard.gitignore}" GITFLOW_GITIGNORE_TEMPLATE="${GITFLOW_GITIGNORE_TEMPLATE:-$_GITFLOW_LIB_DIR/../templates/gitignore/standard.gitignore}"
# Transient planning artifacts (superpowers spec/plan). A feature/bugfix run
# COMMITS them (SDD worktree + reviewers read them from disk); finish PURGES
# them before the merge reaches develop's tip (BDR-065). Fixed path list;
# read GITFLOW_PURGE_TRANSIENT=0 at finish time to opt out (read in the helper,
# never cached here, so an inline `VAR=0 gitflow_finish` override works).
GITFLOW_TRANSIENT_PATHS=("docs/superpowers/specs" "docs/superpowers/plans")
# ── predicates / pure helpers ──────────────────────────────────────────────── # ── predicates / pure helpers ────────────────────────────────────────────────
@@ -97,6 +103,42 @@ _gitflow_delete() { # <branch>
git branch -q -d "$br" || { echo "gitflow: '$br' not fully merged — branch kept" >&2; return 5; } git branch -q -d "$br" || { echo "gitflow: '$br' not fully merged — branch kept" >&2; return 5; }
} }
# _gitflow_purge_transient → remove the committed transient planning artifacts
# (BDR-065) from the CURRENT branch just before the directed merge. Result: the
# removal rides the feature/bugfix branch, whose earlier commits stay reachable
# from develop through the --no-ff merge (`git show <sha>:…` = the archive),
# while develop's TIP lands clean. Automates the manual post-merge chore that
# BDR-065 left as doctrine (and that slipped once — commit 655e364).
#
# BEST-EFFORT BY CONTRACT: this NEVER aborts a finish. Nothing tracked → no-op;
# uncommitted changes under those paths, or a failed commit → warn + degrade to
# the old manual-cleanup behaviour, index/tree restored, merge still proceeds.
# The scoped commit (`-- <paths>`) records only the deletions, so a dirty index
# is never swept in. Opt out with GITFLOW_PURGE_TRANSIENT=0.
_gitflow_purge_transient() {
[ "${GITFLOW_PURGE_TRANSIENT:-1}" = 1 ] || return 0
local p; local -a tracked=()
for p in "${GITFLOW_TRANSIENT_PATHS[@]}"; do
[ -n "$(git ls-files -- "$p")" ] && tracked+=("$p")
done
[ "${#tracked[@]}" -gt 0 ] || return 0 # nothing tracked → no-op
# only purge paths with no pending changes → git rm is all-or-nothing safe and
# never discards uncommitted work under docs/superpowers.
if ! git diff --quiet HEAD -- "${tracked[@]}" 2>/dev/null; then
echo "gitflow: transient artifacts have uncommitted changes — purge skipped, finishing without it (clean up by hand)" >&2
return 0
fi
if git rm -r -q -- "${tracked[@]}" >/dev/null 2>&1 \
&& git commit -q -m "chore: purge transient planning artifacts (BDR-065)" -- "${tracked[@]}"; then
echo "gitflow: purged transient planning artifacts before merge (${tracked[*]})" >&2
else
echo "gitflow: transient-artifact purge failed — finishing without it (clean up by hand)" >&2
git reset -q HEAD -- "${tracked[@]}" 2>/dev/null || true # unstage any partial rm
git checkout -q -- "${tracked[@]}" 2>/dev/null || true # restore working tree
fi
return 0
}
# gitflow_finish [<type> <name>] → directed merge of the CURRENT branch per its # gitflow_finish [<type> <name>] → directed merge of the CURRENT branch per its
# type, then delete. WHEN to call this is the human gate (SKILL.md). # type, then delete. WHEN to call this is the human gate (SKILL.md).
# #
@@ -117,7 +159,10 @@ gitflow_finish() {
fi fi
type="$(gitflow_branch_type "$br")" type="$(gitflow_branch_type "$br")"
case "$type" in case "$type" in
feature|bugfix|chore) feature|bugfix)
_gitflow_purge_transient # BDR-065 auto-cleanup, on HEAD, pre-merge; never blocks
_gitflow_merge_into "$GITFLOW_DEVELOP" "$br" && _gitflow_delete "$br" ;;
chore)
_gitflow_merge_into "$GITFLOW_DEVELOP" "$br" && _gitflow_delete "$br" ;; _gitflow_merge_into "$GITFLOW_DEVELOP" "$br" && _gitflow_delete "$br" ;;
release) release)
_gitflow_merge_into "$GITFLOW_MAIN" "$br" \ _gitflow_merge_into "$GITFLOW_MAIN" "$br" \
@@ -283,8 +328,9 @@ if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
finish) gitflow_finish "$@" ;; finish) gitflow_finish "$@" ;;
init) gitflow_init "$@" ;; init) gitflow_init "$@" ;;
reconcile) gitflow_reconcile_gitignore "$@" ;; reconcile) gitflow_reconcile_gitignore "$@" ;;
purge-transient) _gitflow_purge_transient ;;
install-hook) gitflow_install_hook "$@" ;; install-hook) gitflow_install_hook "$@" ;;
emit-hook) _gitflow_emit_pre_commit ;; emit-hook) _gitflow_emit_pre_commit ;;
*) echo "usage: gitflow.sh {type|protected-base|base-for|release-open|start|finish|init|reconcile|install-hook|emit-hook}" >&2; exit 2 ;; *) echo "usage: gitflow.sh {type|protected-base|base-for|release-open|start|finish|init|reconcile|purge-transient|install-hook|emit-hook}" >&2; exit 2 ;;
esac esac
fi fi
+10
View File
@@ -35,3 +35,13 @@ yet rewritten) — that is why the self-check exists alongside it.
then end the turn. No later step runs, no agent is dispatched, nothing is then end the turn. No later step runs, no agent is dispatched, nothing is
edited. edited.
## 4. Dispatch tiers (BDR-077 — no inherit)
The gate guards the MAIN loop only. Dispatched work NEVER inherits the
session model: typed agents run on their frontmatter pin; built-ins
(general-purpose / Explore / Plan) carry an explicit `model=` at every call
site — `model: "fable"` when the child performs reflection/orchestration on
the main loop's behalf (skill-runners), otherwise its complexity tier
(opus = dispatched judgment, sonnet = execution/collection, haiku = short
mechanical probes).
+90
View File
@@ -0,0 +1,90 @@
# Plugin gate — shared consumer include (plugin-check, onboard, init-project, ship-feature STEP 0)
Runs in the CONSUMER'S MAIN LOOP. The detection and the reasoning are
dispatched (BDR-077 tiers); the validation checkpoint, the report
presentation, and the apply gate live HERE — a dispatched agent can neither
ask the user nor safely mutate plugin state.
## 1. PROBE (dispatch — sonnet)
```
Agent(subagent_type="plugin-probe", description="plugin gate — probe",
prompt="Run your probes from <PROJECT_ROOT>. Emit the PROBE REPORT.")
```
## 2. VALIDATION CHECKPOINT (main loop — between probe and reasoner)
Validate the PROBE REPORT before any reasoning:
- `EXTERNAL` non-empty AND each listed plugin's directory appears under
`CHECKPOINT plugin-dirs`.
- At least one project signal present (MANIFESTS / FRAMEWORK-DEPS /
TSX-JSX-COUNT > 0 / DOCKER-COUNT > 0 / EMBEDDED hits). Else print
`⚠️ No project signals detected — recommendations will be conservative.`
and continue.
- `CHECKPOINT toggle-script=UNAVAILABLE` → print `⚠️ toggle script
unavailable — recommendations will be advisory only, no auto-activation.`
and SKIP step 5 (apply) entirely.
- PROBE REPORT missing/unparsable → retry the probe ONCE fresh; a 2nd
failure → STOP and surface (never reason over invented detection).
## 3. REASON (dispatch — opus)
```
Agent(subagent_type="plugin-advisor", description="plugin gate — reason",
prompt="""
REQUEST: <the user's request / project description, verbatim>
PROBE REPORT (ground truth — do not re-detect):
<the full PROBE REPORT from step 1>
""")
```
## 4. PRESENT + BLOCKING GATE (main loop)
Show the returned PLUGIN CHECK block.
- `ACTION REQUIRED? YES` → offer: A) fix plugins B) type "force". STOP until
answered.
- OK → print `✅ Plugin check passed — [active plugins] — complexity: <score>%`.
## 5. APPLY GATE (main loop — only when the flow auto-activates)
If any plugin has ⚡ ENABLE status:
1. List the changes:
```
PROPOSED CHANGES:
⚡ Enable ui-ux-pro-max (frontend detected, complexity 65%)
⚡ Pre-fetch ctx7 docs for next.js, prisma
Apply these changes? (yes / no / customize)
```
2. "yes" → apply via the exact commands the advisor emitted. "customize" →
user picks. "no" → proceed with current config.
**Never auto-activate without showing the list and getting confirmation.**
### Rollback on partial failure
Track each toggle; roll back the partial set rather than leave a
half-applied configuration:
```bash
applied=()
for change in "${PROPOSED_CHANGES[@]}"; do
if bash "$HOME/.claude/lib/toggle-external.sh" enable "$change"; then
applied+=("$change")
else
echo "❌ failed to enable $change — rolling back ${#applied[@]} prior change(s)"
for prior in "${applied[@]}"; do
bash "$HOME/.claude/lib/toggle-external.sh" disable "$prior" \
|| echo "⚠️ rollback of $prior also failed — manual cleanup required: see ~/.claude/plugins/cache"
done
exit 1
fi
done
```
Surface: `✅ Applied N change(s).` — or on failure:
```
⚠️ Toggle failed at change <name>. Rolled back the N prior change(s).
To inspect manually: ls ~/.claude/plugins/cache; bash ~/.claude/lib/toggle-external.sh list
Re-run /plugin-check after fixing the underlying cause (e.g. permissions).
```
+80 -15
View File
@@ -14,6 +14,9 @@
# - MCPs: delegated to lib/toggle-external.sh for known servers (magic), # - MCPs: delegated to lib/toggle-external.sh for known servers (magic),
# advisory otherwise # advisory otherwise
# - CLIs: advisory only (rtk, gsd, ctx7, graphify — installed externally) # - CLIs: advisory only (rtk, gsd, ctx7, graphify — installed externally)
# - `set` is SYMMETRIC on managed items (BDR-079): plugins, external packs
# and MCPs in the MANAGED_* allowlists are disabled when the profile
# does not list them — nothing outside those lists is ever auto-toggled.
# #
# Always-on plugins (never toggled by `set`): security-guidance, # Always-on plugins (never toggled by `set`): security-guidance,
# superpowers + rtk hook + .claude internal. The script refuses to disable # superpowers + rtk hook + .claude internal. The script refuses to disable
@@ -61,6 +64,23 @@ MANAGED_PLUGINS=(
"pr-review-toolkit@claude-code-plugins" "pr-review-toolkit@claude-code-plugins"
) )
# External skill packs that are toggle-managed by `set` — same allowlist
# doctrine as MANAGED_PLUGINS: listed here only when the enabled state is
# task-type-driven. `set` disables these when the profile does not list
# them; anything else external (e.g. darwin-skill) is never auto-touched.
MANAGED_EXTERNALS=(
emil-design-eng
frontend-design
design-motion-principles
impeccable
)
# MCP servers that are toggle-managed by `set`, both ways (enable AND
# disable), delegated to lib/toggle-external.sh. Same allowlist doctrine.
MANAGED_MCPS=(
magic
)
# Plugins that MUST stay enabled — `set` will refuse to disable these even if # Plugins that MUST stay enabled — `set` will refuse to disable these even if
# they're not in the profile. (Defensive: belt-and-suspenders alongside # they're not in the profile. (Defensive: belt-and-suspenders alongside
# MANAGED_PLUGINS allowlist.) # MANAGED_PLUGINS allowlist.)
@@ -271,6 +291,11 @@ enable_skill() {
ok "enabled: $skill ($type)" ok "enabled: $skill ($type)"
elif [ -e "$SKILLS_DIR/$skill" ]; then elif [ -e "$SKILLS_DIR/$skill" ]; then
: :
elif [ "$type" = external ] && [ -d "$REPO/skills-external/$skill" ]; then
# Symlink never created (or hand-removed): recreate it from the
# vendored pack — mirrors toggle-external.sh's from-source path.
ln -sf "$REPO/skills-external/$skill" "$SKILLS_DIR/$skill"
ok "enabled: $skill (external, symlink created)"
else else
warn "missing: $skill ($type)" warn "missing: $skill ($type)"
fi fi
@@ -422,6 +447,48 @@ parked_gstack_count() {
find "$DISABLED_DIR" -maxdepth 1 -name 'gstack__*' 2>/dev/null | wc -l | tr -d ' ' find "$DISABLED_DIR" -maxdepth 1 -name 'gstack__*' 2>/dev/null | wc -l | tr -d ' '
} }
# ── `set` trim helpers — one per managed category ─────────────
# Each disables the managed items NOT listed in the given profile. Allowlist
# doctrine: only MANAGED_* entries are ever auto-disabled.
disable_plugins_not_in() {
local prof="$1" keep_file p plugin_name marketplace
keep_file="$(mktemp)"
read_profile "$prof" \
| awk -F'\t' '$2 ~ /^plugin@/ { sub(/^plugin@/, "", $2); print $1"@"$2 }' \
| sort -u > "$keep_file"
for p in "${MANAGED_PLUGINS[@]}"; do
if ! grep -qx "$p" "$keep_file"; then
plugin_name="${p%@*}"
marketplace="${p#*@}"
disable_skill "$plugin_name" "plugin@${marketplace}"
fi
done
rm -f "$keep_file"
}
disable_externals_not_in() {
local prof="$1" keep_file x
keep_file="$(mktemp)"
read_profile "$prof" | awk -F'\t' '$2 == "external" { print $1 }' \
| sort -u > "$keep_file"
for x in "${MANAGED_EXTERNALS[@]}"; do
grep -qx "$x" "$keep_file" || disable_skill "$x" external
done
rm -f "$keep_file"
}
disable_mcps_not_in() {
local prof="$1" keep_file s
keep_file="$(mktemp)"
read_profile "$prof" | awk -F'\t' '$2 == "mcp" { print $1 }' \
| sort -u > "$keep_file"
for s in "${MANAGED_MCPS[@]}"; do
grep -qx "$s" "$keep_file" || disable_skill "$s" mcp
done
rm -f "$keep_file"
}
# ── Commands ────────────────────────────────────────────── # ── Commands ──────────────────────────────────────────────
cmd_list() { cmd_list() {
@@ -506,24 +573,20 @@ cmd_apply() {
cmd_set() { cmd_set() {
local prof="$1" local prof="$1"
info "Setting profile: $prof (exclusive — disables non-listed gstack skills + managed plugins)" info "Setting profile: $prof (exclusive — disables non-listed gstack skills + managed plugins/externals/MCPs)"
# Disable gstack-origin skills not in profile. # Disable gstack-origin skills not in profile.
disable_gstack_not_in "$prof" disable_gstack_not_in "$prof"
# Disable managed plugins not in profile (PROTECTED_PLUGINS are excluded # Disable managed plugins not in profile (PROTECTED_PLUGINS are excluded
# by disable_skill itself — belt and suspenders). # by disable_skill itself — belt and suspenders).
local plugin_keep_file p plugin_name marketplace disable_plugins_not_in "$prof"
plugin_keep_file="$(mktemp)"
read_profile "$prof" | awk -F'\t' '$2 ~ /^plugin@/ { sub(/^plugin@/, "", $2); print $1"@"$2 }' | sort -u > "$plugin_keep_file" # Symmetry (BDR-079): a profile switch also parks the managed external
for p in "${MANAGED_PLUGINS[@]}"; do # packs and unregisters the managed MCPs the new profile does not need —
if ! grep -qx "$p" "$plugin_keep_file"; then # design leftovers (emil, magic…) no longer survive a `set backend`.
plugin_name="${p%@*}" disable_externals_not_in "$prof"
marketplace="${p#*@}" disable_mcps_not_in "$prof"
disable_skill "$plugin_name" "plugin@${marketplace}"
fi
done
rm -f "$plugin_keep_file"
# Enable everything listed in the profile. # Enable everything listed in the profile.
cmd_apply "$prof" cmd_apply "$prof"
@@ -679,9 +742,11 @@ EXAMPLES:
bash lib/profile.sh reset # restore everything bash lib/profile.sh reset # restore everything
NOTE: NOTE:
Plugin and MCP entries print advisory commands — they are NOT toggled "set" toggles the MANAGED items automatically, both ways: plugins
automatically. Run "claude plugin enable|disable" or "claude mcp add|remove" (ui-ux-pro-max, plugin-dev, pr-review-toolkit), external packs
yourself for those. (emil-design-eng, frontend-design, design-motion-principles, impeccable)
and the magic MCP. Anything outside those allowlists stays advisory —
run "claude plugin enable|disable" or "claude mcp add|remove" yourself.
EOF EOF
} }
-74
View File
@@ -1,74 +0,0 @@
#!/usr/bin/env bash
# lib/tests/config-protection.test.sh
set -u
H="$(cd "$(dirname "$0")/../.." && pwd)/hooks/config-protection.sh"
pass=0; fail=0
check() { if [ "$2" = "$3" ]; then pass=$((pass+1)); else fail=$((fail+1));
printf 'FAIL %s: got[%s] want[%s]\n' "$1" "$2" "$3"; fi; }
# Run hook for a file_path with NO sentinel present (CWD = a clean temp dir).
run() { local c r; c="$(mktemp -d)"; ( cd "$c" && printf \
'{"tool_name":"Edit","tool_input":{"file_path":"%s"}}' "$1" | bash "$H" ) \
>/dev/null 2>&1; r=$?; rm -rf "$c"; return "$r"; }
# --- Guarded quality-gate files -> blocked (exit 2) ---
run "/home/u/Documents/claude/lib/gitflow.sh"; check T1-gitflow "$?" 2
run "/home/u/.claude/settings.json"; check T2-live-settings "$?" 2
run "/home/u/Documents/claude/.claude/settings.local.json"; check T3-local-settings "$?" 2
run "/home/u/Documents/claude/settings.json"; check T4-root-settings "$?" 2
run "/home/u/Documents/claude/.githooks/pre-commit"; check T5-githook "$?" 2
run "/home/u/Documents/claude/doctor.sh"; check T6-doctor "$?" 2
run "/home/u/Documents/claude/.shellcheckrc"; check T7-shellcheckrc "$?" 2
# self-guard: the hook itself, other hooks, and the test suite are guarded
run "/home/u/Documents/claude/hooks/config-protection.sh"; check T8-self-guard "$?" 2
run "/home/u/.claude/hooks/session-start.sh"; check T9-deployed-hook "$?" 2
run "/home/u/Documents/claude/lib/tests/config-protection.test.sh"; check T10-tests-guarded "$?" 2
# --- Non-guarded -> allowed (exit 0) ---
run "/home/u/Documents/claude/lib/gitflow-migrate.sh"; check T11-near-miss "$?" 0
run "/home/u/project/src/app.js"; check T12-code "$?" 0
run "/home/u/project/settings.json"; check T13-foreign-settings "$?" 0
# --- Fail-open on malformed input (no file_path) -> allowed ---
c="$(mktemp -d)"; ( cd "$c" && printf '{}' | bash "$H" ) >/dev/null 2>&1
check T14-fail-open "$?" 0; rm -rf "$c"
# --- Sentinel one-shot: non-empty reason -> allow + log + consume; 2nd edit blocked ---
tmp="$(mktemp -d)"; mkdir -p "$tmp/.claude"
printf 'fixing eslint false-positive' > "$tmp/.claude/.config-edit-ok"
( cd "$tmp" && printf '{"tool_name":"Edit","tool_input":{"file_path":"/x/doctor.sh"}}' \
| HOME="$tmp" bash "$H" ) >/dev/null 2>&1
check T15-sentinel-allow "$?" 0
check T15-consumed "$([ -e "$tmp/.claude/.config-edit-ok" ] && echo present || echo gone)" gone
check T15-logged "$(grep -c 'BYPASS.*doctor.sh.*fixing eslint' \
"$tmp/.claude/logs/config-protection.log" 2>/dev/null)" 1
( cd "$tmp" && printf '{"tool_name":"Edit","tool_input":{"file_path":"/x/doctor.sh"}}' \
| HOME="$tmp" bash "$H" ) >/dev/null 2>&1
check T16-second-blocked "$?" 2
rm -rf "$tmp"
# --- Sentinel with EMPTY reason -> refused + consumed ---
tmp="$(mktemp -d)"; mkdir -p "$tmp/.claude"; : > "$tmp/.claude/.config-edit-ok"
( cd "$tmp" && printf '{"tool_name":"Edit","tool_input":{"file_path":"/x/doctor.sh"}}' \
| HOME="$tmp" bash "$H" ) >/dev/null 2>&1
check T17-empty-refused "$?" 2
check T17-consumed "$([ -e "$tmp/.claude/.config-edit-ok" ] && echo present || echo gone)" gone
rm -rf "$tmp"
# --- T18/T19: payload shapes beyond Edit (locks against future Edit-only narrowing) ---
c="$(mktemp -d)"; ( cd "$c" && printf \
'{"tool_name":"Write","tool_input":{"file_path":"/x/doctor.sh","content":"x"}}' | bash "$H" ) \
>/dev/null 2>&1; check T18-write-payload "$?" 2; rm -rf "$c"
c="$(mktemp -d)"; ( cd "$c" && printf \
'{"tool_name":"MultiEdit","tool_input":{"file_path":"/x/doctor.sh","edits":[{"old_string":"a","new_string":"b"}]}}' | bash "$H" ) \
>/dev/null 2>&1; check T19-multiedit-payload "$?" 2; rm -rf "$c"
# --- T20: sentinel with ONLY whitespace bytes (not literally empty) -> refused + consumed ---
tmp="$(mktemp -d)"; mkdir -p "$tmp/.claude"; printf ' \n\t' > "$tmp/.claude/.config-edit-ok"
( cd "$tmp" && printf '{"tool_name":"Edit","tool_input":{"file_path":"/x/doctor.sh"}}' \
| HOME="$tmp" bash "$H" ) >/dev/null 2>&1
check T20-whitespace-only-refused "$?" 2
check T20-whitespace-only-consumed "$([ -e "$tmp/.claude/.config-edit-ok" ] && echo present || echo gone)" gone
rm -rf "$tmp"
printf 'PASS=%s FAIL=%s\n' "$pass" "$fail"; [ "$fail" -eq 0 ]
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
# lib/tests/fast-libs.test.sh — fast-libs.sh verbs + ctx7-reminder hook (BDR-078)
set -u
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
L="$ROOT/lib/fast-libs.sh"
H="$ROOT/hooks/ctx7-reminder.sh"
pass=0; fail=0
check() { if [ "$2" = "$3" ]; then pass=$((pass+1)); else fail=$((fail+1));
printf 'FAIL %s: got[%s] want[%s]\n' "$1" "$2" "$3"; fi; }
tmp="$(mktemp -d)"; trap 'rm -rf "$tmp"' EXIT
# --- detect: JS fast-libs matched, stable deps ignored ---
mkdir -p "$tmp/js"
cat > "$tmp/js/package.json" <<'EOF'
{"dependencies":{"react":"^19","express":"^5","@tanstack/react-query":"^5"},
"devDependencies":{"vite":"^6","lodash":"^4"}}
EOF
check T1-js-detect "$(bash "$L" detect "$tmp/js" | tr '\n' ' ')" \
"@tanstack/react-query react vite "
# react-icons must NOT ride the react match (anchored full-key)
mkdir -p "$tmp/near"
printf '{"dependencies":{"react-icons":"^5","express":"^5"}}' \
> "$tmp/near/package.json"
check T2-near-miss "$(bash "$L" detect "$tmp/near" >/dev/null 2>&1; echo $?)" 1
# --- detect: python manifest + stable-tech project (exit 1) ---
mkdir -p "$tmp/py"
printf 'fastapi==0.115\nrequests>=2\n' > "$tmp/py/requirements.txt"
check T3-py-detect "$(bash "$L" detect "$tmp/py")" "fastapi"
mkdir -p "$tmp/cpp"
check T4-none "$(bash "$L" detect "$tmp/cpp" >/dev/null 2>&1; echo $?)" 1
# --- cache-status: missing / fresh / stale ---
check T5-missing "$(bash "$L" cache-status "$tmp/js" || true)" missing
mkdir -p "$tmp/js/.ctx7-cache"; touch "$tmp/js/.ctx7-cache/react-core.md"
check T6-fresh "$(bash "$L" cache-status "$tmp/js")" fresh
touch -d '10 days ago' "$tmp/js/.ctx7-cache/react-core.md"
check T7-stale "$(bash "$L" cache-status "$tmp/js" || true)" stale
# --- hook: fires once per session, silent on stable projects ---
hook() { printf '{"prompt":"add a hook","session_id":"%s","cwd":"%s"}' \
"$1" "$2" | TMPDIR="$tmp" bash "$H"; }
check H1-fires "$(hook s1 "$tmp/js" | grep -c 'Fast-moving')" 1
check H2-once "$(hook s1 "$tmp/js" | wc -l)" 0
check H3-cpp-quiet "$(hook s2 "$tmp/cpp" | wc -l)" 0
check H4-notif-quiet \
"$(printf '{"prompt":"<task-notification>x","session_id":"s3","cwd":"%s"}' \
"$tmp/js" | TMPDIR="$tmp" bash "$H" | wc -l)" 0
printf 'PASS=%s FAIL=%s\n' "$pass" "$fail"; [ "$fail" -eq 0 ]
+107 -8
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# lib/tests/model-routing.test.sh — census: gate wiring + pins + executor shape (BDR-066) # lib/tests/model-routing.test.sh — census: gate wiring + pins + executor shape (BDR-066, BDR-076)
set -u set -u
R="$(cd "$(dirname "$0")/../.." && pwd)" R="$(cd "$(dirname "$0")/../.." && pwd)"
pass=0; fail=0 pass=0; fail=0
@@ -22,7 +22,7 @@ has "agents/feater.md" 'model: sonnet'
has "agents/hotfixer.md" 'model: sonnet' has "agents/hotfixer.md" 'model: sonnet'
has "agents/verifier.md" 'model: sonnet' has "agents/verifier.md" 'model: sonnet'
has "agents/security-auditor.md" 'model: sonnet' has "agents/security-auditor.md" 'model: sonnet'
fm_lacks "agents/analyzer.md" 'model:' has "agents/analyzer.md" 'model: opus'
# 4) /feat executor shape # 4) /feat executor shape
has "skills/feat/SKILL.md" 'subagent_type="feater"' has "skills/feat/SKILL.md" 'subagent_type="feater"'
has "skills/feat/SKILL.md" 'verify-secure-loop.md' has "skills/feat/SKILL.md" 'verify-secure-loop.md'
@@ -54,17 +54,116 @@ lacks "agents/handover-doc-writer.md" 'AskUserQuestion'
lacks "agents/handover-doc-writer.md" 'Agent(' lacks "agents/handover-doc-writer.md" 'Agent('
has "agents/client-handover-writer.md" 'subagent_type="handover-doc-writer"' has "agents/client-handover-writer.md" 'subagent_type="handover-doc-writer"'
# 10) post-merge edge fixes (ronde): F1 feater applier carve-out, F2 /refactor # 10) post-merge edge fixes (ronde): F1 feater applier carve-out, F2 /refactor
# dispatch + pin, F3 /analyze gated (in loop 1), F4 interviewer un-pinned, # dispatch + pin, F3 /analyze gated (in loop 1)
# F5 audit agents' ABSENT pin locked (a stray sonnet pin would silently
# downgrade a live audit even though the skill's gate passed)
has "agents/feater.md" 'Applier path' has "agents/feater.md" 'Applier path'
has "skills/refactor/SKILL.md" 'subagent_type="refactorer"' has "skills/refactor/SKILL.md" 'subagent_type="refactorer"'
has "agents/refactorer.md" 'model: sonnet' has "agents/refactorer.md" 'model: sonnet'
fm_lacks "agents/seo-analyzer.md" 'model:' # 11) BDR-076/077 — session model = orchestration + inline reflection ONLY.
fm_lacks "agents/geo-analyzer.md" 'model:' # Dispatched judgment agents pinned OPUS. validator-analyzer TIERED DOWN
fm_lacks "agents/validator-analyzer.md" 'model:' # to sonnet (W3 — deterministic validator-runner + fixed deduction
# tables, no deep judgment; approved plan). Inline-load-only agents
# (interviewer, client-handover-writer) STAY unpinned: they run IN the
# main loop, a frontmatter pin there is inert and misleads.
has "agents/seo-analyzer.md" 'model: opus'
has "agents/geo-analyzer.md" 'model: opus'
has "agents/validator-analyzer.md" 'model: sonnet'
has "agents/plan-challenger.md" 'model: opus'
fm_lacks "agents/client-handover-writer.md" 'model:' fm_lacks "agents/client-handover-writer.md" 'model:'
fm_lacks "agents/interviewer.md" 'model:' fm_lacks "agents/interviewer.md" 'model:'
has "skills/onboard/SKILL.md" 'model="opus"'
has "skills/tour/SKILL.md" 'model="opus"'
has "lib/challenge-plan.md" 'BDR-076'
# 12) BDR-077 W1 — no-inherit: skill-runner children pinned fable at every
# call site; code-review dispatches carry opus; doctrine in model-gate.
# (fable dispatch alias spike-verified 2026-07-19: resolves
# claude-fable-5, enum-validated, loud failure — never silent fallback)
has "agents/client-handover-writer.md" 'model: "fable"'
has "skills/ship-feature/SKILL.md" 'model: "opus"'
has "skills/init-project/SKILL.md" 'model: "opus"'
has "lib/model-gate.md" 'model: "fable"'
lacks "lib/model-gate.md" 'model: "sonnet" in the Agent call'
# 13) BDR-077 W2 — plugin split: probe (sonnet, facts only) + advisor
# reasoner (opus, PROBE REPORT is ground truth, fail-closed); gate
# include owns checkpoint + apply; 4 consumers run the include, none
# inline-loads the advisor anymore
has "agents/plugin-probe.md" 'model: sonnet'
lacks "agents/plugin-probe.md" 'AskUserQuestion'
has "agents/plugin-advisor.md" 'model: opus'
has "agents/plugin-advisor.md" 'PROBE REPORT'
lacks "agents/plugin-advisor.md" 'PHASE 1 — DETECT'
has "lib/plugin-gate.md" 'subagent_type="plugin-probe"'
has "lib/plugin-gate.md" 'subagent_type="plugin-advisor"'
for s in plugin-check onboard init-project ship-feature; do
has "skills/$s/SKILL.md" 'lib/plugin-gate.md'
# shellcheck disable=SC2016 # literal $HOME wanted: matching the exact inline-load string
lacks "skills/$s/SKILL.md" 'Load `$HOME/.claude/agents/plugin-advisor.md`'
done
# 14) BDR-077 W2/S2 — doc pipeline: ONE agent, TWO modes around the
# dispatcher's gate (audit = opus via call-site override — documented
# precedence over the sonnet pin; patch = sonnet pin). Gate hoisted out
# of the agent (a dispatched agent cannot ask); CHANGE SUMMARY crosses
# the dispatch boundary into doc-commit (LRN-126); scaffolder carries no
# doc step; no consumer inline-loads doc-syncer anymore.
has "agents/doc-syncer.md" 'MODE: audit'
has "agents/doc-syncer.md" 'MODE: patch'
has "agents/doc-syncer.md" 'CHANGE SUMMARY'
has "agents/doc-syncer.md" 'DISPATCHER PROTOCOL'
has "lib/doc-commit.md" 'CHANGE SUMMARY'
has "skills/doc/SKILL.md" 'model="opus"'
has "skills/doc/SKILL.md" 'MODE: patch'
lacks "agents/scaffolder.md" 'INLINE-LOAD'
for s in bugfix hotfix feat ship-feature init-project; do
has "skills/$s/SKILL.md" 'MODE: audit'
has "skills/$s/SKILL.md" 'doc-syncer", model="opus"'
# shellcheck disable=SC2016 # literal $HOME wanted: matching the exact inline-load string
lacks "skills/$s/SKILL.md" 'Load `$HOME/.claude/agents/doc-syncer.md`'
done
# 15) BDR-077 W2 — last inline execution converted: scaffolder + onboarder
# are DISPATCHED (pins live); their gates/arbitration stay in the
# orchestrator loop
has "skills/init-project/SKILL.md" 'subagent_type="scaffolder"'
has "skills/onboard/SKILL.md" 'subagent_type="onboarder"'
# shellcheck disable=SC2016
lacks "skills/init-project/SKILL.md" 'Load `$HOME/.claude/agents/scaffolder.md`'
# shellcheck disable=SC2016
lacks "skills/onboard/SKILL.md" 'Load `$HOME/.claude/agents/onboarder.md`'
# 16) BDR-077 W3 — commit-changer per-mode override: propose = opus at the
# call site (documented precedence over the sonnet pin), apply = pin
has "skills/commit-change/SKILL.md" 'model="opus"'
has "agents/commit-changer.md" 'MODE: propose'
has "agents/commit-changer.md" 'model: sonnet'
# 17) BDR-077 W4 — handover two-mode: synthesize = opus at the call site
# (STEP 9/10/12 → run-scoped .audit/ draft + DRAFT COMPLETE sentinel),
# render = sonnet pin (STEP 13-16, fail-closed on absent/mismatched
# draft). Name + dispatch-string locks of §9 survive untouched.
has "agents/handover-doc-writer.md" 'MODE: synthesize'
has "agents/handover-doc-writer.md" 'MODE: render'
has "agents/handover-doc-writer.md" 'DRAFT COMPLETE'
has "agents/client-handover-writer.md" 'MODE: synthesize'
has "agents/client-handover-writer.md" 'handover-doc-writer", model="opus"'
# 18) BDR-077 W5 — seo/geo 3-mode pipelines: collect/template = sonnet at
# the call site, judge = opus PIN (fail-safe direction: a forgotten
# override over-tiers, never downgrades judgment). Run-scoped signals
# handoff + completeness sentinel + fail-closed judge + dispatcher
# ERROR contract (mute/ERROR judge never carried into templating).
# Body text unmoved — seo-data.test.sh fetch-wiring locks survive.
has "agents/seo-analyzer.md" 'MODE: collect'
has "agents/seo-analyzer.md" 'MODE: judge'
has "agents/seo-analyzer.md" 'MODE: template'
has "agents/seo-analyzer.md" 'COLLECTION COMPLETE'
has "agents/geo-analyzer.md" 'MODE: collect'
has "agents/geo-analyzer.md" 'MODE: judge'
has "agents/geo-analyzer.md" 'MODE: template'
has "agents/geo-analyzer.md" 'COLLECTION COMPLETE'
has "skills/seo/SKILL.md" 'MODE: collect'
has "skills/seo/SKILL.md" 'seo-analyzer", model="sonnet"'
has "skills/seo/SKILL.md" 'never re-derive a score'
has "skills/seo/SKILL.md" 'DISPATCHER ERROR CONTRACT'
has "skills/geo/SKILL.md" 'MODE: collect'
has "skills/geo/SKILL.md" 'geo-analyzer", model="sonnet"'
has "skills/geo/SKILL.md" 'ERROR CONTRACT'
has "skills/geo/SKILL.md" 'never re-derive a score'
has "agents/handover-doc-writer.md" 'SYNTH REPORT'
printf 'model-routing census: %d pass, %d fail\n' "$pass" "$fail" printf 'model-routing census: %d pass, %d fail\n' "$pass" "$fail"
[ "$fail" -eq 0 ] [ "$fail" -eq 0 ]
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
# lib/tests/plan-challenger.test.sh — structure lock: the plan-challenger agent,
# the reusable lib/challenge-plan.md phase, and every reflection orchestrator that
# wires it (3-way adversarial plan challenge, BDR-066). STATIC only — the agent's
# adversarial behavior needs a live model (manual smoke), not a CI gate.
set -u
R="$(cd "$(dirname "$0")/../.." && pwd)"
pass=0; fail=0
ok() { pass=$((pass+1)); }
ko() { fail=$((fail+1)); printf 'FAIL %s\n' "$1"; }
has() { if grep -qF "$2" "$R/$1"; then ok; else ko "$1 missing: $2"; fi; }
fm_lacks() { if awk 'NR<=10' "$R/$1" | grep -qF "$2"; then ko "$1 frontmatter must NOT contain: $2"; else ok; fi; }
A="agents/plan-challenger.md"
L="lib/challenge-plan.md"
# 1) agent shape
has "$A" "name: plan-challenger"
has "$A" "tools: Read, Grep, Glob, Bash"
fm_lacks "$A" "model: sonnet" # BDR-066: audit judgment → NOT sonnet-pinned
has "$A" "CHALLENGE — LENS:" # load-bearing verdict grammar
has "$A" "VERDICT: SOLID | CONCERNS(n) | FATAL(n)"
has "$A" "correctness"
has "$A" "robustness"
has "$A" "simplicity"
has "$A" "Report-only"
# 2) reusable phase — the mechanism lives here (one canonical include)
has "$L" 'subagent_type="plan-challenger"'
has "$L" "BDR-066" # challengers on the big model
has "$L" "a mute verifier is NEVER a PASS" # fail-safe (never fail open)
has "$L" "Severity-driven" # any single-lens BLOCKER = must-address
has "$L" "RE-THINK" # findings re-plan the aspect, not just noted
has "$L" "correctness | robustness | simplicity"
has "$L" "CHALLENGE SUMMARY"
has "$L" "build-plan" # the three KINDs
has "$L" "proposals"
has "$L" "fix-bundle"
# 3) every reflection orchestrator wires the phase + carries a challenge summary
# (hotfix wires it under a logic-only guard — STEP 1.8)
for s in ship-feature init-project feat bugfix hotfix onboard audit-delta code-clean seo geo harden web-validate; do
has "skills/$s/SKILL.md" "lib/challenge-plan.md"
has "skills/$s/SKILL.md" "CHALLENGE SUMMARY"
done
printf 'plan-challenge structure lock: %d pass, %d fail\n' "$pass" "$fail"
[ "$fail" -eq 0 ]
+76
View File
@@ -0,0 +1,76 @@
#!/usr/bin/env bash
# lib/tests/profile-set-managed.test.sh — `set` symmetry on managed
# externals + MCPs, gstack on-demand, external from-source (BDR-079).
# Hermetic: fixture repo via *_REPO_OVERRIDE + fake `claude` on PATH.
set -u
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
pass=0; fail=0
check() { if [ "$2" = "$3" ]; then pass=$((pass+1)); else fail=$((fail+1));
printf 'FAIL %s: got[%s] want[%s]\n' "$1" "$2" "$3"; fi; }
FX="$(mktemp -d)"; trap 'rm -rf "$FX"' EXIT
mkdir -p "$FX/skills" "$FX/skills-disabled" "$FX/lib/profiles" "$FX/bin" \
"$FX/skills-external/emil-design-eng" "$FX/skills-external/other-ext"
for g in gs-a gs-b gs-c; do
mkdir -p "$FX/skills-external/gstack/$g"
touch "$FX/skills-external/gstack/$g/SKILL.md"
done
cp "$ROOT/lib/profile.sh" "$ROOT/lib/toggle-external.sh" "$FX/lib/"
printf 'MAGIC_API_KEY=test-secret-000\n' > "$FX/.env"
# Non-managed external, enabled from the start — must never be touched.
ln -s "$FX/skills-external/other-ext" "$FX/skills/other-ext"
cat > "$FX/lib/profiles/designish.profile" <<'EOF'
gs-a
gs-b
emil-design-eng external
magic mcp
EOF
cat > "$FX/lib/profiles/backendish.profile" <<'EOF'
gs-c
EOF
# Fake claude: logs every call; keeps MCP registry state in a flat file.
cat > "$FX/bin/claude" <<EOF
#!/usr/bin/env bash
FX="$FX"
echo "\$*" >> "\$FX/claude-calls.log"
case "\$1 \${2:-}" in
"mcp list") cat "\$FX/mcp-state" 2>/dev/null ;;
"mcp add") echo "magic: stub" > "\$FX/mcp-state" ;;
"mcp remove") : > "\$FX/mcp-state" ;;
esac
exit 0
EOF
chmod +x "$FX/bin/claude"
run() { PATH="$FX/bin:$PATH" PROFILE_REPO_OVERRIDE="$FX" \
TOGGLE_EXTERNAL_REPO_OVERRIDE="$FX" bash "$FX/lib/profile.sh" "$@"; }
# --- set designish: gstack on-demand + external from-source + magic on ---
run set designish >/dev/null 2>&1
check T1-gsa-on "$([ -e "$FX/skills/gs-a" ] && echo on || echo off)" on
check T2-gsb-on "$([ -e "$FX/skills/gs-b" ] && echo on || echo off)" on
check T3-gsc-off "$([ -e "$FX/skills/gs-c" ] && echo on || echo off)" off
check T4-emil-src "$([ -L "$FX/skills/emil-design-eng" ] && echo on || echo off)" on
check T5-magic-on "$(grep -c '^magic:' "$FX/mcp-state" 2>/dev/null)" 1
check T6-add-call "$(grep -c '^mcp add magic' "$FX/claude-calls.log")" 1
# --- set backendish: managed leftovers parked/unregistered ---
run set backendish >/dev/null 2>&1
check T7-gsc-on "$([ -e "$FX/skills/gs-c" ] && echo on || echo off)" on
check T8-gsa-park "$([ -e "$FX/skills-disabled/gstack__gs-a" ] && echo p || echo n)" p
check T9-emil-off "$([ -e "$FX/skills/emil-design-eng" ] && echo on || echo off)" off
check T10-emil-park "$([ -e "$FX/skills-disabled/emil-design-eng" ] && echo p || echo n)" p
check T11-magic-off "$(grep -c '^magic:' "$FX/mcp-state" 2>/dev/null || true)" 0
check T12-rm-call "$(grep -c '^mcp remove magic' "$FX/claude-calls.log")" 1
check T13-other-untouched "$([ -e "$FX/skills/other-ext" ] && echo on || echo off)" on
# --- back to designish: parked external restored (not re-sourced) ---
run set designish >/dev/null 2>&1
check T14-emil-back "$([ -e "$FX/skills/emil-design-eng" ] && echo on || echo off)" on
check T15-park-gone "$([ -e "$FX/skills-disabled/emil-design-eng" ] && echo p || echo n)" n
check T16-magic-back "$(grep -c '^magic:' "$FX/mcp-state" 2>/dev/null)" 1
printf 'PASS=%s FAIL=%s\n' "$pass" "$fail"; [ "$fail" -eq 0 ]
+7 -11
View File
@@ -250,7 +250,7 @@
"disableBypassPermissionsMode": "disable", "disableBypassPermissionsMode": "disable",
"additionalDirectories": [] "additionalDirectories": []
}, },
"model": "opus[1m]", "model": "claude-fable-5[1m]",
"hooks": { "hooks": {
"SessionStart": [ "SessionStart": [
{ {
@@ -271,16 +271,6 @@
"command": "bash ~/.claude/hooks/rtk-rewrite.sh" "command": "bash ~/.claude/hooks/rtk-rewrite.sh"
} }
] ]
},
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": "bash ~/.claude/hooks/config-protection.sh",
"timeout": 5
}
]
} }
], ],
"UserPromptSubmit": [ "UserPromptSubmit": [
@@ -291,6 +281,12 @@
"command": "bash ~/.claude/hooks/design-toolchain-reminder.sh", "command": "bash ~/.claude/hooks/design-toolchain-reminder.sh",
"timeout": 5, "timeout": 5,
"statusMessage": "Checking design signals..." "statusMessage": "Checking design signals..."
},
{
"type": "command",
"command": "bash ~/.claude/hooks/ctx7-reminder.sh",
"timeout": 5,
"statusMessage": "Checking fast-libs..."
} }
] ]
} }
+21
View File
@@ -166,10 +166,31 @@ Append to `.claude/audits/AUDIT-DELTA.md` (create if absent), append-only:
Then show the user the same compact table inline. Then show the user the same compact table inline.
### 3b-bis. CHALLENGE THE PROPOSALS (before the gate)
This axis' findings + proposed fixes are a proposal set worth attacking before
the human gate. Persist THIS axis' finding list (not the whole append-only
report) to `.claude/tasks/plans/<date>-<axis>-<HHMM>.md`, then run
`$HOME/.claude/lib/challenge-plan.md` with `PLAN` = that file, `KIND` =
`proposals`, `SCOPE` = this axis' STEP 1 audit set, `CONSTRAINTS` = the axis
spec + the project CLAUDE.md norms already loaded. Three blind challengers ask
whether these are the RIGHT findings/priorities and what the audit under-rated;
the main loop RE-THINKS every aspect a BLOCKER lands (a named change to the
finding set, or `[deferred <date>]`) and re-challenges once if it materially
changed. Feed the REVISED findings + a CHALLENGE SUMMARY into 3c.
### 3c. APPROVAL GATE ★ MANDATORY STOP ### 3c. APPROVAL GATE ★ MANDATORY STOP
Show the CHALLENGE SUMMARY (from 3b-bis) with the 3b findings table, then
AskUserQuestion: **fix all / pick which / none**. AskUserQuestion: **fix all / pick which / none**.
```
CHALLENGE SUMMARY (3b-bis — 3 lenses):
BLOCKERs addressed : <n> — <finding → the named finding-set change that closes it>
Deferred (human-ack): <list | none>
Lenses returned : correctness / robustness / simplicity (NAME any that failed to return)
```
- "Fix what you find" said **in the invocation** does NOT skip this gate: - "Fix what you find" said **in the invocation** does NOT skip this gate:
nobody can approve findings that did not exist yet. The gate is about nobody can approve findings that did not exist yet. The gate is about
*these specific findings*. *these specific findings*.
+23 -3
View File
@@ -117,6 +117,19 @@ RISK: <low/medium — what could go wrong>
- If the fix is significant (>10 lines, multiple files, - If the fix is significant (>10 lines, multiple files,
behavior change): wait for user approval. behavior change): wait for user approval.
## STEP 3b — CHALLENGE THE FIX PLAN (before the contract)
Unless the fix is the trivial 1-2 line case STEP 3 already fast-paths, the
DIAGNOSIS + FIX PLAN is a reflection worth attacking before it hardens into a
contract. Persist it to `.claude/tasks/plans/<date>-<slug>-<HHMM>.md`, then run
`$HOME/.claude/lib/challenge-plan.md` with `PLAN` = that file, `KIND` = `build-plan`,
`SCOPE` = the FIX PLAN files, `CONSTRAINTS` = the STEP 2 in-force BDR/LRN/BLK
dispositions. Three blind challengers attack it (correctness = is the root cause
right; robustness = blast radius / regressions; simplicity = is the fix minimal);
RE-THINK every aspect a BLOCKER lands, re-challenge once if the plan materially
changed. STEP 3.5 writes the contract from the REVISED plan. Print a CHALLENGE SUMMARY
(BLOCKERs addressed / deferred / lenses returned), folding any deferred BLOCKER into
the STEP 3 approval gate.
## STEP 3.5 — CONTRACT ## STEP 3.5 — CONTRACT
Run `$HOME/.claude/lib/contract-interview.md` (main loop). The DIAGNOSIS Run `$HOME/.claude/lib/contract-interview.md` (main loop). The DIAGNOSIS
@@ -212,9 +225,16 @@ Parse the `BUGFIX-EXEC REPORT`:
## STEP 7 — DOC SYNC (automatic) ## STEP 7 — DOC SYNC (automatic)
Load `$HOME/.claude/agents/doc-syncer.md`. Dispatch the doc pipeline (BDR-077 — audit judgment on opus, patch on the
Execute in automatic mode: sonnet pin, gate HERE):
`auto-mode scope: <list of files modified during this session>` 1. `Agent(subagent_type="doc-syncer", model="opus")` — `MODE: audit` +
`auto-mode scope: <list of files modified during this session>`.
2. Silence (NONE) → done. `[MINOR]` PATCH PLAN → re-dispatch
`Agent(subagent_type="doc-syncer")` with `MODE: patch` + the plan
verbatim (no gate — auto behavior preserved; a `SHAPE ESCALATION` in
its report comes back here, gated as SIGNIFICANT).
3. SIGNIFICANT → gate here (`Apply? yes / no / select`), then
`MODE: patch` with the approved subset.
**Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits **Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits
ONLY the files doc-syncer patched (its `PATCHED_FILES` output), never `git add -A`, never ONLY the files doc-syncer patched (its `PATCHED_FILES` output), never `git add -A`, never
+5 -3
View File
@@ -29,9 +29,11 @@ Load and follow strictly:
- $HOME/.claude/agents/client-handover-writer.md - $HOME/.claude/agents/client-handover-writer.md
Execute the CLIENT HANDOVER WRITER agent on this project. It runs the Execute the CLIENT HANDOVER WRITER agent on this project. It runs the
audit/fix/gate pipeline INLINE on the big session model (gated above), then audit/fix/gate pipeline INLINE on the big session model (gated above), its
delegates the client deliverable (Markdown + branded HTML + PDF) to the skill-runner children dispatched `model: "fable"`, then delegates the
sonnet-pinned `handover-doc-writer` subagent (BDR-066). client deliverable to the two-mode `handover-doc-writer` subagent —
synthesize on opus, render (Markdown + branded HTML + PDF) on the sonnet
pin (BDR-077).
The agent runs a **ship-and-handover pipeline** with explicit gates: The agent runs a **ship-and-handover pipeline** with explicit gates:
+19 -1
View File
@@ -119,11 +119,29 @@ TOTALS: <N blocking, N warn, N info>
If no issues found: report clean state and stop. If no issues found: report clean state and stop.
## STEP 3b — CHALLENGE THE SCOPE (before approval)
The STEP 3 report is the proposed cleanup scope — worth attacking before the
human approves it. It is still inline, so FIRST persist it to
`.claude/tasks/plans/<date>-<slug>-<HHMM>.md` (STEP 3 report format, one item
per line), then run `$HOME/.claude/lib/challenge-plan.md` with `PLAN` = that
file, `KIND` = `proposals`, `SCOPE` = the scanned target ($ARGUMENTS or repo
root), `CONSTRAINTS` = the STEP 1 project norms + the iron law (zero behavior
change). Three blind challengers ask whether these are the RIGHT items and what
the scan under- or over-scoped; the main loop RE-THINKS every aspect a BLOCKER
lands (a named scope change re-written into the report, or `[deferred <date>]`)
and re-challenges once if the scope materially changed. Feed the REVISED scope +
a CHALLENGE SUMMARY into STEP 4. Advisory — the human still approves per item.
## STEP 4 — VALIDATION GATE (interactive) ## STEP 4 — VALIDATION GATE (interactive)
Present the report from STEP 3. Then ask: Present the report from STEP 3 with the STEP 3b CHALLENGE SUMMARY. Then ask:
``` ```
CHALLENGE SUMMARY (STEP 3b — 3 lenses):
BLOCKERs addressed : <n> — <finding → the named scope change that closes it>
Deferred (human-ack): <list | none>
Lenses returned : correctness / robustness / simplicity (NAME any that failed to return)
AskUserQuestion: AskUserQuestion:
Approve which items for execution? (all / <item numbers> / clarify <item>) Approve which items for execution? (all / <item numbers> / clarify <item>)
``` ```
+10 -4
View File
@@ -18,7 +18,8 @@ allowed-tools:
# /commit-change — propose → confirm → apply dispatcher # /commit-change — propose → confirm → apply dispatcher
Grouping and committing both run on the sonnet-pinned `commit-changer` Grouping (propose, `model="opus"` override — judgment) and committing
(apply, sonnet pin — mechanical) both run on the dispatched `commit-changer`
subagent (dispatch makes the pin effective). No inline reflection happens subagent (dispatch makes the pin effective). No inline reflection happens
in this dispatcher to protect, so there is no model gate. This dispatcher in this dispatcher to protect, so there is no model gate. This dispatcher
owns the two approval gates that used to live inside the subagent: owns the two approval gates that used to live inside the subagent:
@@ -52,11 +53,15 @@ protected branch.
## STEP 1 — Propose ## STEP 1 — Propose
``` ```
Agent(subagent_type="commit-changer") Agent(subagent_type="commit-changer", model="opus")
prompt: "MODE: propose prompt: "MODE: propose
$ARGUMENTS" $ARGUMENTS"
``` ```
(`model="opus"` — BDR-077: propose = narrative reconstruction + capitalize
routing, judgment tier; the call-site override takes precedence over the
sonnet frontmatter pin. Apply, STEP 4, stays on the pin.)
Read the returned `COMMIT PLAN` + `EDGE CASES` + `CAPITALIZE CANDIDATES`, Read the returned `COMMIT PLAN` + `EDGE CASES` + `CAPITALIZE CANDIDATES`,
terminated by `READY TO APPLY — awaiting dispatcher confirmation`. terminated by `READY TO APPLY — awaiting dispatcher confirmation`.
@@ -77,8 +82,9 @@ AskUserQuestion:
uncommitted for a later run. uncommitted for a later run.
- `edit <n>` → re-dispatch `commit-changer` with `MODE: propose` and the - `edit <n>` → re-dispatch `commit-changer` with `MODE: propose` and the
user's correction for step N folded into the prompt, so all grouping / user's correction for step N folded into the prompt, so all grouping /
message judgment stays on the sonnet subagent (never redrawn inline on message judgment stays on the dispatched propose mode (`model="opus"`,
the session model); show the redrawn plan and re-ask. BDR-077 — never redrawn inline on the session model); show the redrawn
plan and re-ask.
- `skip` → exit cleanly, no commits created, no `MODE: apply` dispatch. - `skip` → exit cleanly, no commits created, no `MODE: apply` dispatch.
Note: if the propose run created a `chore/*` branch (gitflow aiguillage Note: if the propose run created a `chore/*` branch (gitflow aiguillage
off a protected base), that branch stays checked out with the work off a protected base), that branch stays checked out with the work
+23 -8
View File
@@ -18,13 +18,28 @@ allowed-tools:
- Agent - Agent
--- ---
Dispatch the doc-syncer as a subagent so its `model: sonnet` pin takes Run the two-mode doc pipeline (BDR-077 — audit judgment on opus, patch on
effect (doc-sync = execution, not the session's big model): the sonnet pin, the validation gate in THIS loop; a dispatched agent cannot
hold a gate):
Agent(subagent_type="doc-syncer") 1. AUDIT — dispatch:
prompt: "Audit + sync public docs for this project. Context from the user: `Agent(subagent_type="doc-syncer", model="opus")`
$ARGUMENTS. Report PATCHED_FILES and a summary — do NOT commit." prompt: "MODE: audit. Audit public docs for this project. Context from
the user: $ARGUMENTS. Emit the DOC SYNC REPORT + PATCH PLAN — no writes."
Then commit the patched docs from THIS loop per `$HOME/.claude/lib/doc-commit.md` 2. GATE — present the report and run the DOC SYNC — VALIDATION GATE from
(surgical: only doc-syncer's PATCHED_FILES, never `.claude/`/`CLAUDE.md`, the agent's DISPATCHER PROTOCOL (AUTO yes/select/cancel; HUMAN, CREATE,
no-op if nothing patched). CLEAN per-item; README CREATE has no skip). Wait for explicit approval.
`DOC SYNC: all docs current` → stop here.
3. PATCH — re-dispatch:
`Agent(subagent_type="doc-syncer")` (sonnet pin)
prompt: "MODE: patch." + the APPROVED PATCH PLAN verbatim (approved item
lines + rendered drafts for approved CREATE items). A `SHAPE ESCALATION`
in its report → re-gate the named set here, then re-dispatch patch with
the kept subset.
4. COMMIT — from THIS loop per `$HOME/.claude/lib/doc-commit.md` (surgical:
only the report's `PATCHED_FILES`, summary composed from its
`CHANGE SUMMARY` block, never `.claude/`/`CLAUDE.md`, no-op if nothing
patched).
+22 -4
View File
@@ -117,6 +117,17 @@ PLAN:
If the approach is ambiguous: ask the user ONE focused question BEFORE If the approach is ambiguous: ask the user ONE focused question BEFORE
dispatching — never after (the executor cannot relay questions). dispatching — never after (the executor cannot relay questions).
## STEP 1b — CHALLENGE THE PLAN (before branching)
The STEP 1 plan is a reflection worth attacking before a branch is spent on it.
Persist it to `.claude/tasks/plans/<date>-<slug>-<HHMM>.md`, then run
`$HOME/.claude/lib/challenge-plan.md` with `PLAN` = that file, `KIND` = `build-plan`,
`SCOPE` = the STEP 1 files, `CONSTRAINTS` = the STEP 0.6 in-force BDR/LRN dispositions.
Three blind challengers attack it; RE-THINK every aspect a BLOCKER lands (a named
plan change, or `[deferred]`), re-challenge once if the plan materially changed. The
STEP 3 executor receives the REVISED plan. Before dispatch, print a CHALLENGE SUMMARY
(BLOCKERs addressed / deferred / lenses returned), surfacing any deferred BLOCKER via
STEP 1's one-question gate.
## STEP 2 — BRANCH ## STEP 2 — BRANCH
**Gitflow aiguillage (before dispatch):** follow `$HOME/.claude/lib/gitflow-aiguillage.md` **Gitflow aiguillage (before dispatch):** follow `$HOME/.claude/lib/gitflow-aiguillage.md`
@@ -175,7 +186,7 @@ feat(<scope>): <what was added>
If the feature touched multiple concerns (e.g., feature + config + If the feature touched multiple concerns (e.g., feature + config +
test), consider splitting into 2-3 atomic commits grouped by logical test), consider splitting into 2-3 atomic commits grouped by logical
unit — or run `/commit-change` on the pending work (it dispatches the unit — or run `/commit-change` on the pending work (it dispatches the
sonnet commit-changer; never inline-load the bare agent, it is now a commit-changer (propose opus / apply sonnet, BDR-077); never inline-load the bare agent, it is now a
propose/apply executor). propose/apply executor).
Print summary: Print summary:
@@ -189,9 +200,16 @@ VERIFIED : <what was checked>
## STEP 6 — DOC SYNC (automatic) ## STEP 6 — DOC SYNC (automatic)
Load `$HOME/.claude/agents/doc-syncer.md`. Dispatch the doc pipeline (BDR-077 — audit judgment on opus, patch on the
Execute in automatic mode: sonnet pin, gate HERE):
`auto-mode scope: <list of files modified during this session>` 1. `Agent(subagent_type="doc-syncer", model="opus")` — `MODE: audit` +
`auto-mode scope: <list of files modified during this session>`.
2. Silence (NONE) → done. `[MINOR]` PATCH PLAN → re-dispatch
`Agent(subagent_type="doc-syncer")` with `MODE: patch` + the plan
verbatim (no gate — auto behavior preserved; a `SHAPE ESCALATION` in
its report comes back here, gated as SIGNIFICANT).
3. SIGNIFICANT → gate here (`Apply? yes / no / select`), then
`MODE: patch` with the approved subset.
**Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits **Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits
ONLY the files doc-syncer patched (its `PATCHED_FILES` output), never `git add -A`, never ONLY the files doc-syncer patched (its `PATCHED_FILES` output), never `git add -A`, never
+60 -17
View File
@@ -36,27 +36,65 @@ terminated by `READY TO APPLY — awaiting dispatcher confirmation`, and this
skill applies it. Applying from here (one dispatch level, no nested spawn) skill applies it. Applying from here (one dispatch level, no nested spawn)
is what makes fixes land on any Claude Code version. is what makes fixes land on any Claude Code version.
## STEP 1 — Dispatch geo-analyzer (audit + bundle) ## STEP 1 — Run the geo pipeline (collect → judge → template, BDR-077)
Gather depth + business context HERE first (ask the user in this loop if
needed — a dispatched agent cannot ask). Mint `RUNID=$(date +%s)-geo`.
Pass the same CONTEXT block ($ARGUMENTS + gathered context) VERBATIM to
every phase (LRN-126). Clean `.audit/geo-signals-<RUNID>.md` after apply.
**A — collect (sonnet):**
```
Agent(subagent_type="geo-analyzer", model="sonnet")
prompt: "MODE: collect
RUNID: <RUNID>
Dispatched from /geo. Context: <CONTEXT>
Execute STEP 0-5 per your spec, write the signals file + COLLECTION
COMPLETE sentinel, emit the COLLECT REPORT, stop."
```
**B — judge (opus pin, no override):**
``` ```
Agent(subagent_type="geo-analyzer") Agent(subagent_type="geo-analyzer")
prompt: """ prompt: "MODE: judge
Dispatched from /geo. Execute your full spec at RUNID: <RUNID>
~/.claude/agents/geo-analyzer.md (STEP 0 onward — gather depth + business Context: <CONTEXT>
context as needed; if you must ask the user, ask and I relay). Load .audit/geo-signals-<RUNID>.md (fail closed per your spec), run STEP
6-12, report scoring + findings + action plan + triage batches."
Produce your report:
- If .claude/audits/SEO.md already exists → merge findings into its
§7 — Optimisation GEO / IA.
- Else write .claude/audits/GEO.md.
Then emit the `## FIX BUNDLE` (STEP 13) terminated by the verbatim
`READY TO APPLY — awaiting dispatcher confirmation` sentinel. Do NOT apply
any fix and do NOT dispatch any sub-agent — /geo applies your bundle.
$ARGUMENTS
"""
``` ```
**ERROR CONTRACT:** `GEO JUDGE — VERDICT: ERROR(…)` or a mute judge →
STOP: no template, no apply. Surface verbatim, retry ONCE with a fresh
collect+judge, then escalate. Never carry a mute/ERROR judge into
templating.
**C — template (sonnet):**
```
Agent(subagent_type="geo-analyzer", model="sonnet")
prompt: "MODE: template
Context: <CONTEXT>
JUDGE REPORT (verbatim, ground truth — never re-derive a score):
<the judge report>
Run STEP 13-15. Produce your report: if .claude/audits/SEO.md already
exists → merge findings into its §7 — Optimisation GEO / IA; else write
.claude/audits/GEO.md. Then emit the `## FIX BUNDLE` terminated by the
verbatim `READY TO APPLY — awaiting dispatcher confirmation` sentinel.
Do NOT apply any fix and do NOT dispatch any sub-agent — /geo applies
your bundle."
```
## STEP 1b — CHALLENGE THE FIX BUNDLE (advisory, before apply)
The analyzer returned a `## FIX BUNDLE` — worth attacking before any edit lands.
**Skip if intervention mode = conservative** (nothing is applied). Else persist the
bundle verbatim to `.claude/tasks/plans/<date>-<slug>-<HHMM>.md`, then run
`$HOME/.claude/lib/challenge-plan.md` with `PLAN` = that file, `KIND` = `fix-bundle`,
`SCOPE` = the target site files the items touch, `CONSTRAINTS` = the geo-analyzer
file-ownership (robots.txt, llms.txt, JSON-LD, content shape) + the shared-file edit
discipline each item carries + intervention mode. Three blind challengers ask, per item:
will it ACHIEVE its goal / could it BREAK or regress the page / is a simpler (or no) fix
better. This main loop RE-THINKS every aspect a BLOCKER lands (a named bundle change, or
`[deferred <date>]`) and re-challenges once if the bundle materially changed. Advisory —
it sits BEFORE (never replaces) the STEP 2 GATED approval; carry its CHALLENGE SUMMARY
into that gate.
## STEP 2 — Apply the fix bundle (from THIS main loop, at L1) ## STEP 2 — Apply the fix bundle (from THIS main loop, at L1)
@@ -90,6 +128,11 @@ Present every GATED item (G5.x) in ONE gate:
``` ```
GEO — gated content-shape changes need approval (visible): GEO — gated content-shape changes need approval (visible):
G5.1 <change> — impact: <visible change> G5.1 <change> — impact: <visible change>
CHALLENGE SUMMARY (STEP 1b — 3 lenses):
BLOCKERs addressed : <n> — <finding → the named bundle change that closes it>
Deferred (human-ack): <list | none>
Lenses returned : correctness / robustness / simplicity (NAME any that failed to return)
Approve all / select (ids) / skip all? Approve all / select (ids) / skip all?
``` ```
+20
View File
@@ -518,6 +518,21 @@ Extract the score and critical-alert count from `.claude/audits/HARDEN.md` for t
--- ---
## STEP 2b — CHALLENGE THE FIX BUNDLE (MODE=fix only, advisory)
Skip if MODE=audit (no bundle exists). Else, before the STEP 3 gate, harden the bundle:
extract the `## 8. Fix bundle` section from HARDEN.md to
`.claude/tasks/plans/<date>-<slug>-<HHMM>.md` (a clean, blind-judgeable artifact), then run
`$HOME/.claude/lib/challenge-plan.md` with `PLAN` = that file, `KIND` = `fix-bundle`,
`SCOPE` = the config/target files each patch touches (.htaccess, next.config.js, _headers…),
`CONSTRAINTS` = the STEP 1 strict scope + framework-native mechanism rule (no `.htaccess`
on Next/Astro) + the severity guide. Three blind challengers ask, per patch: will it ACHIEVE
the hardening goal / could it BREAK the site (over-broad CSP, redirect loop) / is a simpler
fix better. This main loop RE-THINKS every aspect a BLOCKER lands (a named bundle change, or
`[deferred <date>]`) and re-challenges once if the bundle materially changed. Advisory — it
sits BEFORE (never replaces) the STEP 3 confirmation; carry its CHALLENGE SUMMARY into that gate.
---
## STEP 3 — Apply fixes (MODE=fix only) ## STEP 3 — Apply fixes (MODE=fix only)
Skip this step if MODE=audit. Skip this step if MODE=audit.
@@ -534,6 +549,11 @@ If MODE=fix and `.claude/audits/HARDEN.md` ends with `READY TO APPLY — awaitin
- .htaccess (3 fixes : HTTP→HTTPS redirect, HSTS, 404 page) - .htaccess (3 fixes : HTTP→HTTPS redirect, HSTS, 404 page)
- next.config.js (2 fixes : CSP header, X-Frame-Options) - next.config.js (2 fixes : CSP header, X-Frame-Options)
CHALLENGE SUMMARY (STEP 2b — 3 lenses) :
BLOCKERs addressed : <n> — <finding → the named bundle change that closes it>
Deferred (human-ack): <list | none>
Lenses returned : correctness / robustness / simplicity (NAME any that failed to return)
Options : Options :
A) Apply all A) Apply all
B) Review each diff before applying B) Review each diff before applying
+30 -3
View File
@@ -76,6 +76,26 @@ security gate and the escalation report if a gate fails. No verifier is
dispatched at hotfix weight — STEP 4's smoke result already verifies these dispatched at hotfix weight — STEP 4's smoke result already verifies these
trivial criteria; the gate hotfix adds is security (STEP 4). trivial criteria; the gate hotfix adds is security (STEP 4).
## STEP 1.8 — CHALLENGE THE FIX (logic fixes only)
GUARD — this is the one place the plan-challenge phase is kept proportionate to
hotfix's speed. SKIP entirely for a purely cosmetic fix (CSS value, copy/typo, a
broken link): there is nothing for three lenses to bite on, and speed is the
point. Run it ONLY when the settled fix touches control flow or behaviour — an
off-by-one, a wrong operator/variable, a behaviour-changing config value, or a
missing import that alters execution. In doubt → it is probably a `/bugfix`.
For a logic fix: persist the STEP 1 located fix (root cause + the exact edit) to
`.claude/tasks/plans/<date>-<slug>-<HHMM>.md`, then run
`$HOME/.claude/lib/challenge-plan.md` with `PLAN` = that file, `KIND` =
`build-plan`, `SCOPE` = the 1-2 target files, `CONSTRAINTS` = the STEP 1.7
contract's acceptance criteria. Three blind challengers attack the fix; the main
loop RE-THINKS any aspect a BLOCKER lands (a named change to the fix, or
`[deferred]`) and re-challenges once if it materially changed. Print a
CHALLENGE SUMMARY (BLOCKERs addressed / deferred / lenses returned). A BLOCKER
that shows the fix is wrong or incomplete means this was never
a hotfix — escalate to `/bugfix` (its STEP 3b runs the same phase under the full
verify+secure loop).
## STEP 2 — PRE-FLIGHT ## STEP 2 — PRE-FLIGHT
**Gitflow aiguillage (before dispatch):** follow `$HOME/.claude/lib/gitflow-aiguillage.md` **Gitflow aiguillage (before dispatch):** follow `$HOME/.claude/lib/gitflow-aiguillage.md`
@@ -154,9 +174,16 @@ Parse the `HOTFIX-EXEC REPORT`:
## STEP 5 — DOC SYNC (automatic) ## STEP 5 — DOC SYNC (automatic)
Load `$HOME/.claude/agents/doc-syncer.md`. Dispatch the doc pipeline (BDR-077 — audit judgment on opus, patch on the
Execute in automatic mode: sonnet pin, gate HERE):
`auto-mode scope: <list of files modified during this session>` 1. `Agent(subagent_type="doc-syncer", model="opus")` — `MODE: audit` +
`auto-mode scope: <list of files modified during this session>`.
2. Silence (NONE) → done. `[MINOR]` PATCH PLAN → re-dispatch
`Agent(subagent_type="doc-syncer")` with `MODE: patch` + the plan
verbatim (no gate — auto behavior preserved; a `SHAPE ESCALATION` in
its report comes back here, gated as SIGNIFICANT).
3. SIGNIFICANT → gate here (`Apply? yes / no / select`), then
`MODE: patch` with the approved subset.
**Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits **Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits
ONLY the files doc-syncer patched (its `PATCHED_FILES` output), never `git add -A`, never ONLY the files doc-syncer patched (its `PATCHED_FILES` output), never `git add -A`, never
+46 -7
View File
@@ -36,7 +36,8 @@ so the user does not assume Claude has hung.
--- ---
## STEP 0 — PLUGIN CHECK + AUTO-ACTIVATE ## STEP 0 — PLUGIN CHECK + AUTO-ACTIVATE
Load `$HOME/.claude/agents/plugin-advisor.md`. Feed request. Run `$HOME/.claude/lib/plugin-gate.md`. Feed request (dispatch plugin-probe →
checkpoint → dispatch plugin-advisor; gates stay in this loop — BDR-077).
- ACTION REQUIRED → show RECOMMENDATIONS block, offer: A) fix plugins B) type "force". STOP. - ACTION REQUIRED → show RECOMMENDATIONS block, offer: A) fix plugins B) type "force". STOP.
- PROPOSED CHANGES exist → show list, ask "Apply? (yes / no / customize)". Apply on confirm. - PROPOSED CHANGES exist → show list, ask "Apply? (yes / no / customize)". Apply on confirm.
- OK → `✅ Plugin check passed — [active plugins] — complexity: <score>%`, continue. - OK → `✅ Plugin check passed — [active plugins] — complexity: <score>%`, continue.
@@ -91,15 +92,31 @@ contract, each tagged `[gated <date>]`. STEP 9's verifier judges against this
enriched contract. enriched contract.
## STEP 5 — SCAFFOLD ## STEP 5 — SCAFFOLD
Load `$HOME/.claude/agents/scaffolder.md`. Pass: BRIEF + DESIGN + `~/.claude/templates/project-CLAUDE.md` + `~/.claude/CLAUDE.md`. Dispatch `Agent(subagent_type="scaffolder")` (pin sonnet, effort high —
BDR-077 : le design est CLOS au gate #1, le scaffold est de l'exécution,
plus jamais inline sur le modèle de session). Pass IN THE PROMPT (LRN-126 —
every field the scaffolder consumes crosses the dispatch): BRIEF (verbatim)
+ DESIGN (verbatim) + paths `~/.claude/templates/project-CLAUDE.md` +
`~/.claude/CLAUDE.md`. A STOP (missing input) comes back as its report —
resolve here, re-dispatch. The ~30s liveness pings are THIS loop's job
while waiting.
Creates: CLAUDE.md, `.claude/settings.json`, `.claudeignore`, `.gitignore`, `.env.example`, empty entry points. NO README, NO features, NO `.claude/tasks/` or `.claude/memory/` (not bootstrapped by this flow — copy from `~/.claude/templates/memory/` manually if wanted before STEP 10b's memory commit). Creates: CLAUDE.md, `.claude/settings.json`, `.claudeignore`, `.gitignore`, `.env.example`, empty entry points. NO README, NO features, NO `.claude/tasks/` or `.claude/memory/` (not bootstrapped by this flow — copy from `~/.claude/templates/memory/` manually if wanted before STEP 10b's memory commit).
Verify: `git init` + build passes. Verify: `git init` + build passes.
## STEP 5b — CREATE README ## STEP 5b — CREATE README
Load `$HOME/.claude/agents/doc-syncer.md` (AUTO MODE, scope: full project). README.md missing → its README bootstrap creates it. No stop. Dispatch the doc pipeline (BDR-077):
`Agent(subagent_type="doc-syncer", model="opus")`
— `MODE: audit` (FULL-AUDIT path, NOT `auto-mode scope:` —
auto-mode gates a missing README as SIGNIFICANT; the full audit's STEP 5
renders it `[CREATE-AUTO]`, unconditional). README.md missing → the
report carries the rendered README draft as `[CREATE-AUTO]`; re-dispatch
`Agent(subagent_type="doc-syncer")` (sonnet pin) with `MODE: patch` +
that plan to write it. No stop (README bootstrap is unconditional).
## STEP 5c — CTX7 PRE-FETCH (if fast-libs detected) ## STEP 5c — CTX7 PRE-FETCH (if fast-libs detected)
If `fast-libs` signal was detected in STEP 0 (Next.js, React 18+, Prisma, Supabase, Drizzle, etc.): If `fast-libs` signal was detected in STEP 0 — single source of truth:
`bash ~/.claude/lib/fast-libs.sh detect .` (Next.js, React, Prisma,
Supabase, Drizzle… — BDR-078):
1. Create `.ctx7-cache/` directory in project root. 1. Create `.ctx7-cache/` directory in project root.
2. For each detected fast-lib, fetch core docs: 2. For each detected fast-lib, fetch core docs:
```bash ```bash
@@ -155,12 +172,27 @@ implemented on a `feature/*` branch off `develop` (STEP 8).
Invoke `superpowers:writing-plans` with BRIEF + skeleton. Invoke `superpowers:writing-plans` with BRIEF + skeleton.
Granular tasks (2-5 min each), exact file paths, TDD: tests before code. Granular tasks (2-5 min each), exact file paths, TDD: tests before code.
## STEP 6b — CHALLENGE THE PLAN (before the gate)
Before the human sees the implementation plan, harden it. Run
`$HOME/.claude/lib/challenge-plan.md` with `PLAN` = the plan STEP 6 wrote under
`docs/superpowers/plans/`, `KIND` = `build-plan`, `SCOPE` = the skeleton + task file
paths, `CONSTRAINTS` = the STEP 4-validated architecture + founding decisions.
Three blind challengers (correctness / robustness / simplicity) attack it; the main
loop RE-THINKS every aspect a BLOCKER lands (a named plan change, or `[deferred]`),
re-challenges once if the plan materially changed, and feeds the REVISED plan + a
CHALLENGE SUMMARY into STEP 7. Advisory — the human remains the decider.
## STEP 7 — VALIDATION GATE #2 ★ MANDATORY STOP ## STEP 7 — VALIDATION GATE #2 ★ MANDATORY STOP
``` ```
INIT PROJECT — IMPLEMENTATION PLAN INIT PROJECT — IMPLEMENTATION PLAN
SKELETON: ✅ build passes SKELETON: ✅ build passes
FEATURES: <N> → <M> tasks FEATURES: <N> → <M> tasks
<numbered task list with paths> <numbered task list with paths>
CHALLENGE SUMMARY (STEP 6b — 3 lenses):
BLOCKERs addressed : <n> — <finding → the named plan change that closes it>
Deferred (human-ack): <list | none>
Lenses returned : correctness / robustness / simplicity (NAME any that failed to return)
Approve and start? (yes / request changes) Approve and start? (yes / request changes)
``` ```
Changes → back to STEP 6. Approved → continue. Changes → back to STEP 6. Approved → continue.
@@ -208,7 +240,10 @@ against the founding contract. Distinct axis from STEP 10 code review
([[LRN-095]]) — both run. ([[LRN-095]]) — both run.
## STEP 10 — CODE REVIEW ## STEP 10 — CODE REVIEW
Invoke `superpowers:requesting-code-review`. Fix all CRITICAL before proceeding. Invoke `superpowers:requesting-code-review`. **Model routing (BDR-077):** the
review subagent it dispatches MUST carry `model: "opus"` in the Agent call —
craft review is dispatched judgment, never inherited from the session. Fix
all CRITICAL before proceeding.
## STEP 10b — CAPITALIZE FOUNDING DECISIONS (memory registries) ## STEP 10b — CAPITALIZE FOUNDING DECISIONS (memory registries)
A greenfield's founding architecture decisions are the highest-value BDRs — the A greenfield's founding architecture decisions are the highest-value BDRs — the
@@ -267,8 +302,12 @@ does NOT commit them, and `gitflow finish` integrates only COMMITTED history
— so a patch left uncommitted never reaches the merge/PR. Same PR-stranding class as the — so a patch left uncommitted never reaches the merge/PR. Same PR-stranding class as the
STEP 10b capitalize fix (BDR-034). STEP 10b capitalize fix (BDR-034).
Load `$HOME/.claude/agents/doc-syncer.md` (AUTO MODE, scope: files changed this session). Dispatch the doc pipeline (BDR-077):
Detect drift, update cmds/vars/structure, add recent changes entry. `Agent(subagent_type="doc-syncer", model="opus")`
— `MODE: audit` + `auto-mode scope: <files changed this
session>`; NONE → done; `[MINOR]` plan → `MODE: patch` re-dispatch (sonnet
pin, no gate; SHAPE ESCALATION comes back gated); SIGNIFICANT → gate here,
then `MODE: patch` with the approved subset.
**Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits **Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits
ONLY the files doc-syncer patched (its `PATCHED_FILES` output, one path per line → one argv ONLY the files doc-syncer patched (its `PATCHED_FILES` output, one path per line → one argv
+42 -12
View File
@@ -21,7 +21,7 @@ $ARGUMENTS
## STEP 0 — PLUGIN CHECK + AUTO-ACTIVATE ## STEP 0 — PLUGIN CHECK + AUTO-ACTIVATE
Load `$HOME/.claude/agents/plugin-advisor.md` with hint "onboarding existing project + $ARGUMENTS". Run `$HOME/.claude/lib/plugin-gate.md` with hint "onboarding existing project + $ARGUMENTS" (dispatch plugin-probe → checkpoint → dispatch plugin-advisor → gates in this loop, BDR-077).
- ACTION REQUIRED → show RECOMMENDATIONS block, offer: A) apply recos B) type "force". STOP. - ACTION REQUIRED → show RECOMMENDATIONS block, offer: A) apply recos B) type "force". STOP.
- PROPOSED CHANGES exist → show list, ask "Apply? (yes / no / customize)". Apply on confirm. - PROPOSED CHANGES exist → show list, ask "Apply? (yes / no / customize)". Apply on confirm.
@@ -89,7 +89,11 @@ STOP. La réponse détermine si STEP 1 tourne une fois (A) ou N fois (C) ou avec
## STEP 2 — BASELINE CONFIG (onboarder agent) ## STEP 2 — BASELINE CONFIG (onboarder agent)
Load `$HOME/.claude/agents/onboarder.md`. Passer un BRIEF minimal issu du filesystem scan : Dispatch `Agent(subagent_type="onboarder")` (pin sonnet — BDR-077 : config
templating = exécution, plus jamais inline sur le modèle de session). Un
BLOCAGE (clé manquante, CLAUDE.md existant) revient en rapport — l'agent ne
peut pas te demander ; TU arbitres ici puis re-dispatches. Passer un BRIEF
minimal issu du filesystem scan :
- `archetype` (depuis STEP 1) - `archetype` (depuis STEP 1)
- `project_name` (depuis package.json/pyproject.toml/README.md/dir name) - `project_name` (depuis package.json/pyproject.toml/README.md/dir name)
- `stack` (depuis manifests détectés) - `stack` (depuis manifests détectés)
@@ -203,13 +207,10 @@ ls .ctx7-cache/ 2>/dev/null
``` ```
### Détection fast-libs ### Détection fast-libs
Parse manifests selon l'archétype : Source unique : `bash ~/.claude/lib/fast-libs.sh detect .` (deps JS/TS via
- **nextjs-app-router** → chercher : next, react, prisma, @supabase/*, drizzle-orm, next-auth, @clerk/* package.json + Python via requirements/pyproject — liste centralisée,
- **react-spa** → chercher : react, @tanstack/*, zustand, jotai BDR-078). Archétypes wordpress / cli-tool / library / dotfiles-meta /
- **rest-api-node** → chercher : fastify, @nestjs/*, prisma, drizzle-orm static-html → souvent aucune fast-lib, audit léger.
- **rest-api-python** → chercher : fastapi, pydantic, sqlalchemy (si ≥ 2.0)
- **astro-static** → chercher : astro, @astrojs/*
- **wordpress / cli-tool / library / dotfiles-meta / static-html** → souvent aucune fast-lib, audit léger
### Vérification cache ### Vérification cache
Pour chaque fast-lib détectée : Pour chaque fast-lib détectée :
@@ -354,7 +355,7 @@ Lire le bloc `audit_stack:` du fichier `~/.claude/lib/project-archetypes/<archet
| Entry | Action | Livraison | | Entry | Action | Livraison |
|---|---|---| |---|---|---|
| `analyze` | Déjà fait en STEP 5 | L3a | | `analyze` | Déjà fait en STEP 5 | L3a |
| `code-clean` | Spawn subagent `general-purpose` (audit-only, inherits session = big model) | L3a | | `code-clean` | Spawn subagent `general-purpose` (audit-only, `model="opus"` — BDR-076: dispatched audits off the session model) | L3a |
| `cso` | Si gstack ON → Skill(cso). Sinon → Agent general-purpose avec checklist OWASP + deps audit | L3a | | `cso` | Si gstack ON → Skill(cso). Sinon → Agent general-purpose avec checklist OWASP + deps audit | L3a |
| `doc` | Spawn subagent `doc-syncer` (auto-mode OFF, report-only) | L3a | | `doc` | Spawn subagent `doc-syncer` (auto-mode OFF, report-only) | L3a |
| `seo` | Subagents seo-analyzer + geo-analyzer en parallèle | L3b | | `seo` | Subagents seo-analyzer + geo-analyzer en parallèle | L3b |
@@ -370,7 +371,8 @@ Lancer EN PARALLÈLE (un seul message, plusieurs Agent calls) les audits corresp
``` ```
Agent( Agent(
subagent_type="general-purpose", subagent_type="general-purpose",
description="Onboard — code-clean audit only (read-only, big session model)", model="opus",
description="Onboard — code-clean audit only (read-only, opus)",
prompt=""" prompt="""
AUDIT-ONLY mode — NO fixes, NO refactoring, NO file modifications. AUDIT-ONLY mode — NO fixes, NO refactoring, NO file modifications.
Target: <PROJECT_ROOT>. ARCHETYPE: <archetype>. Target: <PROJECT_ROOT>. ARCHETYPE: <archetype>.
@@ -406,6 +408,7 @@ bash $HOME/.claude/lib/toggle-external.sh list 2>/dev/null | grep -E "^gstack\s+
``` ```
Agent( Agent(
subagent_type="general-purpose", subagent_type="general-purpose",
model="opus",
description="Onboard — security audit fallback (archetype-adaptive)", description="Onboard — security audit fallback (archetype-adaptive)",
prompt=""" prompt="""
READ-ONLY security audit. No file modifications. READ-ONLY security audit. No file modifications.
@@ -529,9 +532,11 @@ flux de dev sont deux formes distinctes ([[BDR-050]] pipeline dev ≠ audit).
``` ```
Agent( Agent(
subagent_type="doc-syncer", subagent_type="doc-syncer",
model="opus",
description="Onboard — doc drift audit only", description="Onboard — doc drift audit only",
prompt=""" prompt="""
REPORT-ONLY mode — NO edits, NO auto-sync. MODE: audit — REPORT-ONLY, NO edits, NO auto-sync (no patch dispatch
follows: the report feeds the onboard backlog).
Target: full project at <PROJECT_ROOT>. Target: full project at <PROJECT_ROOT>.
Scope: Scope:
1. README drift (build/test commands, install steps, usage examples vs actual code) 1. README drift (build/test commands, install steps, usage examples vs actual code)
@@ -647,6 +652,7 @@ Si le skill ne supporte pas `--output`, capturer la sortie et écrire à la main
``` ```
Agent( Agent(
subagent_type="general-purpose", subagent_type="general-purpose",
model="opus",
description="Onboard — static design review fallback", description="Onboard — static design review fallback",
prompt=""" prompt="""
AUDIT-ONLY mode — NO edits. Static design review du code UI. AUDIT-ONLY mode — NO edits. Static design review du code UI.
@@ -690,6 +696,7 @@ Puis parser le JSON Lighthouse (scores perf/a11y/bp/seo/pwa + top opportunities)
``` ```
Agent( Agent(
subagent_type="general-purpose", subagent_type="general-purpose",
model="opus",
description="Onboard — static perf audit", description="Onboard — static perf audit",
prompt=""" prompt="""
AUDIT-ONLY mode — NO edits. AUDIT-ONLY mode — NO edits.
@@ -732,6 +739,7 @@ Parser axe-core résultats (violations, incomplete, inapplicable, passes) → `.
``` ```
Agent( Agent(
subagent_type="general-purpose", subagent_type="general-purpose",
model="opus",
description="Onboard — static a11y audit", description="Onboard — static a11y audit",
prompt=""" prompt="""
AUDIT-ONLY mode — NO edits. AUDIT-ONLY mode — NO edits.
@@ -777,6 +785,7 @@ Spawn un subagent synthétiseur (isolé, chargé uniquement du contenu de `.onbo
``` ```
Agent( Agent(
subagent_type="general-purpose", subagent_type="general-purpose",
model="opus",
description="Onboard — synthèse vers .claude/audits/", description="Onboard — synthèse vers .claude/audits/",
prompt=""" prompt="""
Lire tous les fichiers de <PROJECT_ROOT>/.onboard-audit/ : Lire tous les fichiers de <PROJECT_ROOT>/.onboard-audit/ :
@@ -869,6 +878,22 @@ Vérifier que les 4 fichiers `.claude/audits/ONBOARD_REPORT.md`, `.claude/audits
--- ---
## STEP 7b — CHALLENGE THE PROPOSALS (before the human gate)
The 4 audit files are on disk; `AUDIT_PROPOSALS.md` is the artifact worth
attacking before the human spends a gate on it. Run
`$HOME/.claude/lib/challenge-plan.md` with `PLAN` =
`.claude/audits/AUDIT_PROPOSALS.md`, `KIND` = `proposals`, `SCOPE` = the audited
project paths (the `audit_stack` coverage), `CONSTRAINTS` = the STEP 1 archetype
profile + the STEP 3 interview constraints (stade, légal, budget perf). Three
blind challengers ask whether these are the RIGHT priorities and what the audit
under-rated; the main loop RE-THINKS every aspect a BLOCKER lands (a named
proposals change re-written into `AUDIT_PROPOSALS.md`, or `[deferred <date>]`)
and re-challenges once if the file materially changed. Feed the REVISED
proposals + a CHALLENGE SUMMARY into STEP 8. Advisory — the human remains the
decider.
---
## STEP 8 — VALIDATION GATE ★ MANDATORY STOP ## STEP 8 — VALIDATION GATE ★ MANDATORY STOP
Afficher à l'utilisateur : Afficher à l'utilisateur :
@@ -893,6 +918,11 @@ TOP 5 PRIORITÉS :
4. [P1 Haute] <titre> 4. [P1 Haute] <titre>
5. [P2 Moyenne] <titre> 5. [P2 Moyenne] <titre>
CHALLENGE SUMMARY (STEP 7b — 3 lenses):
BLOCKERs addressed : <n> — <finding → the named proposals change that closes it>
Deferred (human-ack): <list | none>
Lenses returned : correctness / robustness / simplicity (NAME any that failed to return)
Prochaine étape : générer .claude/tasks/TODO.md depuis .claude/audits/AUDIT_PROPOSALS.md approuvé. Prochaine étape : générer .claude/tasks/TODO.md depuis .claude/audits/AUDIT_PROPOSALS.md approuvé.
Options : Options :
+10 -4
View File
@@ -2,13 +2,19 @@
name: plugin-check name: plugin-check
description: 'Audit active plugins vs project needs. Read-only advisory recommending enable/disable. Triggers: "plugin-check", "quels plugins".' description: 'Audit active plugins vs project needs. Read-only advisory recommending enable/disable. Triggers: "plugin-check", "quels plugins".'
argument-hint: '[ex: "React + FastAPI" or "Rust CLI, no frontend"]' argument-hint: '[ex: "React + FastAPI" or "Rust CLI, no frontend"]'
allowed-tools: Read, Bash, Glob, Grep allowed-tools: Read, Bash, Glob, Grep, Agent
--- ---
Load and follow strictly: `$HOME/.claude/agents/plugin-advisor.md`. Run `$HOME/.claude/lib/plugin-gate.md` on the context below — dispatch
plugin-probe (sonnet) → validation checkpoint → dispatch plugin-advisor
(opus) → present the PLUGIN CHECK block (BDR-077: detection and reasoning
dispatched, gates in this loop).
Analyze active plugins + context below, produce PLUGIN ADVISOR REPORT. /plugin-check is READ-ONLY advisory: SKIP the gate's step 5 (apply) — show
the advisor's exact toggle commands for the user instead. Never write; user
toggles via `claude plugin enable/disable`.
If `$HOME/.claude/agents/plugin-advisor.md` unreachable: emit `Plugin advisor agent missing.` and STOP. Never write — user toggles via `claude plugin enable/disable`. If `$HOME/.claude/lib/plugin-gate.md` unreachable: emit `Plugin gate include
missing.` and STOP.
$ARGUMENTS $ARGUMENTS
+15 -3
View File
@@ -61,6 +61,15 @@ protected — `set` will refuse to disable them even if the profile omits them.
**Managed plugins** that `set` may disable when not in profile: **Managed plugins** that `set` may disable when not in profile:
`ui-ux-pro-max@ui-ux-pro-max-skill`, `plugin-dev@claude-code-plugins`, `ui-ux-pro-max@ui-ux-pro-max-skill`, `plugin-dev@claude-code-plugins`,
`pr-review-toolkit@claude-code-plugins`. Other plugins are never auto-toggled. `pr-review-toolkit@claude-code-plugins`. Other plugins are never auto-toggled.
**Managed externals** (`emil-design-eng`, `frontend-design`,
`design-motion-principles`, `impeccable`) and **managed MCPs** (`magic`)
follow the same symmetry (BDR-079): `set` enables them when the profile
lists them (from parked state, or from `skills-external/` if the symlink
never existed) and parks/unregisters them when it does not — e.g. `set
backend` after design work turns emil and magic off. `darwin-skill` and any
other unlisted external are never auto-touched. gstack works the same
all the way down: a profile listing gstack skills while the whole pack is
off (via `toggle-external.sh`) re-enables JUST those skills on demand.
## Commands ## Commands
@@ -117,8 +126,11 @@ bash "$HOME/.claude/lib/profile.sh" $ARGUMENTS
update-check, learnings — script doesn't touch that infra. Disabled skills update-check, learnings — script doesn't touch that infra. Disabled skills
are just hidden from Claude Code's scanner; the gstack repo stays installed. are just hidden from Claude Code's scanner; the gstack repo stays installed.
- Profile changes DO toggle the managed Claude Code plugins (ui-ux-pro-max, - Profile changes DO toggle the managed Claude Code plugins (ui-ux-pro-max,
plugin-dev, pr-review-toolkit) and the `magic` MCP — see the Mechanism table plugin-dev, pr-review-toolkit), the managed external packs (emil-design-eng,
above (BDR-008). Anything outside that managed set stays manual: frontend-design, design-motion-principles, impeccable) and the `magic` MCP —
`claude plugin enable|disable`, `claude mcp add|remove`. in BOTH directions: `set` enables what the profile lists and disables the
managed leftovers it doesn't (BDR-008, BDR-079). Anything outside those
allowlists stays manual: `claude plugin enable|disable`, `claude mcp
add|remove`.
- `set` is destructive in the sense that it disables non-listed gstack skills. - `set` is destructive in the sense that it disables non-listed gstack skills.
Use `apply` if the user wants additive behavior. Use `apply` if the user wants additive behavior.
+98 -21
View File
@@ -302,14 +302,31 @@ still carries this rule for its applier:
If `Edit` is insufficient (full-template refactor), the item is escalated If `Edit` is insufficient (full-template refactor), the item is escalated
as a cross-agent note → §11 user action instead. as a cross-agent note → §11 user action instead.
## STEP 1 — Spawn both agents IN PARALLEL ## STEP 1 — Run both domain pipelines (3 phases; domains parallel per phase)
Issue both `Agent` tool calls **in the same message** (parallel tool BDR-077: each domain runs collect (sonnet) → judge (opus pin) → template
calls). The harness runs them concurrently. (sonnet), the two domains IN PARALLEL at every phase (both `Agent` calls
in the same message). Mint run ids first: `RUNID_SEO=$(date +%s)-seo`,
`RUNID_GEO=$(date +%s)-geo`. The CONTEXT payloads below are the SHARED
CONTEXT of each domain — pass them VERBATIM to every phase dispatch of
that domain (LRN-126). Clean both `.audit/*-signals-*.md` files after
STEP 2.
**DISPATCHER ERROR CONTRACT (fail-closed at the pipeline, not just the
judge):** a `SEO JUDGE — VERDICT: ERROR(…)` / `GEO JUDGE — VERDICT:
ERROR(…)`, a mute judge, or a BLOCKED collect → STOP that domain: NO
template dispatch, NO L1 apply for it. Surface the error verbatim, retry
ONCE with a fresh collect+judge for that domain; a 2nd failure →
escalate to the human. A mute or ERROR judge is NEVER carried into
templating.
**PHASE A — collect (both domains, one message):**
``` ```
Agent(subagent_type="seo-analyzer") Agent(subagent_type="seo-analyzer", model="sonnet")
prompt: """ prompt: """
MODE: collect
RUNID: <RUNID_SEO>
Dispatched from /seo. Context: Dispatched from /seo. Context:
AUDIT DEPTH: <LOCAL|FULL> AUDIT DEPTH: <LOCAL|FULL>
@@ -379,18 +396,15 @@ SHARED-FILE EDIT DISCIPLINE (carried into each bundle item):
legal pages, new city/service pages. legal pages, new city/service pages.
- If full-template refactor is needed, emit as a cross-agent note → §11. - If full-template refactor is needed, emit as a cross-agent note → §11.
Execute your agent spec at ~/.claude/agents/seo-analyzer.md starting Execute MODE: collect per your spec — STEP 2-5 only (context above
at STEP 2 (skip STEP 0 and STEP 1 — context is provided above). replaces STEP 0-1). Write the signals file + COLLECTION COMPLETE
sentinel, emit the COLLECT REPORT, stop. No scoring, no bundle.
At STEP 13, emit the STRUCTURED ENVELOPE for merging (not a standalone
SEO.md), INCLUDING the `## FIX BUNDLE` section terminated by the verbatim
`READY TO APPLY — awaiting dispatcher confirmation` sentinel. Do NOT apply
any fix, do NOT dispatch any sub-agent, do NOT write SEO.md — /seo applies
your bundle in STEP 1.5 and merges the reports.
""" """
Agent(subagent_type="geo-analyzer") Agent(subagent_type="geo-analyzer", model="sonnet")
prompt: """ prompt: """
MODE: collect
RUNID: <RUNID_GEO>
Dispatched from /seo. Context: Dispatched from /seo. Context:
AUDIT DEPTH: <LOCAL|FULL> AUDIT DEPTH: <LOCAL|FULL>
@@ -436,17 +450,75 @@ SHARED-FILE EDIT DISCIPLINE (carried into each bundle item):
llms-full.txt. llms-full.txt.
- If full-template refactor is needed, emit as a cross-agent note → §11. - If full-template refactor is needed, emit as a cross-agent note → §11.
Execute your agent spec at ~/.claude/agents/geo-analyzer.md starting Execute MODE: collect per your spec — STEP 2-5 only (context above
at STEP 2 (skip STEP 0 and STEP 1 — context is provided above). replaces STEP 0-1). Write the signals file + COLLECTION COMPLETE
sentinel, emit the COLLECT REPORT, stop. No scoring, no bundle.
At STEP 14, emit the STRUCTURED ENVELOPE for merging (not a standalone
GEO.md), INCLUDING the `## FIX BUNDLE` section terminated by the verbatim
`READY TO APPLY — awaiting dispatcher confirmation` sentinel. Do NOT apply
any fix, do NOT dispatch any sub-agent, do NOT write GEO.md/SEO.md — /seo
applies your bundle in STEP 1.5 and merges the reports.
""" """
``` ```
**PHASE B — judge (both domains, one message, AFTER both COLLECT REPORTs
are DONE):** no `model=` override — the opus frontmatter pins apply.
```
Agent(subagent_type="seo-analyzer")
prompt: "MODE: judge
RUNID: <RUNID_SEO>
<the seo SHARED CONTEXT verbatim>
Load .audit/seo-signals-<RUNID_SEO>.md (fail closed per your spec), run
STEP 6-11, report scoring + findings + action plan + triage batches."
Agent(subagent_type="geo-analyzer")
prompt: "MODE: judge
RUNID: <RUNID_GEO>
<the geo SHARED CONTEXT verbatim>
Load .audit/geo-signals-<RUNID_GEO>.md (fail closed per your spec), run
STEP 6-12, report scoring + findings + action plan + triage batches."
```
Apply the DISPATCHER ERROR CONTRACT above on each returned verdict.
**PHASE C — template (both domains, one message, only for domains whose
judge is DONE):**
```
Agent(subagent_type="seo-analyzer", model="sonnet")
prompt: "MODE: template
<the seo SHARED CONTEXT verbatim>
JUDGE REPORT (verbatim, ground truth — never re-derive a score):
<the seo judge report>
Run STEP 12-14: emit the STRUCTURED ENVELOPE for merging (not a
standalone SEO.md), INCLUDING the `## FIX BUNDLE` section terminated by
the verbatim `READY TO APPLY — awaiting dispatcher confirmation`
sentinel. Do NOT apply any fix, do NOT dispatch any sub-agent, do NOT
write SEO.md — /seo applies your bundle in STEP 1.5 and merges the
reports."
Agent(subagent_type="geo-analyzer", model="sonnet")
prompt: "MODE: template
<the geo SHARED CONTEXT verbatim>
JUDGE REPORT (verbatim, ground truth — never re-derive a score):
<the geo judge report>
Run STEP 13-15: emit the STRUCTURED ENVELOPE for merging (not a
standalone GEO.md), INCLUDING the `## FIX BUNDLE` section terminated by
the verbatim `READY TO APPLY — awaiting dispatcher confirmation`
sentinel. Do NOT apply any fix, do NOT dispatch any sub-agent, do NOT
write GEO.md/SEO.md — /seo applies your bundle in STEP 1.5 and merges
the reports."
```
## STEP 1b — CHALLENGE THE FIX BUNDLE (advisory, before apply)
Both envelopes now carry a `## FIX BUNDLE` — worth attacking before any edit lands.
**Skip if intervention mode = conservative** (nothing is applied). Else persist both
bundles (seo + geo, verbatim) to `.claude/tasks/plans/<date>-<slug>-<HHMM>.md`, then run
`$HOME/.claude/lib/challenge-plan.md` with `PLAN` = that file, `KIND` = `fix-bundle`,
`SCOPE` = the target site files the items touch, `CONSTRAINTS` = the STEP 0 file-ownership
matrix + shared-file edit discipline + confirmed Canonical NAP + intervention mode. Three
blind challengers ask, per item: will it ACHIEVE its goal / could it BREAK or regress the
page / is a simpler (or no) fix better. This main loop RE-THINKS every aspect a BLOCKER
lands (a named bundle change, or `[deferred <date>]`) and re-challenges once if the bundle
materially changed. Advisory — it sits BEFORE (never replaces) the STEP 1.5 GATED approval;
carry its CHALLENGE SUMMARY into that gate.
## STEP 1.5 — Apply fix bundles (from THIS main loop, at L1) ## STEP 1.5 — Apply fix bundles (from THIS main loop, at L1)
Both analyzers returned an envelope containing a `## FIX BUNDLE` section Both analyzers returned an envelope containing a `## FIX BUNDLE` section
@@ -493,6 +565,11 @@ Collect every GATED item from BOTH bundles and present ONE gate:
SEO/GEO — gated changes need approval (visible / structural): SEO/GEO — gated changes need approval (visible / structural):
D1 <change> — impact: <visible change> [seo] D1 <change> — impact: <visible change> [seo]
G5.1 <change> — impact: <visible change> [geo] G5.1 <change> — impact: <visible change> [geo]
CHALLENGE SUMMARY (STEP 1b — 3 lenses):
BLOCKERs addressed : <n> — <finding → the named bundle change that closes it>
Deferred (human-ack): <list | none>
Lenses returned : correctness / robustness / simplicity (NAME any that failed to return)
Approve all / select (ids) / skip all? Approve all / select (ids) / skip all?
``` ```
+33 -5
View File
@@ -20,7 +20,8 @@ $ARGUMENTS
--- ---
## STEP 0 — PLUGIN CHECK + AUTO-ACTIVATE ## STEP 0 — PLUGIN CHECK + AUTO-ACTIVATE
Load `$HOME/.claude/agents/plugin-advisor.md`. Feed request. Run `$HOME/.claude/lib/plugin-gate.md`. Feed request (dispatch plugin-probe →
checkpoint → dispatch plugin-advisor; gates stay in this loop — BDR-077).
- ACTION REQUIRED → show RECOMMENDATIONS block, offer: A) fix plugins B) type "force". STOP. - ACTION REQUIRED → show RECOMMENDATIONS block, offer: A) fix plugins B) type "force". STOP.
- PROPOSED CHANGES exist → show list, ask "Apply? (yes / no / customize)". Apply on confirm. - PROPOSED CHANGES exist → show list, ask "Apply? (yes / no / customize)". Apply on confirm.
- OK → `✅ Plugin check passed — [active plugins] — complexity: <score>%`, continue. - OK → `✅ Plugin check passed — [active plugins] — complexity: <score>%`, continue.
@@ -51,7 +52,8 @@ ls .gsd/ROADMAP.md 2>/dev/null | head -1
STOP. STOP.
## STEP 0c — CTX7 CACHE CHECK (if fast-libs in project) ## STEP 0c — CTX7 CACHE CHECK (if fast-libs in project)
Check if the project uses fast-evolving libs (scan `package.json` for next, react, prisma, supabase, drizzle, expo): Check if the project uses fast-moving libs — single source of truth:
`bash ~/.claude/lib/fast-libs.sh detect .` (exit 1 = none; BDR-078):
1. If `.ctx7-cache/` exists with recent files (<7 days old) → print `📚 ctx7 cache found: <libs>` and continue. 1. If `.ctx7-cache/` exists with recent files (<7 days old) → print `📚 ctx7 cache found: <libs>` and continue.
2. If `.ctx7-cache/` missing or stale AND `ctx7` is installed AND fast-libs detected: 2. If `.ctx7-cache/` missing or stale AND `ctx7` is installed AND fast-libs detected:
```bash ```bash
@@ -115,6 +117,19 @@ Invoke `superpowers:writing-plans` with the validated design AND the 0d digest:
must be consistent with the in-force constraints; where a task implements or affects one, must be consistent with the in-force constraints; where a task implements or affects one,
note the ID inline. Break design into tasks (2-5 min each). Each task: exact file paths, full code, verification steps. note the ID inline. Break design into tasks (2-5 min each). Each task: exact file paths, full code, verification steps.
## STEP 2b — CHALLENGE THE PLAN (adversarial, before the gate)
Before the human sees the plan, harden it. Run `$HOME/.claude/lib/challenge-plan.md`:
- `PLAN` = the plan STEP 2 wrote under `docs/superpowers/plans/`
- `KIND` = `build-plan`
- `SCOPE` = the files/dirs the plan touches
- `CONSTRAINTS` = the STEP 1 validated design's decided trade-offs / rejected options
Three blind `plan-challenger` subagents (correctness / robustness / simplicity)
attack it in parallel on the big model; the main loop RE-THINKS every aspect a
BLOCKER lands (a named plan change, or `[deferred <date>]`), re-challenges once if
the plan materially changed, and feeds the REVISED plan + a CHALLENGE SUMMARY into
STEP 3. Advisory — the human remains the decider.
## STEP 3 — VALIDATION GATE ★ MANDATORY STOP ## STEP 3 — VALIDATION GATE ★ MANDATORY STOP
``` ```
SHIP FEATURE — VALIDATION GATE SHIP FEATURE — VALIDATION GATE
@@ -127,6 +142,11 @@ RELATED MEMORY — disposition CLAIMED by this plan (review each):
- BLK-009 [already seen] — <how avoided / why N-A> - BLK-009 [already seen] — <how avoided / why N-A>
Review the claims above — flag any item the plan does NOT actually honor. Review the claims above — flag any item the plan does NOT actually honor.
CHALLENGE SUMMARY (STEP 2b — 3 lenses):
BLOCKERs addressed : <n> — <finding → the named plan change that closes it>
Deferred (human-ack): <list | none>
Lenses returned : correctness / robustness / simplicity (NAME any that failed to return)
Approve and execute? (yes / request changes) Approve and execute? (yes / request changes)
``` ```
This block EXPOSES each in-force item with the plan's CLAIMED disposition, for human This block EXPOSES each in-force item with the plan's CLAIMED disposition, for human
@@ -204,7 +224,10 @@ conformity + security vs. craft/design) — both run, neither subsumes the
other ([[LRN-095]]). other ([[LRN-095]]).
## STEP 6 — CODE REVIEW ## STEP 6 — CODE REVIEW
Invoke `superpowers:requesting-code-review`. Fix all CRITICAL before proceeding. Invoke `superpowers:requesting-code-review`. **Model routing (BDR-077):** the
review subagent it dispatches MUST carry `model: "opus"` in the Agent call —
craft review is dispatched judgment, never inherited from the session. Fix
all CRITICAL before proceeding.
## STEP 7 — CAPITALIZE (memory registries) ## STEP 7 — CAPITALIZE (memory registries)
Feature shipped implies at least one design decision worth capturing. Run this BEFORE STEP 9 FINISH — the implementation commits (STEP 4) already exist, so the entries' hash references are valid, and the memory commit lands on the branch that FINISH integrates (otherwise it strands outside the PR): Feature shipped implies at least one design decision worth capturing. Run this BEFORE STEP 9 FINISH — the implementation commits (STEP 4) already exist, so the entries' hash references are valid, and the memory commit lands on the branch that FINISH integrates (otherwise it strands outside the PR):
@@ -246,8 +269,13 @@ Run BEFORE STEP 9 FINISH. doc-syncer PATCHES public docs but does NOT commit the
uncommitted (or committed after) never reaches the merge/PR. Same PR-stranding class as the uncommitted (or committed after) never reaches the merge/PR. Same PR-stranding class as the
STEP 7 capitalize fix (BDR-034). STEP 7 capitalize fix (BDR-034).
Load `$HOME/.claude/agents/doc-syncer.md`. Execute in automatic mode: Dispatch the doc pipeline (BDR-077 — audit judgment on opus, patch on the
`auto-mode scope: <list of files modified during this session>` sonnet pin, gate HERE): `Agent(subagent_type="doc-syncer", model="opus")` —
`MODE: audit` + `auto-mode scope: <list of files modified during this
session>`. NONE → done; `[MINOR]` PATCH PLAN → re-dispatch
`Agent(subagent_type="doc-syncer")` with `MODE: patch` + the plan verbatim
(SHAPE ESCALATION comes back here, gated); SIGNIFICANT → gate here, then
`MODE: patch` with the approved subset.
**Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits **Then commit the docs** — follow `$HOME/.claude/lib/doc-commit.md`: it surgically commits
ONLY the files doc-syncer patched (its `PATCHED_FILES` output, one path per line → one argv ONLY the files doc-syncer patched (its `PATCHED_FILES` output, one path per line → one argv
+3 -3
View File
@@ -111,9 +111,9 @@ honestly in the summary. Never loop past 3.
### Phase B — CLEAN ### Phase B — CLEAN
1. Dispatch a read-only cleanup audit (analyzer or general-purpose — 1. Dispatch a read-only cleanup audit (analyzer — opus-pinned, BDR-076 —
inherits the big session model; NOT the sonnet code-cleaner, which is or general-purpose with `model="opus"`; NOT the sonnet code-cleaner,
now a fix executor): dead code, unused imports/exports, which is now a fix executor): dead code, unused imports/exports,
commented-out blocks, stale flags, norm violations. Findings as commented-out blocks, stale flags, norm violations. Findings as
`id | file:line | finding | proposed fix`. `id | file:line | finding | proposed fix`.
2. Apply **behavior-preserving** fixes only. A finding that would change 2. Apply **behavior-preserving** fixes only. A finding that would change
+21
View File
@@ -251,6 +251,22 @@ grep -c '^### \[Critique\]' .claude/audits/VALIDATE.md
--- ---
## STEP 2b — CHALLENGE THE FIX BUNDLE (MODE=fix only, advisory)
Skip if MODE=audit (no bundle exists). Else, before the STEP 3 gate, harden the bundle:
extract the `## 5. Fix bundle` section from VALIDATE.md to
`.claude/tasks/plans/<date>-<slug>-<HHMM>.md` (a clean, blind-judgeable artifact), then run
`$HOME/.claude/lib/challenge-plan.md` with `PLAN` = that file, `KIND` = `fix-bundle`,
`SCOPE` = the HTML/CSS files each fix touches, `CONSTRAINTS` = the STEP 1 strict scope (W3C
validity + WCAG 2.1) + the conservative auto-fix rule (structural/syntactic only, content →
§6) + the shared-template discipline (targeted Edit, never Write — templates carry /seo +
/geo content). Three blind challengers ask, per fix: will it ACHIEVE conformance / could it
BREAK rendering or regress another SC / is a simpler fix better. This main loop RE-THINKS
every aspect a BLOCKER lands (a named bundle change, or `[deferred <date>]`) and re-challenges
once if the bundle materially changed. Advisory — it sits BEFORE (never replaces) the STEP 3
confirmation; carry its CHALLENGE SUMMARY into that gate.
---
## STEP 3 — Apply fixes (MODE=fix only) ## STEP 3 — Apply fixes (MODE=fix only)
Skip this step if `MODE=audit`. Skip this step if `MODE=audit`.
@@ -271,6 +287,11 @@ Files to modify (N) :
Critical : X | Haute : Y | Moyenne : Z | Basse : W Critical : X | Haute : Y | Moyenne : Z | Basse : W
CHALLENGE SUMMARY (STEP 2b — 3 lenses) :
BLOCKERs addressed : <n> — <finding → the named bundle change that closes it>
Deferred (human-ack): <list | none>
Lenses returned : correctness / robustness / simplicity (NAME any that failed to return)
Options : Options :
A) Apply all A) Apply all
B) Review each diff before applying B) Review each diff before applying
+1 -1
View File
@@ -1 +1 @@
1.1.0 1.4.0