Commit Graph
63 Commits
Author SHA1 Message Date
Bastien ChanotandClaude Opus 4.8 3d0ee262c1 fix(hooks): tighten design-toolchain trigger + add fire-log counter
The 07-02 tightening left bare tokens common in non-UI talk (design, component, theme, transition, frontend, palette) -> ~6 false-fires/session during the ECC config audit. Dropped them; dashboard now word-boundary matched (kills the ecc_dashboard.py filename match, keeps 'admin dashboard'); kept animation; added 'front-end design' bigram. Each fire now logs time+token+excerpt to a light file so 're-firing?' is measured, not argued. Regression test 18/18, shellcheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpphkdTosUzokBDNG7PToS
2026-07-03 15:52:37 +02:00
Bastien ChanotandClaude Opus 4.8 55347445cc feat(hooks): config-protection PreToolUse guards quality-gate files
Blocks Edit/Write to guardrails (settings.json + .claude/settings*, lib/gitflow.sh, .githooks/*, doctor.sh, hooks/*.sh self-guard, lib/tests/*, lint) so a gate can't be weakened to pass an error. Bypass = one-shot sentinel .claude/.config-edit-ok (non-empty reason, logged+consumed), not an env-var. Adaptation from the ECC second-look (BDR-047 corrob): own bash idiom, not ECC's Node dispatcher. shellcheck clean, test 20/20.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpphkdTosUzokBDNG7PToS
2026-07-03 15:29:20 +02:00
Bastien ChanotandClaude Opus 4.8 d9fdd4cbdf fix(gitflow): gitflow_finish validates its named branch against HEAD
gitflow_finish ignored its <type> <name> args and always merged the
checked-out branch — `finish bugfix audit-bugs` run from
feature/audit-tokens merged the wrong branch (audit UX trap, 2026-07-02).
Args are now an optional safety ASSERTION: if present and != current
branch, refuse loudly (rc 2) instead of merging the wrong thing. No args
= unchanged (the only real caller, SKILL.md:36, passes none). +7 T12
regression assertions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XpphkdTosUzokBDNG7PToS
2026-07-03 13:57:59 +02:00
Bastien Chanot c6660c1c5f Merge bugfix/audit-bugs into develop 2026-07-02 14:35:31 +02:00
Bastien ChanotandClaude Fable 5 56bd5ff0c2 feat(tokens): pr-review-toolkit OFF by default — heaviest plugin, PR-only use
Measured: 6 agent descriptions = ~2.2k tokens injected EVERY session
(the single largest plugin contributor) for a toolkit useful only when
reviewing PRs. enabledPlugins → false; removed from full+backend
profiles (BDR-017 caveat already accepts full excluding rarely-used
items); audit.profile KEEPS it = profile reactivation channel.
Per-PR-session: claude plugin enable pr-review-toolkit@claude-code-plugins
(or bash lib/profile.sh apply audit); a later 'profile set full'
re-disables it via the MANAGED_PLUGINS lifecycle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:17:55 +02:00
Bastien ChanotandClaude Fable 5 17fb6dda43 fix(tests): run-reconcile T6c — oracle pointed at the removed parasite dir
$MEM/../skills resolved to .claude/skills/ (the LRN-042 parasite, removed
2026-06-30 by make plugin Step 8.5), not the real skills/. Green at build
time only because the parasite still existed — green-for-wrong-reason
(LRN-077 class); red ever since. Suite back to 20/20.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:07:31 +02:00
Bastien ChanotandClaude Fable 5 f0b7e89468 fix(rtk): rtk resolution + absolute-path rewrite — compression was silently dead
rtk lives at ~/.cargo/bin but the hand-managed .bashrc lost the cargo
line: command -v failed in hook AND tool shell, so the hook no-op'd with
a stderr warn on every Bash call — input compression silently OFF.

- Resolve RTK_BIN by probing known install dirs (LRN-036 class).
- Substitute the ABSOLUTE path at the rewrite head: a bare 'rtk …'
  exits 127 in the tool shell, whose PATH the hook cannot fix (proven).
- Compound rewrites carrying further bare rtk segments pass through
  unrewritten: quoted text (commit messages) makes a global substitution
  unsafe — lose compression, never emit a command that 127s (proven:
  a commit chain 127'd mid-flow).
- detect_rtk probes the same dirs so the banner reports capability.
- Re-pinned .rtk-hook.sha256: the rtk BINARY verifies the hook against
  it at execution time and refuses a modified hook — the pin is live
  machinery, not a vestige; coupling documented in the header.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016zA3Qh2Q1QpcGXzXxKeDHR
2026-07-02 14:07:22 +02:00
Bastien ChanotandClaude Opus 4.8 e8807a7333 feat(gitflow): chore branch type + aiguillage for standalone memory/doc skills
Standalone /capitalize /close /prune-memory /reconcile no longer lean on the .claude/** hook exemption when run on main/develop: the aiguillage branches them to chore/* off develop before writing. New chore type (base develop, finish->develop) added to the lib; hook unchanged (chore/* non-protected). Closes the leak where standalone memory work (memory IS the work, no code branch to follow) landed direct on a protected base. 64/64 gitflow-test green, shellcheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RNaYKPEkjH1jbgoX1TwKMX
2026-07-01 13:25:36 +02:00
Bastien ChanotandClaude Opus 4.8 d3d6cede65 feat(release-candidate): orchestrator skill over gitflow release + the version tag
/release-candidate cuts a release by orchestrating the existing gitflow
release mechanic (start from develop; finish fan-out main+develop+delete)
and adding the one piece the lib lacks: the version tag.

- skills/release-candidate/SKILL.md: thin orchestrator — preconditions →
  gitflow start release → prep (version.txt + CHANGELOG, breaking doc'd) →
  run-tests gate → human WHEN-to-release gate → gitflow finish → git tag -a
  vX.Y.Z (in the skill, lib untouched) → push (gated).
- lib/tests/run-release-candidate.sh: throwaway-repo flow replay. RC_TAG=0
  reds the tag (gitflow fans out but never tags); RC_TAG=1 → 5/5.
- CLAUDE.md: Skill routing line. CHANGELOG [Unreleased]: /reconcile +
  /release-candidate under Added (so the eventual v4.0.0 captures them).

Tag scheme vX.Y.Z continues the version.txt/CHANGELOG lineage. writing-skills TDD.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C6bUdvHnajCNzgVQefZowj
2026-06-30 14:41:12 +02:00
Bastien ChanotandClaude Opus 4.8 82e6322a9f feat(reconcile): deterministic declared-vs-real engine + thin gated skill
/reconcile confronts declarative sources (TODO checkboxes, registry
statuses, ## Index) against real git/fs state and surfaces the gaps,
in 4 categories + contradiction candidates.

- lib/reconcile.sh: engine — body-only enumeration (never the Index),
  git/fs oracles, BLK last-block-wins status, lexical deferral sweep,
  contradiction candidates, pure reconcile_verdict kernel.
- lib/tests/run-reconcile.sh + fixtures (neutral-named): 20/20;
  recursive-coherence T1 reds if the engine reads the Index (teeth).
- skills/reconcile/SKILL.md: thin orchestration + A/B/C write-back gate,
  honest limits (lexical deferrals, contradictions surfaced not asserted).
- CLAUDE.md: Skill routing line.

Founding principle: never trust a declarative source as an oracle — the
skill practices what it preaches (tested). Built via writing-skills TDD.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C6bUdvHnajCNzgVQefZowj
2026-06-30 13:42:24 +02:00
Bastien ChanotandClaude Opus 4.8 09f5b28d99 feat(doc-sync): MINOR-shape oracle + fail-loud commit (strengthen MINOR gate)
(1) lib/doc-shape.sh — deterministic shape oracle re-checks each LLM-classified
MINOR patch before the silent auto-commit; a shape-suspect patch (added heading
/ oversize / new file / non-doc) escalates to the EXISTING SIGNIFICANT gate.
Genuine MINOR still auto-commits (zero friction, BDR-036 preserved). A structural
floor under the LLM call, not a semantic SIGNIFICANT-detector (BDR-040).

(2) lib/doc-commit.sh — guard the commit itself: a rejected git commit (pre-commit
hook / protected branch / signing) now fails loud with exit 5 + empty stdout,
instead of a false "committed" + the previous HEAD's hash + exit 0 that left docs
silently uncommitted (LRN-071, 3rd occurrence of the swallowed-commit pattern
after LRN-066 and LRN-068/BLK-012).

Wired doc-syncer STEP A4 (whole-set escalation: no=revert all, select=keep subset)
+ doc-commit.md (rc-5 consumer row + ACKNOWLEDGMENTS coherence).
TDD RED->GREEN: run-doc-commit.sh 32/32, run-doc-shape.sh 19/19, behavioral C/D.
shellcheck clean. Branch-guard (3) deferred.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C6bUdvHnajCNzgVQefZowj
2026-06-29 17:41:24 +02:00
Bastien Chanot 167ea9678e feat(gitflow): universal gitflow model — lib + skill + orchestrator wiring
lib core (start/finish/init, transactional bootstrap) + migrate + 57-test suite + aiguillage; skills/gitflow + gitignore template; CLAUDE.md gitflow rule; wiring init-project (5f/8/11), onboard (2.6), ship-feature (0/4/9), feat/bugfix/hotfix aiguillage.
2026-06-29 02:58:13 +02:00
Bastien ChanotandClaude Opus 4.8 79741e36e7 fix(deploy): final-review fixes — NEXT.sh-absence regen, git-ignored fail-loud (rc5), bootstrap gitignore guard, doc polish
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ho5EQCFTSvYamuRtVZpp2d
2026-06-27 18:11:14 +02:00
Bastien ChanotandClaude Opus 4.8 0ed074f4bb fix(deploy): helper polish — pending allowlist, pipefail, no-op guard, --git-dir
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ho5EQCFTSvYamuRtVZpp2d
2026-06-27 17:51:39 +02:00
Bastien ChanotandClaude Opus 4.8 24e6b84add feat(deploy): deploy-commit.sh — allowlist surgical commit for .claude/deploy/
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ho5EQCFTSvYamuRtVZpp2d
2026-06-27 16:55:39 +02:00
Bastien ChanotandClaude Opus 4.8 4af339a312 test(lib): doc-commit behavioral check — coupled + fail-closed scenarios
lib/tests/run-doc-behavioral.md — in-vivo whole-chain check (twin of run-behavioral.md for memory). Scenario A: doc-syncer patches a public doc, the include commits it surgically with dangling code present (coupled + surgical). Scenario B: a forbidden .claude/ path in PATCHED_FILES → helper refuses (rc 4), nothing half-committed, offender named (fail-closed + loud). Complements the 28-assertion deterministic suite (run-doc-commit.sh).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ho5EQCFTSvYamuRtVZpp2d
2026-06-27 03:22:22 +02:00
Bastien ChanotandClaude Opus 4.8 fb1f359da5 feat(doc-syncer): PATCHED_FILES newline output for doc-commit handoff
doc-syncer now emits PATCHED_FILES — every public-doc file created/modified this run, ONE PATH PER LINE — in both STEP 9 OUTPUT (full audit) and AUTO MODE STEP A4 (the path orchestrators call). NONE stays silent (no line → doc-commit sees empty → no-ops). Additive: detection/patching logic and the `auto-mode scope:` input contract are unchanged → callers unaffected.

Separator contract, producer↔consumer aligned + proven: newline is doc-syncer's OUTPUT format (paths carry no newlines); the agent splits on newline and passes EACH path as a SEPARATE argv element to lib/doc-commit.sh. The helper takes argv (no in-band separator) → a path with spaces survives as one argument. lib/doc-commit.md spells this out (never flatten to a space-joined string + re-split, which would mis-split a spaced path the helper then silently drops). New test T7 PROVES it on real git: 'docs/My Guide.md' → committed as one file (28/28, shellcheck clean).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ho5EQCFTSvYamuRtVZpp2d
2026-06-27 01:07:03 +02:00
Bastien ChanotandClaude Opus 4.8 4a54a65dd4 chore(lib): doc-commit include protocol
lib/doc-commit.md — TAIL of the doc-sync step, twin of capitalize-commit.md. Tells an orchestrator how to commit ONLY the docs doc-syncer patched (PATCHED_FILES), surgically, BEFORE FINISH so they reach the merge/PR.

Centerpiece: a report-by-(rc, hash) table covering ALL four exits — rc 0 visible surface (files + agent-composed change summary, NOT a bare count), rc 0/empty no-op, rc 3 unsafe-state manual fallback, and rc 4 SCOPE VIOLATION relayed LOUDLY as an upstream BDR-022 anomaly (name the offender, do not swallow or hand-commit the rest — the refusal IS the alarm).

Attribution locked in 3 places: the rc 0 <one-line> summary comes from the AGENT (in-thread patch context), the helper returns only the hash — otherwise the surface degenerates to the bare count that the gate-replacement was meant to avoid. Two conscious acknowledgments inline: MINOR non-gated-by-choice (surface replaces gate); partial init-project fix linking BLK-010.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ho5EQCFTSvYamuRtVZpp2d
2026-06-27 00:51:42 +02:00
Bastien ChanotandClaude Opus 4.8 ae1f218d3e feat(lib): surgical doc-commit helper + real-exec scope/exclusion tests
New lib/doc-commit.sh: surgical commit of ONLY the public-doc files doc-sync patched (passed as args), twin of memory-commit.sh with INVERSE scope. Δ1 dynamic pathspec filtered to changed paths (LRN-051); Δ2 fail-closed + LOUD scope guard rejecting .claude/** and CLAUDE.md (BDR-022) with a dedicated exit 4; Δ3 no hash anchoring (LRN-052); Δ4 `docs:` message. Hash-only on stdout for `doc_hash=$(...)` capture.

lib/tests/run-doc-commit.sh: 24 assertions, all REALLY EXECUTED on real git fixtures (no presumed behavior). T1a/b/c prove the guard CATCHES — forbidden path alone → exit 4, mixed legit+forbidden → refuse-all (nothing half-committed, offender named); T2 dynamic pathspec no-match filter; T3/T4 dangling + stale-index safety; T5/T6 idempotent + unsafe-state skip. shellcheck clean (both).

Part of the doc-sync coupled chantier (twin of BDR-034). Include + 2 orchestrator reorders follow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ho5EQCFTSvYamuRtVZpp2d
2026-06-27 00:38:18 +02:00
Bastien ChanotandClaude Opus 4.8 67c6a8165d feat(analyze-before-plan): read-before bookend — scan memory (+code) before planning
Shared include lib/analyze-before-plan.md (two-pass on '## <PREFIX>-' headings, disposition-not-reading invariant, guarded no-op). Wired into the dev flows: ship-feature STEP 0d (analyzer code+memory, INPUT INJECTION into brainstorm/plan + STEP 3 reconciliation gate), bugfix STEP 2.5 (blockers-first), feat STEP 0.6 (decisions-first, MINI-PLAN names in-force or states none), hotfix opt-in. analyzer gains a RELATED MEMORY output section pointing at the include (DRY). init-project / onboard no-op by construction (guarded scan on absent/empty registries).

Mirror of the coupled-capitalize write-after (BDR-034): read-before / write-after bookend.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ho5EQCFTSvYamuRtVZpp2d
2026-06-26 23:46:41 +02:00
Bastien ChanotandClaude Opus 4.8 037e14dacc test(lib): behavioral end-to-end check doc for coupled-capitalize
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W9sqAwZxBMZSynZoVrEJhd
2026-06-26 13:27:57 +02:00
Bastien ChanotandClaude Opus 4.8 b44791bb1b chore(lib): capitalize-commit include protocol
Shared snippet referenced by every dev flow's capitalize step (design-gate.md
pattern). Tail of capitalize: commit the already-approved memory via
memory-commit.sh, surgically. Documents the stdout hash contract, the 2-hash
non-confusion (memory commit vs code-commit anchored in entries), the orchestrator
ordering (before FINISH), and what it must NOT do.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W9sqAwZxBMZSynZoVrEJhd
2026-06-26 12:46:51 +02:00
Bastien ChanotandClaude Opus 4.8 bbef41cebf feat(lib): emit memory-commit hash on stdout + T6/T7 (stdout contract, idempotence)
commit_memory now routes diagnostics to stderr and prints ONLY the memory-commit
short hash to stdout, so the capitalize-commit include can report it. Proven:
- T6: commit→hash (matches independent rev-parse), no-op→empty, unsafe→empty+exit3.
- T7: double run creates exactly one commit (real run, not by construction).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W9sqAwZxBMZSynZoVrEJhd
2026-06-26 12:45:06 +02:00
Bastien ChanotandClaude Opus 4.8 58cb91d2b7 feat(lib): surgical memory-commit helper + deterministic scope tests (T1/T2/T2-bis)
Foundation for the coupled-capitalize invariant (Frame 2): commit ONLY
.claude/memory + .claude/tasks, never `git add -A`. Safety lives in the
pathspec because automation removes the human diff review.

Proven on real git behavior, not assumed:
- T1/T2: dangling code (untracked or pre-staged) never embarked.
- T2-bis: `git commit -- pathspec` takes the working tree, not a stale index.
- T3 idempotent, T4 fail-closed on broken state, T5 TODO.md in scope.

_changed_paths filters to paths with real changes: `git commit -- pathspec`
aborts the whole commit on a no-match pathspec (e.g. empty .claude/tasks),
unlike `git add` which tolerates it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W9sqAwZxBMZSynZoVrEJhd
2026-06-26 12:31:46 +02:00
Bastien ChanotandClaude Opus 4.8 11792ccb33 feat(design-gate): §4 anim-lib suggestion — suggest-only, non-blocking, stateless 1-line
Add §4 to lib/design-gate.md: when a non-trivial design task hits a motion
signal (animation/transition/hover/motion/animate, now in §DETECTION) and the
stack is motion-eligible with no anim lib installed, surface a single-line
suggestion for the recommended `motion` package. Suggest-only (install on
explicit consent), non-blocking, stateless (always one line, no marker; calls
the helper, no 3rd copy of the lib list).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W9sqAwZxBMZSynZoVrEJhd
2026-06-26 00:11:01 +02:00
Bastien ChanotandClaude Opus 4.8 e5e673ac1f refactor(skill): rename validate → web-validate
Clearer scoped name for the W3C + WCAG skill. Updated: folder (git mv),
frontmatter name, H1 title, command refs, CLAUDE.md routing, 6 profiles
(functional — activate the skill by folder name), cross-refs in
harden/seo/depth-matrix/client-handover, agent dispatch refs, README +
USAGE tables.

Confidentiality: the client-deliverable leak-guard regex
(client-handover-writer.md) now matches BOTH /web-validate and legacy
/validate, so older client docs stay covered.

Left intentionally: validator-analyzer agent name (lockstep with
subagent_type + registry), .validate-cache/ + VALIDATE.md (audit-file
family {SEO,GEO,HARDEN,CSO,VALIDATE}.md), .claude/ history (append-only),
CHANGELOG old entry (added a new "renamed" entry instead). NL trigger
keywords kept so "validate" still routes here. Third-party html-validate
untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W9sqAwZxBMZSynZoVrEJhd
2026-06-25 01:29:36 +02:00
Bastien ChanotandClaude Opus 4.8 0b92935d6d feat(profile): activate gstack skills on-demand per profile
gstack stays OFF by default (no per-skill symlink in skills/, zero context
cost). enable_skill now gains a gstack branch: a skill absent from skills/
and skills-disabled/ but present in the skills-external/gstack submodule is
symlinked in on demand when a profile lists it; disable_gstack_not_in()
parks it again on an unrelated profile.

This makes `set full` (which lists 35 gstack skills) work without 35 bogus
"missing — try: bash link.sh" warnings, without abandoning the OFF-by-default
policy. The old remedy message was wrong (link.sh never creates gstack
skills) and is replaced with submodule-aware messages.

Refs BDR-030.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0169vjUD1sP9Nx4ZiCa8wvAw
2026-06-24 14:22:36 +02:00
Bastien ChanotandClaude Opus 4.8 f96331844c fix(design-gate): resolve claude on a sanitized PATH (real cause of unknown)
The "unknown -> exit 11" path triggers when `command -v claude` fails. Root
cause is NOT the interactive alias (claude->dtach_claude) not surviving the
subshell — that's true but harmless: the real binary is on the inherited PATH,
so `command -v` finds it in a normal `bash script.sh` (proven: toggle-external
and the gate both resolve claude). The actual lever is PATH carrying the nvm
node bin. A skill/hook that shells the gate out with a sanitized PATH, or a node
upgrade moving the version-pinned nvm path, loses it.

ensure_claude_on_path(): if `command -v claude` already resolves, do nothing;
else probe known install dirs (~/.claude/local, ~/.local/bin, /usr/local/bin)
and the nvm glob, prepending the bin dir — which carries BOTH claude and its
node runtime (claude's shebang needs node, same dir). nvm keeps old versions
after an upgrade, so pick the newest that ships claude via sort -V, not the
first glob match. If nothing resolves, command -v still fails -> unknown ->
exit 11 (fail-visible net stays).

Verified: shellcheck clean; normal PATH -> READY exit 0 (function returns early,
no regression); PATH=/usr/bin:/bin (sanitized hook) -> now resolves claude via
the nvm glob and reports REAL magic state (READY exit 0), where before the fix
it was exit 11 unknown.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 12:12:49 +02:00
Bastien ChanotandClaude Opus 4.8 4d191354c6 fix(design-gate): fail-visible on unverified tools (no silent READY)
When `claude` is unreachable, the plugin/MCP checks (magic, ui-ux-pro-max —
the most important design tools) return `unknown`. The old verdict folded that
into a silent `READY` exit 0 — a fail-OPEN: the gate granted "proceed" exactly
when it had verified nothing. Contradicts the fail-closed-on-uncertainty rule.

Now fail-VISIBLE (not strict fail-closed — claude can be merely slow, false
blocks would get the gate ignored): a third outcome READY BUT UNVERIFIED with a
distinct exit 11. It proceeds, but says so loudly and names the unchecked tools,
telling the user to confirm with `claude mcp list` / `claude plugin list`. The
distinct non-zero code also stops a naive `if gate; then proceed` shell caller
from silently passing. Also covers the non-reproducible "transient claude absent
after apply" flake seen in live testing.

design-gate.md §DECISION updated: exit-code line + a real branch-3 state for 11.

Verified: shellcheck clean; reachable+active -> READY exit 0; claude off PATH ->
READY BUT UNVERIFIED exit 11 naming magic+ui-ux-pro-max; magic off -> INCOMPLETE
exit 10 (trip branch intact after the restructure).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 11:57:48 +02:00
Bastien ChanotandClaude Opus 4.8 131d0bcb5d feat(secrets): .env source-of-truth in ~/.claude + repo symlink
Move the real secret out of the git tree: the key lives in ~/.claude/.env
(outside the repo), and link.sh symlinks repo/.env -> ~/.claude/.env so
`source "$REPO/.env"` resolves transparently. The secret never enters git —
not as content (it's a link) and not by accident (gitignored).

link.sh: add link_env() — verify ~/.claude/.env exists + has MAGIC_API_KEY
(warn, never create/copy the secret), then create repo/.env -> ~/.claude/.env.
Defensive + idempotent: links only when repo/.env is absent or already the
right symlink; a residual REAL repo/.env is left untouched with a migrate hint
(never clobbered, so the secret can't be destroyed).

.gitignore: harden .env -> .env + .env.* + !.env.example (covers .env.local,
.env.bak, .env.save; keeps the template tracked).

Messages point at ~/.claude/.env (the canonical edit location) instead of the
ambiguous $REPO/.env: design-tool-gate.sh gate output, design-gate.md
(branch 3 + IMPORTANT), toggle-external.sh, install-plugins.sh.

Verified: shellcheck clean (link.sh, toggle-external.sh, design-tool-gate.sh);
link.sh created the symlink (1 change, idempotent re-run); repo/.env absent
from git status; magic-off path still exits 10 with the ~/.claude/.env hint.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 11:44:47 +02:00
Bastien ChanotandClaude Opus 4.8 3eefb8ad7c feat(design-gate): profile-based toolchain gate + design-tool-gate.sh
design-tool-gate.sh: deterministic design-toolchain state check. Reads the
design-core tools from design.profile's `# GATE-BLOCK:` allowlist + their
types via `profile.sh show design --plain` (claude-free parse contract),
checks each on its own channel (skill symlink / claude plugin list / claude
mcp list / command -v). Never reads disabledMcpServers. Exit 0 ready · 10
incomplete · 2 error.

Remedy is always a profile (/profile design), never an atomic tool toggle —
the profile system stays the single source of truth for activation. magic is
required-but-manual: it TRIPS the gate (not advisory) and the output names
the MAGIC_API_KEY step. Non-design tools bundled in the profile (browse,
plan-*, design-shotgun, graphify) are excluded from the trip via GATE-BLOCK,
so the gate fires only on real design tools.

design-gate.md: §DECISION rewritten profile-based (tier → run script → branch
on 3 groups), replacing the old atomic "ask user to activate ui-ux-pro-max".
§DETECTION unchanged. design.profile: add the `# GATE-BLOCK:` allowlist
(8 design-core tools); it is a comment, so read_profile/--plain are unaffected.

Verified: shellcheck clean; magic-off (real design profile) → exit 10 +
API-key line; all active → exit 0; browse-off (non-GATE-BLOCK) → exit 0,
no trip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 11:21:48 +02:00
Bastien ChanotandClaude Opus 4.8 5776195eb6 feat(profile): list counters + grouped show + --plain parse mode
list: ITEMS column with compact per-category counts (e.g. 12s·1p·1m·1c),
left block aligned; full descriptions kept (CLI output, not 80-strict).
show: grouped by type (gstack/external/personal/plugin/mcp/cli) with
status; redundant per-line TYPE column and plugin marketplace suffix dropped.
show --plain: parsable "type<TAB>name" list, no status, zero claude calls —
the contract for the upcoming design gate. All derived from .profile files.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X3e8LaH2vymmxyh36h3jFU
2026-06-19 19:59:19 +02:00
Bastien ChanotandClaude Opus 4.8 d4a5cfec93 chore(caveman): purge plugin + always-on integration
Disable + uninstall caveman@caveman and delete every repo dependency on
it: SessionStart/UserPromptSubmit hook blocks, standalone hook files,
settings.json enabledPlugins + marketplace entries, install-plugins.sh
STEP 5.5, update-all.sh refresh step, plugins.lock.json entry, doctor.sh
checks, lib/detect-plugins.sh helpers, lib/profile.sh + plugin-advisor +
skills/profile protected-list entries, .gitignore runtime-file block,
and README/USAGE docs. Dead /caveman:compress refs replaced with
manual/claude.ai guidance. Memory-registry terse-format convention kept
(separate subsystem). Version 3.4.0 -> 3.5.0.

On a subscription plan caveman's ~75% output-token compression has no
cost benefit, and the always-on hooks added friction on validation
gates and client deliverables.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X3e8LaH2vymmxyh36h3jFU
2026-06-19 19:08:40 +02:00
Bastien ChanotandClaude Opus 4.8 f928a53ed9 fix(gstack): link spec skill, complete gitignore allowlist (BLK-007)
A gstack submodule bump added `spec` (v1.47) and the iOS device-farm
5-skill set (v1.43) but gstack `./setup` was never re-run, leaving all 6
as source-only — unlinked, invisible to Claude and untouched by
reset/`gstack on`.

  - Link `spec` only, surgically: skills/spec/SKILL.md symlink matching
    gstack setup's per-skill pattern (real dir + absolute SKILL.md link).
    Platform-agnostic, useful planning skill. Added to full + web-full
    profiles (must be in the active `full` profile or `set full` would
    re-disable it).
  - iOS skills deliberately NOT linked: this is a Linux host, and the iOS
    device-farm needs a Mac daemon + Tailscale + physical iOS devices.
    Linking them would add 5 dead skills plus passive token cost.
  - Complete the .gitignore gstack allowlist: add the 12 entries missing
    from it — `spec`, the 5 `ios-*` (so they stay ignored if a future
    setup on a Mac materializes them), and 6 already-parked skills
    (document-generate, landing-report, scrape, setup-gbrain, skillify,
    sync-gbrain). The parked ones matter because `gstack on` moves parked
    skills into skills/, where any allowlist gap becomes untracked git
    noise. Also drop the stale `skills/checkpoint` entry (renamed to
    context-save).

Verified: `profile show full`/`web-full` report spec enabled; the
source-vs-allowlist drift check is now empty; profile.sh still parses.

The gstack submodule pointer bump itself is left uncommitted (separate
pending work).

Memory: BLK-007 -> resolved; LRN-025 (gitignore allowlist must cover all
toggleable skills incl. parked, else enabling one = git noise).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 18:41:43 +02:00
Bastien ChanotandClaude Opus 4.8 da4e6b9590 feat(profile): add gstack on|off verb to lib/profile.sh
Centralize gstack toggling in the `profile` command without losing the
active-profile label.

  - `gstack on`  re-enables ALL parked gstack skills (moves
    skills-disabled/gstack__* back) but does NOT touch .active-profile,
    so the user layers full gstack on top of their current profile and
    the statusline label is preserved. Unlike `reset`, which clears the
    label to "none".
  - `gstack off` disables gstack skills not listed in the active profile;
    errors cleanly when no profile is active (needs one to know what to
    keep).

Refactor (behavior-preserving): extract three shared helpers
`enable_all_gstack`, `disable_gstack_not_in`, `parked_gstack_count` and
rewire `cmd_reset` + `cmd_set` to reuse them instead of duplicating the
symlink-toggle loops. Wire `gstack` into main() dispatch, usage(), and the
header usage block.

Docs: SKILL.md argument-hint, examples, and output-policy updated. The
generic `make profile cmd="gstack on"` target already covers Make usage.

Verified: shellcheck CLEAN, `bash -n` OK, 6-case test (help, bad-action,
off-with-no-profile, on, off-trim, on-cycle) with final assertion that the
live symlink state was restored exactly to its pre-test value.

Memory: capitalize BDR-018 (decision), LRN-024 (DRY helper-extraction
pattern), BLK-007 (6 gstack source skills ios-*/spec unlinked post
submodule bump — open follow-up), EVAL-002 (self-eval, false "full.profile
bug" flag corrected pre-edit). Backfill index drift: BDR-017, BLK-005/006.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 18:31:48 +02:00
Bastien ChanotandClaude Opus 4.6 c44852e665 feat(skills): add pdf-translate — PDF to translated HTML via Vision
OCR/image-based PDF pipeline: convert pages to PNGs, read with Claude
Vision (bypasses unreliable OCR text layer), translate with cross-page
glossary consistency, reconstruct faithful HTML via /design-html.

5 steps: deps check → page images + assets → style analysis →
page-by-page read+translate → HTML reconstruction → visual QA.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-26 03:38:16 +02:00
Bastien ChanotandClaude Opus 4.6 469c807c10 feat(skills): add design-motion-principles from kylezantos
Motion/animation design skill with 3-designer lens (Emil Kowalski,
Jakub Krehel, Jhey Tompkins). Two modes: Create and Audit.
Complements frontend-design (broad) with deep motion expertise.

Integration points:
- skills-external/design-motion-principles/ — skill files + references
- link.sh EXTERNAL_SKILLS — symlink to ~/.claude/skills/
- install-plugins.sh step 8c — presence check
- update-all.sh step 7.2 — sync from GitHub
- profiles: design, full, web, web-full — listed as external
- plugin-advisor — decision table, recommended sets, conditional rules
- design-gate — symlink check + non-blocking warning if missing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 22:40:23 +02:00
Bastien ChanotandClaude Opus 4.6 237810396c chore(plugins): add frontend-design to plugin-advisor and design-gate
frontend-design (Anthropic's anti-AI-slop skill) was installed but not
referenced in the plugin-advisor decision table, recommended sets, or
conditional rules — nor in the design-gate detection logic.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 20:59:19 +02:00
Bastien ChanotandClaude Opus 4.6 057a5b7f5c feat(skills): add frontend-design from anthropic-agent-skills
Source: anthropic-agent-skills plugin cache → skills-external/frontend-design/
Auto-install via install-plugins.sh step 8b, auto-update via update-all.sh
step 7.1, symlinked by link.sh (refactored to loop over EXTERNAL_SKILLS).
Added to profiles: design, web, web-full, full.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 00:58:30 +02:00
Bastien ChanotandClaude cb1e8cd814 feat(profile,statusline): show active profile in statusline via .active-profile cache
`bash lib/profile.sh current` is 12s+ — far too slow to call from the
statusline hook (runs on every keystroke). Add a one-line cache file at
`<repo>/.active-profile`, written by `cmd_apply` and `cmd_reset`. The
statusline reads the file directly with a single `head -n1`, no
sub-shell into `profile.sh`.

Behavior:
- `bash lib/profile.sh set <name>` (which ends in `cmd_apply`) and
  `bash lib/profile.sh apply <name>` both write `<name>` to
  `<repo>/.active-profile`.
- `bash lib/profile.sh reset` writes the literal `none`.
- Statusline inserts the cached profile name between the plan segment
  and the context-bar segment, e.g.
  `Opus 4.7 | claude (master) | Max | full | ████░░░░░░ 42% | 3m`.
- Missing or empty cache → statusline shows `?`.

Cache file is gitignored — it tracks runtime state, not source.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-21 05:14:12 +02:00
Bastien ChanotandClaude a4558ee805 fix(profile): resolve REPO via physical path (cd -P) so cmd_current finds the right skills-disabled
`lib/profile.sh:43` set `REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"`.
Default `cd` preserves symlinks (logical pathname), so when the script is
invoked via the `~/.claude/lib/profile.sh` symlink, `$REPO` resolves to
`/home/bchanot-ubuntu/.claude` instead of the real repo path. `$SKILLS_DIR`
still works because `~/.claude/skills` is itself a symlink to the repo's
`skills/`. But `$DISABLED_DIR` ends up at `~/.claude/skills-disabled` (a real
sibling directory containing only stale npx-skill symlinks) while the actual
disabled gstack skills sit at `<repo>/skills-disabled`.

Symptom: `bash "$HOME/.claude/lib/profile.sh" current` returns
`none (all gstack skills enabled — no profile set)` even when a profile is
applied — because `find $DISABLED_DIR -name 'gstack__*'` returns 0.

Adding `-P` to `cd` forces physical-path resolution so `$REPO` always points
to the real repo regardless of how the script was invoked. `cmd_current`
now correctly reports `full (100% match, 14 gstack skills disabled)`.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-21 05:04:54 +02:00
Bastien ChanotandClaude 69c5ded827 fix(profiles): rename checkpoint to context-save in 5 profiles
gstack upstream renamed the `checkpoint` skill to `context-save`
(it was shadowed by Claude Code's native /checkpoint rewind alias).
`/profile set full|dev|backend|web|web-full` was emitting
`⚠ missing: checkpoint — try: bash link.sh`, but link.sh only
materializes symlinks for skills that exist upstream — it cannot
resurrect a renamed skill, so the suggested next step was a dead
end.

Replace `checkpoint` → `context-save` in all 5 affected profiles
so the warning goes away and the new skill is enabled when the
profile is applied.

CLAUDE.md routing rule (line 193) also updated locally but left
uncommitted because the file carries an unrelated in-progress
graphify section rewrite.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-21 04:57:14 +02:00
Bastien ChanotandClaude 5da97d10f6 feat(profile): add full profile for end-to-end MVP via /init-project
Superset of web-full + dev + audit + plan tools — covers brainstorm →
design → architecture review → scaffold → implement → ship → audit
pipeline needed by /init-project to produce a real MVP in one session.

Also renames cmd_current "no-profile" sentinel from "full" to "none"
to avoid collision with the new profile name.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-18 20:50:13 +02:00
bastienandClaude 4535cce700 fix(toggle-external): handle source-only state in enable
`enable <tool>` for npx/external skills (darwin-skill, find-skills,
emil-design-eng) only handled two states: symlink in skills-disabled/
(move) and symlink in skills/ (already enabled). Missed the state
right after `make plugin` where the source dir exists at
~/.agents/skills/<tool> but no symlink has been created yet — first
run errored "not installed — run: make plugin" misleadingly.

Add a third branch: when the resolved source dir exists, create the
symlink in place. Resolve source path per tool (skills-external for
emil-design-eng, ~/.agents/skills for darwin-skill/find-skills). Error
message now names the path checked so the caller can verify install
vs symlink state without rereading the script.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-06 17:09:21 +02:00
bastienandClaude Opus 4.7 239d91db67 feat(profile): partition skills/plugins/MCPs/CLIs by usage profile
Ship lib/profile.sh + 9 profiles in lib/profiles/. A profile is a
plain-text file listing items + types (gstack | personal | external |
plugin@<marketplace> | mcp | cli). `profile set <name>` enables the
listed items and disables the rest:

  - gstack/personal/external skills: symlink toggle skills/ ↔
    skills-disabled/ (gstack__<name> prefix to avoid collisions; no
    prefix for personal/external).
  - plugins typed `plugin@<marketplace>`: actually toggled via
    `claude plugin enable|disable <name>@<marketplace>`. Allowlist:
    MANAGED_PLUGINS = ui-ux-pro-max, plugin-dev, pr-review-toolkit.
    Denylist: PROTECTED_PLUGINS = caveman, security-guidance,
    superpowers (always-on, never disabled even if absent from a
    profile).
  - mcp magic: delegated to lib/toggle-external.sh which already
    handles the MAGIC_API_KEY env lookup. Other MCPs stay advisory.
  - cli (rtk, gsd, ctx7, graphify): status-only, never auto-installed.

Profiles shipped:
  web        public website work — frontend + content + light dev
  seo        SEO + GEO + W3C audit (search/AI indexability + a11y)
  web-full   production website end-to-end (web ∪ seo ∪ qa-only/canary)
  backend    backend / API / system dev — no design, no SEO
  design     visual QA, design systems, mockups, polish
  dev        daily code work — features, fixes, refactor, ship
  qa         site testing, perf, canary, validation
  audit      comprehensive audit — security + SEO + perf + health
  minimal    strip all gstack skills (quiet session)

Commands:
  profile list / show <name> / current / apply <name> / set <name> /
  reset / diff <a> <b>

`current` heuristic returns "full" when nothing is disabled, otherwise
picks the profile with the highest available-ratio (counts both
"enabled" and "installed" — the latter for CLIs). Tiebreaker: larger
profile total wins, so web-full beats web at a 100% tie.

`reset` re-enables every gstack skill but does NOT touch plugins —
the user re-enables a managed plugin manually or via `apply <profile>`.
This is documented in the trailing info line.

Integration:
  - skills/profile/SKILL.md — `/profile` slash command, lists profiles,
    documents the per-type mechanism, points at lib/profile.sh.
  - agents/plugin-advisor.md — DETECT phase calls `profile current`,
    OUTPUT adds a PROFILE line, and TOGGLING EXTERNAL TOOLS gains a
    "Skill profiles" section with a signal → profile recommendation
    table.
  - lib/toggle-external.sh — header pointer to profile.sh for fine-
    grained activation (toggle-external still owns whole-gstack and
    magic-MCP toggles).
  - Makefile — `make profile cmd="set <name>"`, profile-list,
    profile-current, profile-reset.

Tested end-to-end: `set web` enables ui-ux-pro-max + magic; `set seo`
disables ui-ux-pro-max; `set minimal` disables ui-ux-pro-max but
spares always-on plugins; `reset` restores all 64 skills; shellcheck
clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 02:09:28 +02:00
bastienandClaude Opus 4.7 e4f4edc121 feat(caveman): full install — plugin + standalone hooks + MCP scaffold
Wires JuliusBrussee/caveman into the always-on tier alongside
security-guidance and superpowers. Caveman compresses Claude's output
tokens (~75%) by speaking like a caveman while keeping technical
substance. Three layers:

  1. Plugin (caveman@caveman, marketplace JuliusBrussee/caveman)
     — adds /caveman, /caveman-commit, /caveman-review, /caveman-stats,
       /caveman-help, /cavecrew, /compress + 3 cavecrew agents +
       SessionStart/UserPromptSubmit hooks from the plugin path.
  2. Standalone hooks (statusline + stats badge) deployed by
     caveman's own hooks/install.sh into ~/.claude/hooks/. Paths in
     settings.json normalized to ~/.claude/hooks/... so this user's
     home dir doesn't leak across machines.
  3. caveman-shrink MCP proxy — NOT auto-registered. The bare proxy
     fails health checks because it requires an upstream MCP server
     to wrap. install-plugins.sh STEP 5.5 prints a snippet showing how
     to register a wrapped entry (e.g. caveman-shrink-fs) when the user
     decides which upstream to compress.

New helper enable_plugin() for explicit always-on activation —
'claude plugin install' only copies into cache, doesn't write
enabledPlugins. Idempotent via Python json check.

doctor.sh adds detect_caveman / detect_caveman_hooks / detect_caveman_shrink
checks plus a 300t passive-cost adder. update-all.sh refreshes hook
files via the upstream installer's --force mode.

.gitignore covers caveman runtime files materialized into hooks/
because ~/.claude/hooks is symlinked to this repo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 23:02:47 +02:00
bastienandClaude Opus 4.7 f80f83ee77 feat(animation): auto-install motion lib + detection across init/onboard/advisor
Add lib/animation-lib-check.sh with detect_anim_eligibility,
is_anim_lib_installed and recommend_anim_install_cmd helpers.
Wire it into the framework:
- init-project STEP 5e: silent auto-install after scaffold validated
- onboard STEP 2.5: propose + wait for user confirmation (opt-in on
  existing projects)
- plugin-advisor PHASE 1/2/3: read-only detection only, never installs
- scaffolder PHASE 4: clarifies boundary (orchestrator owns motion install)
- design-gate filesystem signals: motion / motion-v / framer-motion /
  gsap / lottie-react / react-spring / popmotion / auto-animate

Recommends `motion` (rebranded from framer-motion in Nov 2024) for
React-family and Svelte stacks, `motion-v` for Vue 3 / Nuxt. Excludes
React Native (use react-native-reanimated), backend, embedded, Flutter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 15:32:46 +02:00
bastienandClaude Opus 4.7 c1efd87aad refactor(onboard,init-project): migrate file paths to .claude/
Adapt the two skills that scaffold project config + the underlying
onboarder agent + the dotfiles-meta archetype folder tree to the new
governance layout. AUDIT_* files go to .claude/audits/, TODO goes to
.claude/tasks/, and .claude/memory/ is seeded from ~/.claude/templates/memory/
with all 5 registries (decisions/learnings/blockers/journal/evals).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-23 16:06:14 +02:00
bastienandClaude 3c8b2a8f68 feat(archetypes): add project archetype library + detection algorithm
Introduce ~/.claude/lib/project-archetypes/ with 25 archetype files
(web, mobile, APIs, CMS, infra, firmware, etc.) and the detection
algorithm in lib/archetype-detector.md. Consumed by /onboard STEP 1
to drive archetype-specific audit stacks and plugin recommendations.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-21 22:37:07 +02:00
bastienandClaude Opus 4.7 72920e032e feat(toggle-external): manage Magic MCP (21st-dev) — installed disabled by default
Add `magic` to the unified toggle-external.sh helper alongside gstack,
emil-design-eng, darwin-skill, find-skills. MCPs are toggled via
`claude mcp add|remove` instead of symlink moves.

API key loaded from $REPO/.env (gitignored) via .env.example template.
install-plugins.sh step 8.7 forces magic MCP off after each install run
so the MCP doesn't load into every session unless explicitly enabled.

Toggle: bash lib/toggle-external.sh enable|disable|status magic

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 20:28:03 +02:00