Compare commits
6
Commits
709cf9bf0b
..
v1.2.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c243ece97 | ||
|
|
51b65727e7 | ||
|
|
2f8dc6be1a | ||
|
|
dc4f78b1f0 | ||
|
|
709facfb52 | ||
|
|
d3d72fd3ca |
@@ -208,10 +208,3 @@ 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.
|
|
||||||
|
|||||||
@@ -91,7 +91,6 @@ 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 |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -981,7 +980,6 @@ 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]].
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -1067,12 +1065,3 @@ Supersedes BDR-076 scope + amends BDR-066. Doctrine: session model (Fable) = mai
|
|||||||
### BDR-078 — ctx7 coverage: central fast-libs list + once-per-session reminder hook; every code path covered [accepted] (2026-07-20)
|
### 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).
|
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.
|
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).
|
|
||||||
|
|
||||||
### BDR-081 — Config recalibrated for Claude 5 family (Opus 5 dispatch tier) [accepted] (2026-07-30)
|
|
||||||
Opus 5 (released 2026-07-24) now backs every `model: opus` pin (BDR-076/077) + any `/model opus` session. Research (official migration guide + web + registries): Opus 5 OVER-delegates (inverts LRN-030 Opus 4.8 trait that CLAUDE.global.md:43-47 compensated), self-verifies (explicit verify instructions → over-verification, "removing them reduces wasted tokens with no loss in quality"), literal following (conservative-reporting clauses depress recall; MUST/CRITICAL over-triggers), scope expansion = named regression, written deliverables +30-40%. Claude Code injects Opus-5-only anti-delegation prompt sections (heron_brook + subagent_steer_delegation, issue #80988, server-gated, no opt-out) — prose caps would triple-stack. Shipped: delegation block → model-neutral WHEN-guidance + explicit gates carve-out (verifier/security/challenge still dispatch as written); "staff engineer" self-check bar dropped; finish-whole-task clause folded into Deviations (gone-WRONG→STOP still wins); deliverable-length rule; design hook `\bux\b` dropped (`\bui\b` KEPT — 0 FP, 1 logged TP, lock-tested); plan-challenger grounded-doubt→[MINOR] in-place reword (grammar byte-identical). Plan challenged by 3 blind Opus 5 plan-challengers: correctness CONCERNS(4) / robustness FATAL(5, BLOCKER: all surfaces symlink-deployed LIVE — gates fire post-deployment) / simplicity CONCERNS(4); every fix adopted as prescribed (scratch-validation before live hook write, minimal diffs, ux-only, MINOR-routing). Alternatives rejected: leave as-is (nudge actively counter-productive); hard spawn caps in prose (harness injects one); confidence axis on challenger grammar (consumer unwired); dropping \bui\b (no evidence). NOT touched: verify-secure-loop + fresh gates (harness architecture BDR-049/050, ≠ model self-check prose); Security/Architecture sections (BDR-021); settings effortLevel xhigh (user pref — Opus 5 carry-over trap → LRN-139); superpowers plugin wording (external upstream). Plan+synthesis: .claude/tasks/plans/2026-07-30-opus5-config-tuning-1238.md. Branch feature/opus5-config-tuning, unmerged (human gate).
|
|
||||||
|
|||||||
@@ -417,16 +417,3 @@ rules:
|
|||||||
## 2026-07-20
|
## 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.
|
- 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.
|
- 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.
|
|
||||||
|
|
||||||
## 2026-07-30
|
|
||||||
- User: Opus 5 "needs more freedom" → analyse config + adapt. Research 3-agent (registries / config audit / web) + official migration guide: over-delegation (inverts LRN-030), over-verification, literal following, scope expansion, #80988 injections. Plan challenged 3 blind Opus 5 plan-challengers — robustness FATAL (BLOCKER: symlink-live deployment), all fixes adopted. Shipped: CLAUDE.global.md recalibrated (delegation when-guidance, staff-bar dropped, finish-whole-task, deliverable-length; 308/320), design hook \bux\b dropped flip-tested (22/0), plan-challenger grounded-doubt→[MINOR] (44/0). BDR-081 + LRN-139. feature/opus5-config-tuning, UNMERGED.
|
|
||||||
|
|||||||
@@ -1349,15 +1349,3 @@ rules:
|
|||||||
- **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).
|
- **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.
|
- **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]].
|
- **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]].
|
|
||||||
|
|
||||||
## LRN-139 — model-trait compensations invert across generations; state WHEN-guidance, not direction (2026-07-30)
|
|
||||||
- **pattern**: config rules that COMPENSATE a model trait become counter-productive when the next generation inverts the trait. LRN-030 (Opus 4.8 under-delegates → "Default to delegation… counters under-delegation") inverted by Opus 5 (delegates MORE readily, official guide) — the rule pushed the failure the model now has. Same class: explicit verify instructions → over-verification; conservative-reporting clauses → literal recall suppression; MUST/CRITICAL → over-triggering.
|
|
||||||
- **Opus 5 traps found**: (a) Claude Code injects Opus-5-only anti-delegation prompt sections (heron_brook + subagent_steer_delegation, issue #80988; server-gated, no opt-out, absent from transcripts) — own prose stacks on top blindly; (b) NO model-default effort hold on Opus 5 — persisted effortLevel (xhigh, settings.json) silently carries over, against "start high, sweep low/medium"; run /effort sweep per model; (c) effort does NOT shorten visible output/deliverables — only prose length rules do (+30-40% docs).
|
|
||||||
- **future application**: at every model-generation bump, grep config for trait-compensating language ("counters model tendency…", "default to X") and re-verify the premise; prefer WHEN-guidance (conditions where X pays) over directional nudges — survives inversions unchanged.
|
|
||||||
- **link**: [[LRN-030]] [[BDR-081]].
|
|
||||||
|
|||||||
+5
-79
@@ -1,77 +1,5 @@
|
|||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
## 2026-07-30 — adapt config for Claude 5 family / Opus 5 (feature/opus5-config-tuning)
|
|
||||||
User: Opus 5 "needs more freedom" → research (official migration guide +
|
|
||||||
web + registres) confirms: over-delegates (inverts LRN-030 Opus 4.8 trait),
|
|
||||||
over-verifies if told to verify, literal instruction following, scope
|
|
||||||
expansion named regression, harness already injects anti-delegation on
|
|
||||||
Opus 5 (#80988). Plan: .claude/tasks/plans/2026-07-30-opus5-config-tuning-1238.md
|
|
||||||
— to be challenged by 3 blind plan-challengers (opus pins → Opus 5), then
|
|
||||||
executed on feature branch. NO merge (human gate).
|
|
||||||
Challenged 2026-07-30: correctness CONCERNS(4) · robustness FATAL(5, 1
|
|
||||||
BLOCKER: symlink-live deployment) · simplicity CONCERNS(4) — all fixes
|
|
||||||
adopted as prescribed (plan §5bis, v2 items below).
|
|
||||||
- [x] W0 branch first (eab2a10 parent); hook regex validated on scratch copy
|
|
||||||
(bash -n + shellcheck + 5 replays, HOME sandboxed) before live write
|
|
||||||
- [x] W1 delegation block v2 (when-guidance + gates carve-out + scoped don't-redo) — 0f7b565
|
|
||||||
- [x] W2 "staff engineer" bar line deleted — 0f7b565
|
|
||||||
- [x] W3 finish-whole-task folded into Deviations (+ gone-WRONG→STOP) — 0f7b565
|
|
||||||
- [x] W4 deliverable-length rule — 0f7b565
|
|
||||||
- [x] W5 line budget: 308/320
|
|
||||||
- [x] W6 hook \bux\b dropped, \bui\b kept + F10 must-fire lock, D11 quiet row
|
|
||||||
flip-tested (fire before/quiet after) — eab2a10, suite 22/0
|
|
||||||
- [x] W7 plan-challenger :82-83 reworded → [MINOR] routing, census row — c3d3f4d, 44/0
|
|
||||||
- [x] W8 BDR-081 + LRN-139 + journal + CHANGELOG
|
|
||||||
- [ ] W9 final gate: make test full suite
|
|
||||||
- [ ] W10 no gitflow finish (human gate) — merge only on explicit user signal
|
|
||||||
|
|
||||||
## 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)
|
## 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
|
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.
|
never consult ctx7; fast-libs list hardcoded 3×; zero deterministic backstop.
|
||||||
@@ -90,7 +18,7 @@ never consult ctx7; fast-libs list hardcoded 3×; zero deterministic backstop.
|
|||||||
- [x] `lib/tests/fast-libs.test.sh` (lib verbs + hook fire/sentinel/quiet)
|
- [x] `lib/tests/fast-libs.test.sh` (lib verbs + hook fire/sentinel/quiet)
|
||||||
— 11/0, auto-discovered by the make test glob.
|
— 11/0, auto-discovered by the make test glob.
|
||||||
- [x] Gate: shellcheck + make test green (review-guards 5/0). BDR-078 +
|
- [x] Gate: shellcheck + make test green (review-guards 5/0). BDR-078 +
|
||||||
journal + CHANGELOG done. Merged 8ee7d19, shipped v1.2.0.
|
journal + CHANGELOG done. Committed on branch, NO merge (human gate).
|
||||||
|
|
||||||
## 2026-07-19 — Opus-pin dispatched judgment agents (branch feature/opus-pin-audit-agents)
|
## 2026-07-19 — Opus-pin dispatched judgment agents (branch feature/opus-pin-audit-agents)
|
||||||
|
|
||||||
@@ -117,8 +45,8 @@ on audits). User approved: opus for judgment agents, drop local opus pin.
|
|||||||
- [x] `.claude/settings.local.json` — drop `"model": "opus-4-8[1m]"`
|
- [x] `.claude/settings.local.json` — drop `"model": "opus-4-8[1m]"`
|
||||||
(local, gitignored; Fable default from settings.json applies).
|
(local, gitignored; Fable default from settings.json applies).
|
||||||
- [x] Tests: model-routing + loops-light + shellcheck + make test.
|
- [x] Tests: model-routing + loops-light + shellcheck + make test.
|
||||||
- [x] Memory: BDR-076 append + journal line. Commit (feat + chore);
|
- [x] Memory: BDR-076 append + journal line. Commit (feat + chore),
|
||||||
merged 17fbe51, shipped v1.2.0 (reconcile 2026-07-20).
|
NO merge (human gate).
|
||||||
|
|
||||||
## 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)
|
## 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 ·
|
||||||
@@ -126,8 +54,8 @@ 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).
|
||||||
H1 DONE (url-guard 7d6aa09) · C1 DONE (sitemap verb, C1a/b/c). Branch MERGED
|
NEXT: H1 (SSRF/injection guard) → C1 (sitemap crawl). Human merge gate: all
|
||||||
to develop (92301fe), shipped in v1.2.0 (reconcile 2026-07-20).
|
10 commits await review; nothing merged to develop.
|
||||||
|
|
||||||
### 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
|
||||||
@@ -550,8 +478,6 @@ 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
|
||||||
|
|||||||
@@ -1,255 +0,0 @@
|
|||||||
# PLAN — Adapt claude-config for the Claude 5 family (Opus 5 focus)
|
|
||||||
|
|
||||||
Date: 2026-07-30 · Branch (planned): feature/opus5-config-tuning (off develop)
|
|
||||||
KIND: build-plan · Author: main-loop session (Fable 5)
|
|
||||||
|
|
||||||
## 1. Context & evidence
|
|
||||||
|
|
||||||
Opus 5 (`claude-opus-5`, released 2026-07-24) now backs every `model: opus`
|
|
||||||
agent pin in this repo (analyzer, plan-challenger, seo/geo-analyzer,
|
|
||||||
plugin-advisor — BDR-076/077) and any session the user switches to via
|
|
||||||
`/model opus`. Its documented behavioral profile differs from Opus 4.8 in
|
|
||||||
ways that make parts of this config counterproductive:
|
|
||||||
|
|
||||||
- E1 **Over-delegation**: Opus 5 "delegates to subagents more readily than
|
|
||||||
prior models" (official prompting guide). Opus 4.8 had the OPPOSITE trait
|
|
||||||
(LRN-030), and `CLAUDE.global.md:43-47` was written to counter it
|
|
||||||
("Counters model tendency to under-delegate"). The premise is inverted.
|
|
||||||
- E2 **Anti-delegation already injected by the harness**: Claude Code
|
|
||||||
v2.1.219 server-gates an Opus-5-only prompt section (`heron_brook` +
|
|
||||||
`subagent_steer_delegation`, GitHub issue #80988) that says "Do not call
|
|
||||||
the AgentTool unless the user requested it" and "Subagents multiply cost
|
|
||||||
and time…". Stacking our own hard cap on top would triple-constrain;
|
|
||||||
keeping a pro-delegation nudge would fight the injection. Model-neutral
|
|
||||||
when-guidance is the stable middle.
|
|
||||||
- E3 **Over-verification**: official guidance — "If your prompt contains
|
|
||||||
explicit verification instructions … remove them: instructions like these
|
|
||||||
cause over-verification on Claude Opus 5, and removing them reduces wasted
|
|
||||||
tokens with no loss in quality." Also true of per-prompt "double-check"
|
|
||||||
phrasing. Targets PROSE told to the model, not harness-level gates.
|
|
||||||
- E4 **Scope expansion**: named Opus 5 regression ("can expand the scope of
|
|
||||||
a task, adding steps that weren't requested"). Anthropic ships a literal
|
|
||||||
counter-block; tested to reduce scope changes "to nearly zero".
|
|
||||||
- E5 **Literal instruction following** (since 4.7, stronger now): aggressive
|
|
||||||
MUST/CRITICAL language over-triggers; conservative-reporting instructions
|
|
||||||
("only report high-severity") measurably depress recall in review/challenge
|
|
||||||
harnesses.
|
|
||||||
- E6 **Longer written deliverables**: files written to disk run ~30-40%
|
|
||||||
longer; `effort` does NOT control visible/deliverable length — only prose
|
|
||||||
instructions do.
|
|
||||||
- E7 **Overconstraint costs reasoning**: Anthropic removed >80% of Claude
|
|
||||||
Code's system prompt for Claude-5-generation models "with no measurable
|
|
||||||
loss"; named mechanism = tokens burned resolving conflicting rules.
|
|
||||||
- E8 **Hook false positive (today)**: `\bux\b` in
|
|
||||||
`hooks/design-toolchain-reminder.sh:47` fired on French prose ("changement
|
|
||||||
ux vu" — matches after apostrophe/slash/space); 2nd `ux` FP in the log,
|
|
||||||
both French. Continues the LRN-1005/1007 false-positive series. No test
|
|
||||||
row covers `\bui\b`/`\bux\b`.
|
|
||||||
- E9 **Effort carry-over trap**: Opus 5 has no model-default effort hold in
|
|
||||||
Claude Code — a persisted `xhigh` (our `settings.json:333`) silently
|
|
||||||
carries onto Opus 5 sessions, against Anthropic's "start at high, sweep
|
|
||||||
low/medium" guidance for that model.
|
|
||||||
|
|
||||||
## 2. Design decisions
|
|
||||||
|
|
||||||
- D1 The global instruction layer must be MODEL-NEUTRAL across the Claude 5
|
|
||||||
family (sessions run Fable 5 by default; dispatched judgment agents run
|
|
||||||
Opus 5; executors Sonnet). Fixes therefore express WHEN-guidance and
|
|
||||||
outcome bars, not directional compensation for one model's trait.
|
|
||||||
- D2 Harness-level quality gates (fresh blind verifier + security-auditor,
|
|
||||||
BDR-049/050; plan-challenge, BDR-075) are architecture, not model
|
|
||||||
self-check prompting. They stay. E3 applies only to prose that tells the
|
|
||||||
MODEL to verify its own work.
|
|
||||||
- D3 Per BDR-021, the Security and Architecture-decisions sections of
|
|
||||||
CLAUDE.global.md stay verbatim (deliberate policy). No softening there.
|
|
||||||
- D4 Registries are append-only: LRN-030 is not edited; a new LRN records
|
|
||||||
the trait inversion and points back to it.
|
|
||||||
- D5 Deterministic backstops (gitflow pre-commit, Gitea protection,
|
|
||||||
permissions.deny, rtk pinning) are explicitly out of "more freedom" scope
|
|
||||||
— community reports show Opus 5 working AROUND soft controls, which argues
|
|
||||||
for keeping hard ones.
|
|
||||||
|
|
||||||
## 3. Work items
|
|
||||||
|
|
||||||
### W1 — CLAUDE.global.md: rewrite the delegation block (:43-47)
|
|
||||||
Replace the 5-line block (incl. "Default to delegation for multi-file
|
|
||||||
exploration. Counters model tendency to under-delegate.") with model-neutral
|
|
||||||
when-guidance, same footprint (≤5 lines):
|
|
||||||
|
|
||||||
```
|
|
||||||
- Sub-agents: one task per sub-agent, main context stays clean.
|
|
||||||
Delegate genuinely independent, sizeable tracks (wide multi-file
|
|
||||||
exploration, parallel audits) — not work doable in a few tool
|
|
||||||
calls, and not self-verification (harness gates own that). Brief
|
|
||||||
precisely, then commit to the delegation — don't redo its work.
|
|
||||||
```
|
|
||||||
Rationale: E1+E2. No hard spawn cap in prose (harness already injects one on
|
|
||||||
Opus 5; Fable benefits from delegation).
|
|
||||||
|
|
||||||
### W2 — CLAUDE.global.md: reframe "After code changes" (:75-83)
|
|
||||||
Keep the concrete quality bar; drop the proof-mandate/self-check phrasing
|
|
||||||
(E3). Replace steps 2-4 with faithful-outcome reporting:
|
|
||||||
|
|
||||||
```
|
|
||||||
## After code changes
|
|
||||||
1. Run tests, lint, build, type-check if available.
|
|
||||||
2. Report outcomes faithfully: what passed, what wasn't run,
|
|
||||||
remaining risks, surviving deviations. Completion claims only
|
|
||||||
for verified work.
|
|
||||||
3. Correction or notable event → capitalize to right registry.
|
|
||||||
```
|
|
||||||
Net: -2 lines. "Would staff engineer approve?" bar and "Don't mark complete
|
|
||||||
without proof" are removed as self-check choreography; honest-reporting
|
|
||||||
line preserves the intent (grounded completion claims) without mandating an
|
|
||||||
extra verification pass.
|
|
||||||
|
|
||||||
### W3 — CLAUDE.global.md: add scope fence (Workflow section)
|
|
||||||
Append (adapted from Anthropic's tested block, caveman-compressed, ~5 lines):
|
|
||||||
|
|
||||||
```
|
|
||||||
- Scope: deliver what was asked, at the scope intended. Routine
|
|
||||||
judgment calls → decide alone; materially different readings →
|
|
||||||
ask. Better approach spotted → say so in one line, still do the
|
|
||||||
task as asked. Finish the whole task; genuinely blocked → do the
|
|
||||||
rest, state plainly what's missing.
|
|
||||||
```
|
|
||||||
Rationale: E4. Complements existing "Scope changes to task — no unrelated
|
|
||||||
edits" (line ~15) without contradicting it.
|
|
||||||
|
|
||||||
### W4 — CLAUDE.global.md: add deliverable-length rule (Code style / Comments area)
|
|
||||||
~2 lines:
|
|
||||||
|
|
||||||
```
|
|
||||||
- Written deliverables (docs, reports, .md): length matched to what
|
|
||||||
the task needs — no filler sections, no boilerplate summaries.
|
|
||||||
```
|
|
||||||
Rationale: E6. Registries already covered by caveman rule.
|
|
||||||
|
|
||||||
### W5 — Line budget
|
|
||||||
After W1-W4: expected ~309 lines. Hard check: `wc -l CLAUDE.global.md` ≤ 320
|
|
||||||
(session-start.sh warning threshold at :202-213).
|
|
||||||
|
|
||||||
### W6 — hooks/design-toolchain-reminder.sh: drop `\bui\b` and `\bux\b`
|
|
||||||
- Remove the two 2-char alternatives from the pattern at :47. Keep
|
|
||||||
`ui/ux|ux/ui|ui kit` and all other tokens.
|
|
||||||
- Add a dated header comment (3rd tightening pass, 2026-07-30, cites the
|
|
||||||
two French-prose `ux` FPs; series LRN-1005/1007).
|
|
||||||
- Trade-off accepted: a bare "améliore l'ux" prompt with no other design
|
|
||||||
token goes quiet — the CLAUDE.global.md "Design work" section still
|
|
||||||
routes it (the hook is a belt, self-described soft nudge).
|
|
||||||
- Update `lib/tests/design-toolchain-reminder.test.sh`: add 2 quiet rows
|
|
||||||
(the real FP prompt excerpt; a bare "l'ui" French sentence) — flip-tested
|
|
||||||
per LRN-096. Existing 9 must-fire rows unaffected (none uses ui/ux).
|
|
||||||
|
|
||||||
### W7 — agents/plan-challenger.md: coverage-first reporting line
|
|
||||||
Add one clause to the findings rules (add-only, no removal): uncertain or
|
|
||||||
low-severity findings are REPORTED with an explicit confidence + severity
|
|
||||||
tag rather than self-censored — severity filtering happens in the
|
|
||||||
orchestrator's synthesis, not in the challenger. Rationale: E5 (literal
|
|
||||||
Opus 5 + "manufactured concern is a failure" wording risks suppressing real
|
|
||||||
low-confidence findings). Must not touch: verdict grammar, MANDATORY PROOF
|
|
||||||
clause, blind-dispatch rules (test-locked in plan-challenger.test.sh).
|
|
||||||
|
|
||||||
### W8 — Memory + docs capitalization (same branch, follows the work)
|
|
||||||
- decisions.md: new BDR (config adapted for Claude 5 family — scope,
|
|
||||||
rationale, alternatives incl. "leave config as-is" and "hard spawn caps"
|
|
||||||
rejected).
|
|
||||||
- learnings.md: new LRN — Opus 5 behavioral profile (over-delegation
|
|
||||||
inverts LRN-030's Opus 4.8 trait; over-verification; literal following;
|
|
||||||
no effort hold on Opus 5 in Claude Code; heron_brook/#80988 injection).
|
|
||||||
- journal.md: one line.
|
|
||||||
- CHANGELOG.md: entry under Unreleased.
|
|
||||||
|
|
||||||
### W9 — Gates (before commit)
|
|
||||||
- `shellcheck hooks/design-toolchain-reminder.sh` clean.
|
|
||||||
- Manual flip-test of the hook: FP prompt → quiet; "redesign the navbar" →
|
|
||||||
fires.
|
|
||||||
- `make test` full suite green (design-toolchain-reminder.test.sh,
|
|
||||||
plan-challenger.test.sh, model-routing.test.sh untouched-but-must-pass,
|
|
||||||
curated-config-guard, loops-light…).
|
|
||||||
- `wc -l CLAUDE.global.md` ≤ 320.
|
|
||||||
|
|
||||||
### W10 — Gitflow
|
|
||||||
`bash ~/.claude/lib/gitflow.sh start feature opus5-config-tuning` off
|
|
||||||
develop; atomic commits (hook+test / CLAUDE.global.md / agent / memory+docs);
|
|
||||||
NO `gitflow finish` — merge only on explicit human signal.
|
|
||||||
|
|
||||||
## 4. Explicitly NOT doing (considered, rejected)
|
|
||||||
|
|
||||||
- N1 Touching lib/verify-secure-loop.md or the fresh-verifier/security
|
|
||||||
gates: harness architecture (BDR-049/050, D2), verifies SONNET executor
|
|
||||||
output — not Opus 5 self-check prose.
|
|
||||||
- N2 Softening the Security / Architecture sections (BDR-021, D3).
|
|
||||||
- N3 Editing the superpowers plugin's "1% chance → MUST invoke" language:
|
|
||||||
external upstream code; flagged as residual over-triggering risk in the
|
|
||||||
new LRN, revisit as its own decision if observed.
|
|
||||||
- N4 Changing `settings.json` `effortLevel: "xhigh"`: user preference,
|
|
||||||
optimal for the Fable 5 session default; the Opus 5 carry-over trap (E9)
|
|
||||||
is documented in the LRN + surfaced to the user for a manual decision.
|
|
||||||
- N5 De-prescribing seo-analyzer.md / geo-analyzer.md (1528/1106 lines,
|
|
||||||
heavy MUST density): separate project, backlog note in TODO.md.
|
|
||||||
- N6 Removing or session-gating the design/ctx7 reminder hooks: soft
|
|
||||||
nudges, cheap, deliberately built; tightened only (W6).
|
|
||||||
- N7 Any model pin change: `model: opus` pins now resolve to Opus 5 —
|
|
||||||
desired outcome, census (model-routing.test.sh) untouched.
|
|
||||||
- N8 Committing settings.json for any reason (LRN-098/1049 /model-churn
|
|
||||||
trap): file is currently clean; keep it out of every commit.
|
|
||||||
|
|
||||||
## 5bis. CHALLENGE SYNTHESIS (2026-07-30) — FINAL amendments (v2)
|
|
||||||
|
|
||||||
Verdicts: correctness CONCERNS(4) · robustness FATAL(5, 1 BLOCKER) ·
|
|
||||||
simplicity CONCERNS(4). Every fix below is the challenger's own named FIX,
|
|
||||||
adopted as written. No re-challenge pass: scope narrowed, no new dependency;
|
|
||||||
W0 is an execution-time safety procedure, not a new config mechanism.
|
|
||||||
|
|
||||||
- **W0 (NEW — robustness BLOCKER)**: all edited surfaces are symlink-deployed
|
|
||||||
LIVE (~/.claude/CLAUDE.md, hooks/, agents/ → this repo); edits take effect
|
|
||||||
machine-wide at save time, before any W9 gate. Mitigations:
|
|
||||||
(a) `gitflow start` BEFORE any live-file edit; never checkout develop
|
|
||||||
mid-work; (b) hook regex change validated on a SCRATCH copy first
|
|
||||||
(bash -n + shellcheck + pattern replay), then written to the live file in
|
|
||||||
ONE atomic Edit; (c) named reverts: `git show develop:<file> > <file>`;
|
|
||||||
escape hatch = remove the hook registration block from settings.json.
|
|
||||||
- **W1 v2** (robustness#3, correctness#2): replacement text carves out the
|
|
||||||
mandated gates explicitly and scopes "don't redo":
|
|
||||||
"Skill-mandated gates (fresh verifier/security/challenge) always dispatch
|
|
||||||
as written. Don't redo delegated work by hand — failed gates re-dispatch
|
|
||||||
fresh executors instead."
|
|
||||||
- **W2 v2** (simplicity#2): minimal diff — delete ONLY the line
|
|
||||||
`Bar: "would staff engineer approve?"`. Steps 1-4 + capitalize step stay.
|
|
||||||
- **W3 v2** (simplicity#1, robustness#4): no new bullet. Fold the only new
|
|
||||||
clause into the existing Deviations bullet: "Finish the whole task:
|
|
||||||
blocked on an independent sub-part → do the rest, state what's missing.
|
|
||||||
Gone WRONG → still STOP, re-plan." (net +2 lines, no conflict with :53).
|
|
||||||
- **W4**: unchanged (+2 lines). Budget v2: 304 +1 −1 +2 +2 = 308 ≤ 320.
|
|
||||||
- **W6 v2** (all lenses): drop `\bux\b` ONLY — keep `\bui\b` (zero evidenced
|
|
||||||
FP; one logged true positive). Accepted trade-off: the 2026-07-21 "ameliore
|
|
||||||
le tutoriel…gamifier" ux row (plausible TP) goes quiet; CLAUDE.global.md
|
|
||||||
design-routing section remains the router. Header comment notes the log
|
|
||||||
records `head -1` only → per-token FP rate not fully derivable. Tests:
|
|
||||||
quiet row = synthetic "changement ux vu…" (verified matches pre-change →
|
|
||||||
flips); must-fire row = "revois l'ui du panneau admin" (locks `\bui\b`;
|
|
||||||
apostrophe escaped correctly, doubles as JSON-path control per
|
|
||||||
robustness#7). No log-excerpt rows (vacuous — 100-char truncation).
|
|
||||||
- **W7 v2** (all lenses): in-place reword of the `:82-83` sentence (NOT
|
|
||||||
test-locked; plan v1 misstated that) instead of an add-only clause:
|
|
||||||
"No invention — ungrounded is noise. Silently dropping a grounded doubt is
|
|
||||||
equally a failure: file it as `[MINOR]` with the uncertainty stated in
|
|
||||||
`WHY:`. Nothing real at all → `SOLID` with `FINDINGS: none`."
|
|
||||||
OUTPUT grammar byte-identical; no confidence axis; no consumer change.
|
|
||||||
Census: add `has "$A" "grounded doubt"` row to plan-challenger.test.sh in
|
|
||||||
the same commit.
|
|
||||||
- **W9 v2**: adds the W0 scratch-validation step; rest unchanged.
|
|
||||||
- **W10 v2**: branch creation moves FIRST in execution order.
|
|
||||||
|
|
||||||
## 5. Constraints for challengers
|
|
||||||
|
|
||||||
- Registries append-only; curation only via /prune-memory.
|
|
||||||
- Census tests lock behavior: any hook/agent edit must land with its test
|
|
||||||
update in the same commit; `make test` must stay green.
|
|
||||||
- CLAUDE.global.md ≤ 320 lines (runtime warning threshold).
|
|
||||||
- BDR-021: Security + Architecture sections verbatim.
|
|
||||||
- Gitflow: feature branch off develop, no merge without human signal.
|
|
||||||
- The global file serves ALL models (Fable sessions, Opus 5 dispatches,
|
|
||||||
Sonnet executors read skill/agent prompts instead) — no Opus-5-only
|
|
||||||
wording in CLAUDE.global.md.
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -6,71 +6,6 @@ Format follows [Keep a Changelog](https://keepachangelog.com/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Changed
|
|
||||||
- **Global instruction layer recalibrated for the Claude 5 family (BDR-081)** —
|
|
||||||
delegation block is now model-neutral when-guidance (the Opus 4.8
|
|
||||||
under-delegation counter inverted on Opus 5, which over-delegates and gets
|
|
||||||
an injected harness cap); "staff engineer" self-check bar dropped (Opus 5
|
|
||||||
over-verification trigger); finish-whole-task clause added to Deviations;
|
|
||||||
written-deliverable length rule added. 308/320 lines.
|
|
||||||
- **design-toolchain hook** — dropped `\bux\b` (2 French-prose false
|
|
||||||
positives; 3rd tightening pass, series LRN-1005/1007); `\bui\b` kept and
|
|
||||||
locked by a must-fire test row.
|
|
||||||
- **plan-challenger** — grounded-but-uncertain findings now file as `[MINOR]`
|
|
||||||
with the uncertainty stated, instead of being self-censored (Opus 5 follows
|
|
||||||
conservative-reporting clauses literally).
|
|
||||||
|
|
||||||
## [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
|
## [1.2.1] — 2026-07-20
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
+7
-14
@@ -22,8 +22,6 @@ Apply unless repo-specific instructions override.
|
|||||||
- Document intent, not mechanics. Use project doc style (docstring, JSDoc…).
|
- Document intent, not mechanics. Use project doc style (docstring, JSDoc…).
|
||||||
- Explicit, consistent, meaningful names. Straight control flow,
|
- Explicit, consistent, meaningful names. Straight control flow,
|
||||||
no hidden side effects.
|
no hidden side effects.
|
||||||
- Written deliverables (docs, reports, .md): length matched to what
|
|
||||||
the task needs — no filler sections, no boilerplate summaries.
|
|
||||||
|
|
||||||
## Refactoring
|
## Refactoring
|
||||||
- Priority: safety → readability → consistency.
|
- Priority: safety → readability → consistency.
|
||||||
@@ -42,12 +40,11 @@ Apply unless repo-specific instructions override.
|
|||||||
- Confirm before implementing only when real trade-offs exist (multiple
|
- Confirm before implementing only when real trade-offs exist (multiple
|
||||||
valid approaches, breaking change, destructive action) — else proceed.
|
valid approaches, breaking change, destructive action) — else proceed.
|
||||||
- Minimal changes unless broader refactor requested. State trade-offs.
|
- Minimal changes unless broader refactor requested. State trade-offs.
|
||||||
- Sub-agents: one task per sub-agent, main context stays clean.
|
- Sub-agents keep main context clean — one task per sub-agent.
|
||||||
Delegate genuinely independent, sizeable tracks (wide multi-file
|
More compute on hard problems. Task fans out across independent
|
||||||
exploration, parallel audits) — not work doable in a few tool
|
items (many files, parallel searches, multi-point checks) → delegate
|
||||||
calls. Skill-mandated gates (fresh verifier/security/challenge)
|
to sub-agents, don't iterate serially. Default to delegation for
|
||||||
always dispatch as written. Don't redo delegated work by hand —
|
multi-file exploration. Counters model tendency to under-delegate.
|
||||||
failed gates re-dispatch fresh executors instead.
|
|
||||||
- One question upfront if needed — don't interrupt mid-task.
|
- One question upfront if needed — don't interrupt mid-task.
|
||||||
*Exception: skill-mandated gates and checkpoints (orchestrator
|
*Exception: skill-mandated gates and checkpoints (orchestrator
|
||||||
validation gates, approval gates, darwin checkpoints) always fire.*
|
validation gates, approval gates, darwin checkpoints) always fire.*
|
||||||
@@ -56,8 +53,6 @@ Apply unless repo-specific instructions override.
|
|||||||
- Something goes wrong → STOP, re-plan. Never push through.
|
- Something goes wrong → STOP, re-plan. Never push through.
|
||||||
- Deviations: minor or clearly justified → do, explain after.
|
- Deviations: minor or clearly justified → do, explain after.
|
||||||
Significant or shaky justification → ask before deviating.
|
Significant or shaky justification → ask before deviating.
|
||||||
Finish the whole task: blocked on an independent sub-part → do
|
|
||||||
the rest, state what's missing. Gone WRONG → still STOP, re-plan.
|
|
||||||
- Root causes only. No temp fixes. Never assume — verify paths, APIs,
|
- Root causes only. No temp fixes. Never assume — verify paths, APIs,
|
||||||
variables before use.
|
variables before use.
|
||||||
|
|
||||||
@@ -82,6 +77,7 @@ Apply unless repo-specific instructions override.
|
|||||||
2. Report what verified, what not.
|
2. Report what verified, what not.
|
||||||
3. List remaining risks, surviving deviations.
|
3. List remaining risks, surviving deviations.
|
||||||
4. Don't mark complete without proof it works.
|
4. Don't mark complete without proof it works.
|
||||||
|
Bar: "would staff engineer approve?"
|
||||||
5. Correction or notable event → capitalize to right registry
|
5. Correction or notable event → capitalize to right registry
|
||||||
(see "Memory registries").
|
(see "Memory registries").
|
||||||
|
|
||||||
@@ -256,10 +252,7 @@ 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 → bugfix (full framework: gitflow, contract, fresh
|
- Bug / error / 500 → investigate (bugfix if gstack off)
|
||||||
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
|
||||||
|
|||||||
@@ -32,13 +32,8 @@ 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 (the SDD worktree + reviewers read them
|
They are committed DURING the run and DELETED in the post-merge cleanup
|
||||||
from disk — NOT gitignored), then AUTO-PURGED by `gitflow finish` on a
|
(BDR-065) — git history at the feature commits is their archive. Durable
|
||||||
`feature`/`bugfix` branch, before the merge, so develop's tip stays clean
|
knowledge goes to `.claude/memory/` registries, never to these files.
|
||||||
(BDR-065, `lib/gitflow.sh` `_gitflow_purge_transient`). The feature commits
|
Derived scan/audit outputs (`.audit/**`) are gitignored and never
|
||||||
stay reachable from develop, so `git show <sha>:docs/…` is still the archive.
|
committed, even redacted (LRN-124).
|
||||||
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).
|
|
||||||
|
|||||||
@@ -1,67 +1,43 @@
|
|||||||
# claude-config
|
# claude-config
|
||||||
|
|
||||||
One repo that turns Claude Code into a reproducible engineering system —
|
Global Claude Code configuration — agents, skills, plugins, and project templates.
|
||||||
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.
|
|
||||||
|
|
||||||
## What it is
|
> **Guide d'utilisation complet :** voir [`USAGE.md`](./USAGE.md) — workflows typiques, exemples par type de projet, arbre de décision "quel skill utiliser ?".
|
||||||
|
> **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.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Everything below is the reference manual — model routing, components,
|
## Overview
|
||||||
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 + 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 principle:**
|
||||||
|
- `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.
|
||||||
|
|
||||||
|
### Agent model routing (BDR-076/077 — model-tiering v2)
|
||||||
|
|
||||||
Doctrine: the session model (Fable) does main-loop reflection ONLY —
|
Doctrine: the session model (Fable) does main-loop reflection ONLY —
|
||||||
brainstorm, plan, contract, audit judgment, gates, loop decisions — enforced
|
brainstorm, plan, contract, audit judgment, gates, loop decisions — enforced
|
||||||
@@ -93,18 +69,32 @@ children are dispatched `model:"fable"` (they carry reflection).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Install notes
|
## Fresh install (new machine)
|
||||||
|
|
||||||
|
```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. The doc-fetch surface is
|
step installs the `ctx7` CLI and wires it into Claude Code. The doc-fetch surface is
|
||||||
the `find-docs` skill alone (the generated `rules/context7.md` is purged by
|
the `find-docs` skill alone (BDR-053 — the generated `rules/context7.md` is purged by
|
||||||
design; 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
|
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
|
carries fast-moving libs (`lib/fast-libs.sh`) — a scoped second surface refining
|
||||||
refinement of the single-surface rule, not a reversal.
|
BDR-053, not reversing it (BDR-078).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ctx7 login # optional: OAuth / API key for higher rate limits
|
ctx7 login # optional: OAuth / API key for higher rate limits
|
||||||
@@ -170,7 +160,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 (web / seo / web-full / full / backend / design / dev / qa / audit / minimal) |
|
| `/profile` | Activate a skill profile (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,
|
||||||
@@ -246,15 +236,17 @@ 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).
|
bit us once: job7/BDR-026).
|
||||||
|
|
||||||
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
|
||||||
MAGIC_API_KEY=<Enter your magic api key here from https://21st.dev/settings/api-keys >
|
# WRONG — plaintext key lands in ~/.claude.json:
|
||||||
# single-quoted so bash doesn't expand it; Claude Code expands it at
|
claude mcp add magic --scope user --env API_KEY="$MAGIC_API_KEY" -- npx -y @21st-dev/magic@latest
|
||||||
|
|
||||||
|
# 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
|
||||||
```
|
```
|
||||||
@@ -272,26 +264,6 @@ 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
|
||||||
@@ -301,7 +273,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,
|
`permissions.ask` explicitly lists all 4 `mcp__magic__*` tools ([[BDR-059]]),
|
||||||
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
|
||||||
@@ -327,10 +299,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 + lib/tests/run-*.sh)
|
make test # run deterministic tests (lib/tests/*.test.sh + lib/seo-data/*.test.sh + lib/gitflow-test.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 (web/seo/web-full/full/backend/design/dev/qa/audit/minimal)
|
make profile cmd="set X" # activate a skill profile (design/dev/qa/audit/minimal/full)
|
||||||
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
|
||||||
@@ -338,11 +310,3 @@ 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.
|
|
||||||
|
|||||||
@@ -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 | web / seo / web-full / full / backend / design / dev / qa / audit / minimal |
|
| `/profile` | Changer le profil de skills | 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 —
|
||||||
|
|||||||
@@ -79,9 +79,8 @@ PROOF: read <n> files, inspected <what>, checked plan §<…>
|
|||||||
|
|
||||||
- Report-only. Never edit, write, or implement — naming the flaw precisely is
|
- Report-only. Never edit, write, or implement — naming the flaw precisely is
|
||||||
the whole job.
|
the whole job.
|
||||||
- No invention — ungrounded is noise. Silently dropping a grounded doubt is
|
- No invention. If your lens finds nothing real, return `SOLID` with
|
||||||
equally a failure: file it as `[MINOR]` with the uncertainty stated in
|
`FINDINGS: none` — a manufactured concern is a failure, not diligence.
|
||||||
`WHY:`. Nothing real at all → `SOLID` with `FINDINGS: none`.
|
|
||||||
- `PROOF` is MANDATORY. A verdict without a `PROOF` line is a structural failure
|
- `PROOF` is MANDATORY. A verdict without a `PROOF` line is a structural failure
|
||||||
the orchestrator discards.
|
the orchestrator discards.
|
||||||
- Stay in your lens. A finding outside it belongs to another challenger.
|
- Stay in your lens. A finding outside it belongs to another challenger.
|
||||||
|
|||||||
@@ -0,0 +1,385 @@
|
|||||||
|
# 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)**.
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
# 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
@@ -0,0 +1,143 @@
|
|||||||
|
# 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.
|
||||||
@@ -44,11 +44,7 @@ lc="$(printf '%s' "$prompt" | tr '[:upper:]' '[:lower:]')"
|
|||||||
# "design system", "redesign", "front-?end design". dashboard -> \bdashboard\b
|
# "design system", "redesign", "front-?end design". dashboard -> \bdashboard\b
|
||||||
# so a filename like ecc_dashboard.py no longer matches while "admin dashboard"
|
# so a filename like ecc_dashboard.py no longer matches while "admin dashboard"
|
||||||
# still does. animation kept (rarely non-UI).
|
# still does. animation kept (rarely non-UI).
|
||||||
# Tightened 2026-07-30 (3rd pass): dropped \bux\b — bare "ux" matched inside
|
pattern='redesign|refonte|refont|ui/ux|ux/ui|\bui\b|\bux\b|ui kit|design system|design-system|front-?end design|\bnavbar\b|\bsidebar\b|\bmodal\b|\bbouton\b|\bbutton\b|formulaire|\bhero\b|\bheader\b|\bfooter\b|dropdown|tooltip|\bbadge\b|\bchart\b|graphique|accordion|carousel|\bslider\b|landing|\bdashboard\b|homepage|home page|\baccueil\b|\bécran\b|\becran\b|portfolio|maquette|mockup|wireframe|prototype|\bjoli\b|\bjolie\b|\bbeau\b|\bbelle\b|esth[eé]tique|aesthetic|\bvisuel\b|\bvisual\b|embellir|fignol|peaufin|polish|styliser|styling|stylesheet|\bskin\b|charte graphique|\bbrand\b|branding|\blogo\b|favicon|ic[oô]ne|\bicon\b|\bcss\b|tailwind|shadcn|couleur|gradient|d[eé]grad[eé]|\bombre\b|spacing|espacement|\bmarge\b|\bpadding\b|\bmargin\b|\bradius\b|arrondi|\bhover\b|dark mode|light mode|typograph|\bfont\b|\bfonts\b|font pairing|\bpolice\b|animation|\bmotion\b|micro-interaction|keyframe|glassmorph|neumorph|claymorph|skeuomorph|brutalis|bento|minimalis|responsive|figma'
|
||||||
# French prose ("changement ux vu…"; 2 logged FPs, both FR). \bui\b KEPT
|
|
||||||
# (zero logged FP, one logged true positive). NB: the log records only the
|
|
||||||
# FIRST match per fire (head -1), so per-token FP rates aren't derivable.
|
|
||||||
pattern='redesign|refonte|refont|ui/ux|ux/ui|\bui\b|ui kit|design system|design-system|front-?end design|\bnavbar\b|\bsidebar\b|\bmodal\b|\bbouton\b|\bbutton\b|formulaire|\bhero\b|\bheader\b|\bfooter\b|dropdown|tooltip|\bbadge\b|\bchart\b|graphique|accordion|carousel|\bslider\b|landing|\bdashboard\b|homepage|home page|\baccueil\b|\bécran\b|\becran\b|portfolio|maquette|mockup|wireframe|prototype|\bjoli\b|\bjolie\b|\bbeau\b|\bbelle\b|esth[eé]tique|aesthetic|\bvisuel\b|\bvisual\b|embellir|fignol|peaufin|polish|styliser|styling|stylesheet|\bskin\b|charte graphique|\bbrand\b|branding|\blogo\b|favicon|ic[oô]ne|\bicon\b|\bcss\b|tailwind|shadcn|couleur|gradient|d[eé]grad[eé]|\bombre\b|spacing|espacement|\bmarge\b|\bpadding\b|\bmargin\b|\bradius\b|arrondi|\bhover\b|dark mode|light mode|typograph|\bfont\b|\bfonts\b|font pairing|\bpolice\b|animation|\bmotion\b|micro-interaction|keyframe|glassmorph|neumorph|claymorph|skeuomorph|brutalis|bento|minimalis|responsive|figma'
|
|
||||||
|
|
||||||
if printf '%s' "$lc" | grep -Eq "$pattern"; then
|
if printf '%s' "$lc" | grep -Eq "$pattern"; then
|
||||||
# Counter: log the fire (time, matched token, excerpt) — best-effort, never blocks.
|
# Counter: log the fire (time, matched token, excerpt) — best-effort, never blocks.
|
||||||
|
|||||||
@@ -239,7 +239,6 @@ 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'
|
||||||
@@ -253,57 +252,10 @@ 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 ]
|
||||||
|
|||||||
+2
-48
@@ -18,12 +18,6 @@ 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 ────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -103,42 +97,6 @@ _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).
|
||||||
#
|
#
|
||||||
@@ -159,10 +117,7 @@ gitflow_finish() {
|
|||||||
fi
|
fi
|
||||||
type="$(gitflow_branch_type "$br")"
|
type="$(gitflow_branch_type "$br")"
|
||||||
case "$type" in
|
case "$type" in
|
||||||
feature|bugfix)
|
feature|bugfix|chore)
|
||||||
_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" \
|
||||||
@@ -328,9 +283,8 @@ 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|purge-transient|install-hook|emit-hook}" >&2; exit 2 ;;
|
*) echo "usage: gitflow.sh {type|protected-base|base-for|release-open|start|finish|init|reconcile|install-hook|emit-hook}" >&2; exit 2 ;;
|
||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|||||||
+15
-80
@@ -14,9 +14,6 @@
|
|||||||
# - 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
|
||||||
@@ -64,23 +61,6 @@ 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.)
|
||||||
@@ -291,11 +271,6 @@ 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
|
||||||
@@ -447,48 +422,6 @@ 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() {
|
||||||
@@ -573,20 +506,24 @@ cmd_apply() {
|
|||||||
|
|
||||||
cmd_set() {
|
cmd_set() {
|
||||||
local prof="$1"
|
local prof="$1"
|
||||||
info "Setting profile: $prof (exclusive — disables non-listed gstack skills + managed plugins/externals/MCPs)"
|
info "Setting profile: $prof (exclusive — disables non-listed gstack skills + managed plugins)"
|
||||||
|
|
||||||
# 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).
|
||||||
disable_plugins_not_in "$prof"
|
local plugin_keep_file p plugin_name marketplace
|
||||||
|
plugin_keep_file="$(mktemp)"
|
||||||
# Symmetry (BDR-079): a profile switch also parks the managed external
|
read_profile "$prof" | awk -F'\t' '$2 ~ /^plugin@/ { sub(/^plugin@/, "", $2); print $1"@"$2 }' | sort -u > "$plugin_keep_file"
|
||||||
# packs and unregisters the managed MCPs the new profile does not need —
|
for p in "${MANAGED_PLUGINS[@]}"; do
|
||||||
# design leftovers (emil, magic…) no longer survive a `set backend`.
|
if ! grep -qx "$p" "$plugin_keep_file"; then
|
||||||
disable_externals_not_in "$prof"
|
plugin_name="${p%@*}"
|
||||||
disable_mcps_not_in "$prof"
|
marketplace="${p#*@}"
|
||||||
|
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"
|
||||||
@@ -742,11 +679,9 @@ EXAMPLES:
|
|||||||
bash lib/profile.sh reset # restore everything
|
bash lib/profile.sh reset # restore everything
|
||||||
|
|
||||||
NOTE:
|
NOTE:
|
||||||
"set" toggles the MANAGED items automatically, both ways: plugins
|
Plugin and MCP entries print advisory commands — they are NOT toggled
|
||||||
(ui-ux-pro-max, plugin-dev, pr-review-toolkit), external packs
|
automatically. Run "claude plugin enable|disable" or "claude mcp add|remove"
|
||||||
(emil-design-eng, frontend-design, design-motion-principles, impeccable)
|
yourself for those.
|
||||||
and the magic MCP. Anything outside those allowlists stays advisory —
|
|
||||||
run "claude plugin enable|disable" or "claude mcp add|remove" yourself.
|
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ check D8-dash-file "$(fire 'ecc_dashboard.py')" quiet
|
|||||||
# --- Harness-generated inputs must be QUIET even with UI tokens ---
|
# --- Harness-generated inputs must be QUIET even with UI tokens ---
|
||||||
check D9-tasknotif "$(fire '<task-notification> <task-id>x</task-id> add css header fonts')" quiet
|
check D9-tasknotif "$(fire '<task-notification> <task-id>x</task-id> add css header fonts')" quiet
|
||||||
check D10-notif-file "$(fire '<task-notification> design-motion-principles keyframe done')" quiet
|
check D10-notif-file "$(fire '<task-notification> design-motion-principles keyframe done')" quiet
|
||||||
check D11-bare-ux "$(fire 'changement ux vu de tes trouvailles')" quiet
|
|
||||||
|
|
||||||
# --- Real UI signals must still FIRE ---
|
# --- Real UI signals must still FIRE ---
|
||||||
check F1-button "$(fire 'add a button')" fire
|
check F1-button "$(fire 'add a button')" fire
|
||||||
@@ -34,7 +33,6 @@ check F6-frontdesign "$(fire 'frontend design work')" fire
|
|||||||
check F7-admin-dash "$(fire 'admin dashboard screen')" fire
|
check F7-admin-dash "$(fire 'admin dashboard screen')" fire
|
||||||
check F8-animation "$(fire 'add an animation')" fire
|
check F8-animation "$(fire 'add an animation')" fire
|
||||||
check F9-designsys "$(fire 'our design system')" fire
|
check F9-designsys "$(fire 'our design system')" fire
|
||||||
check F10-bare-ui "$(fire 'revois l'\''ui du panneau admin')" fire
|
|
||||||
|
|
||||||
# --- Fire is logged (time + token + excerpt) ---
|
# --- Fire is logged (time + token + excerpt) ---
|
||||||
tmp="$(mktemp -d)"
|
tmp="$(mktemp -d)"
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ has "$A" "correctness"
|
|||||||
has "$A" "robustness"
|
has "$A" "robustness"
|
||||||
has "$A" "simplicity"
|
has "$A" "simplicity"
|
||||||
has "$A" "Report-only"
|
has "$A" "Report-only"
|
||||||
has "$A" "grounded doubt" # uncertain findings → [MINOR], not self-censored (Opus 5 literalism)
|
|
||||||
|
|
||||||
# 2) reusable phase — the mechanism lives here (one canonical include)
|
# 2) reusable phase — the mechanism lives here (one canonical include)
|
||||||
has "$L" 'subagent_type="plan-challenger"'
|
has "$L" 'subagent_type="plan-challenger"'
|
||||||
|
|||||||
@@ -1,76 +0,0 @@
|
|||||||
#!/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 ]
|
|
||||||
+3
-15
@@ -61,15 +61,6 @@ 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
|
||||||
|
|
||||||
@@ -126,11 +117,8 @@ 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), the managed external packs (emil-design-eng,
|
plugin-dev, pr-review-toolkit) and the `magic` MCP — see the Mechanism table
|
||||||
frontend-design, design-motion-principles, impeccable) and the `magic` MCP —
|
above (BDR-008). Anything outside that managed set stays manual:
|
||||||
in BOTH directions: `set` enables what the profile lists and disables the
|
`claude plugin enable|disable`, `claude mcp add|remove`.
|
||||||
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.
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
1.4.0
|
1.2.1
|
||||||
|
|||||||
Reference in New Issue
Block a user