Jelajahi Sumber

chore(memory): capitalize BDR-006 + LRN-005 + LRN-006 from caveman session

- BDR-006: Caveman as 4th always-on plugin (output compression, sym
  with rtk's input compression). Full install rationale, alternatives
  rejected, caveats around path normalization + gitignore.
- LRN-005: claude plugin install does NOT enable — explicit
  claude plugin enable required for ALWAYS-ON plugins. Pattern: read
  enabledPlugins, never hardcode plugin names in status displays.
- LRN-006: caveman-shrink (and any MCP middleware proxy) needs an
  upstream wrapper. Bare registration fails health checks. Pattern:
  register under derived names (proxy-upstream), don't auto-register.
- Journal: 2026-05-03 entry summarizing the session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bastien 1 Minggu lalu
induk
melakukan
6f81b6ca0a
3 mengubah file dengan 52 tambahan dan 0 penghapusan
  1. 17 0
      .claude/memory/decisions.md
  2. 7 0
      .claude/memory/journal.md
  3. 28 0
      .claude/memory/learnings.md

+ 17 - 0
.claude/memory/decisions.md

@@ -27,6 +27,7 @@ rules:
 | BDR-003 | 2026-04-23 | Gitignore wildcard + negations pattern for .claude/ | accepted |
 | BDR-004 | 2026-04-27 | Adopt auto permission mode as default | accepted |
 | BDR-005 | 2026-04-27 | `motion` as default animation library; advisor stays read-only | accepted |
+| BDR-006 | 2026-05-03 | Caveman as 4th always-on plugin (output compression) | accepted |
 
 ---
 
@@ -95,3 +96,19 @@ rules:
   - `eligible|motion-v`: Vue 3, Nuxt
   - `no|-`: backend, CLI, embedded, Flutter, static HTML, **React Native** (use `react-native-reanimated`), Astro without UI integration, no `package.json`
 - **Reference**: helper at `lib/animation-lib-check.sh`; integration in `skills/init-project/SKILL.md` STEP 5e, `skills/onboard/SKILL.md` STEP 2.5, `agents/plugin-advisor.md` PHASE 1/2/3, `lib/design-gate.md`.
+
+## BDR-006 — Caveman as 4th always-on plugin (output compression)
+
+- **Date**: 2026-05-03
+- **Status**: accepted
+- **Decision**: install `JuliusBrussee/caveman` in the always-on tier alongside `security-guidance`, `superpowers`, and `rtk`. "Full" install = plugin (`/caveman` + cavecrew agents + plugin-scoped SessionStart/UserPromptSubmit hooks) + standalone hooks (statusline + stats badge in `~/.claude/hooks/`) + `caveman-shrink` MCP scaffold (NOT auto-registered — proxy needs upstream wrapper). `install-plugins.sh` STEP 5.5 calls `enable_plugin "caveman" "caveman"` to write it into `enabledPlugins`. Hook paths in `settings.json` are normalized to `~/.claude/hooks/...` post-install so this user's home dir doesn't leak across machines.
+- **Why**: caveman compresses Claude's output ~75% via caveman-speak while preserving technical substance. Symmetrical with rtk (input compression hook) — rtk shrinks tool I/O, caveman shrinks model output. Both hooks pay zero passive cost in a clean session and amortize across long runs. Always-on is justified: the plugin auto-deactivates with phrases like "stop caveman" / "normal mode", so toggle would be friction without benefit.
+- **Alternatives rejected**:
+  - Toggle plugin (start OFF) — rejected: misses the by-default benefit; the user would need to remember `claude plugin enable caveman@caveman` per session, which negates the auto-compression value.
+  - `--minimal` install (plugin only) — rejected: loses the standalone stats badge that surfaces token-saving telemetry.
+  - `--all` install (adds per-repo `caveman-rules.md` etc. into `$PWD`) — rejected: would litter THIS config repo (the cwd at install time) with rule files meant for project repos. Let users opt in per-repo when they want it.
+  - Auto-register `caveman-shrink` MCP — rejected: the proxy errors with "missing upstream command" without an upstream MCP to wrap, fails health checks. Print a snippet instead and let the user pick which upstream they want compressed (filesystem, github, …).
+- **Caveats**:
+  - Caveman's `hooks/install.sh` writes absolute paths (`$HOME/.claude/hooks/caveman-*.js`) into `settings.json`. Since `settings.json` is symlinked into the repo, the absolute path would commit a username. STEP 5.5 runs a Python post-process to rewrite to portable `~/.claude/hooks/...` form (bash expands `~` before passing to `node`).
+  - Caveman's hook files materialize in `hooks/` (the repo dir, not `~/.claude/hooks/`) because the latter is a symlink. They're added to `.gitignore` to prevent accidental commit of user-scope state.
+- **Reference**: install-plugins.sh STEP 5.5, lib/detect-plugins.sh `detect_caveman*` + `plugin_enabled`, doctor.sh caveman block, commit `9b20b84`.

+ 7 - 0
.claude/memory/journal.md

@@ -28,3 +28,10 @@ rules:
 - Learning: Claude Code `disable*` settings use the sentinel string `"disable"`, not a boolean (LRN-003).
 - 3 atomic commits (`f7f033f..1421578`) via `/commit-change`.
 - Animation lib autoflow added: new helper `lib/animation-lib-check.sh` + STEP 5e in `/init-project` (auto-install) + STEP 2.5 in `/onboard` (opt-in) + read-only detection in `plugin-advisor` PHASE 1/2/3 + signal in `lib/design-gate.md` + scaffolder note. `motion` chosen over legacy `framer-motion` (BDR-005, LRN-004).
+
+## 2026-05-03
+
+- Added `JuliusBrussee/caveman` as 4th always-on plugin (BDR-006). Full install: plugin + standalone hooks + caveman-shrink MCP scaffold (snippet only, not auto-registered — proxy needs upstream wrapper, LRN-006).
+- Discovered two co-masking bugs: `claude plugin install` doesn't enable (LRN-005) and `session-start.sh` was hardcoding "✅ ON: security-guidance rtk superpowers" regardless of actual state. Added `enable_plugin()` helper + `plugin_enabled()` detector reading `enabledPlugins` from `settings.json`. Banner now reflects reality.
+- Side fix: doctor.sh exited under `set -euo pipefail` when gstack/skills/ was missing — wrapped find in brace + `|| true`.
+- 3 atomic commits (`0184818..2ec7935`).

+ 28 - 0
.claude/memory/learnings.md

@@ -24,6 +24,8 @@ rules:
 | LRN-002 | 2026-04-23 | Moving report-file paths requires grepping bash READS, not just WRITES | any refactor that moves a generated file used by a dispatcher |
 | LRN-003 | 2026-04-27 | Claude Code `disable*` settings use sentinel string `"disable"`, not boolean | any change to `permissions.defaultMode` or related blocker keys |
 | LRN-004 | 2026-04-27 | `framer-motion` was rebranded `motion` in Nov 2024 — different packages per framework | any new project recommending an animation lib; auditing legacy imports |
+| LRN-005 | 2026-05-03 | `claude plugin install` does NOT enable — separate `claude plugin enable` required | every plugin installer that targets ALWAYS-ON status |
+| LRN-006 | 2026-05-03 | `caveman-shrink` (and any MCP middleware proxy) is non-functional without an upstream wrapper | any MCP middleware/proxy package — never `claude mcp add` it bare |
 
 ---
 
@@ -67,3 +69,29 @@ rules:
   - When auditing existing projects, check both `framer-motion` and `motion` keys in `package.json` deps; treat either as "animation already covered".
   - Before adopting any "industry default" lib in a framework, verify the canonical package name is current — naming churn (rebrand, scope change `@org/lib`, fork) is common in JS land.
 - **Reference**: helper `lib/animation-lib-check.sh`, BDR-005.
+
+## LRN-005 — `claude plugin install` does NOT enable — `claude plugin enable` is a separate step
+
+- **Date**: 2026-05-03
+- **Pattern**: the Claude Code CLI splits "available" from "active" for marketplace plugins. `claude plugin install --scope user name@source` only copies the plugin into `~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/`. It does NOT write `name@source: true` into the user's `settings.json:enabledPlugins` map. Without an explicit `claude plugin enable name@source`, the plugin sits dormant — installed but unloaded. This is symmetric with `claude plugin disable`, which keeps the cache and only removes the enabledPlugins entry.
+- **Context**: discovered while auditing why `security-guidance` and `superpowers` were ✘ disabled in `claude plugin list` despite the project's `install-plugins.sh` summary banner declaring them "ALWAYS ON". Root cause: `install_plugin()` only ran `claude plugin install`, never `enable`. The bug had stayed invisible because a hardcoded `printf "│  ✅ ON  : security-guidance rtk superpowers │"` in `session-start.sh` printed the same names regardless of actual state — the lying banner agreed with the lying install.
+- **Future application**:
+  - For any plugin meant to be ALWAYS ON, follow `claude plugin install` with `claude plugin enable name@source` (idempotent — no-op if already enabled).
+  - Detect "actually enabled" via `enabledPlugins[name@source] === true` in `settings.json`, NOT by the presence of the cache dir. The pattern is implemented in `lib/detect-plugins.sh:plugin_enabled()` (filesystem grep, no subprocess).
+  - Any banner / status display that claims a plugin is on must read state, never hardcode names. Hardcoded labels turn a single bug into two co-conspiring bugs that mask each other.
+- **Reference**: commit `2ec7935`, `lib/detect-plugins.sh:plugin_enabled`, `install-plugins.sh:enable_plugin()`.
+
+## LRN-006 — `caveman-shrink` (and any MCP middleware proxy) needs an upstream wrapper to function
+
+- **Date**: 2026-05-03
+- **Pattern**: some MCP packages are middleware proxies, not standalone servers. They wrap an upstream MCP server and transform its responses (e.g. `caveman-shrink` compresses prose fields). Running them bare via `claude mcp add proxy-name -- npx -y proxy-pkg` registers a server that errors immediately with "missing upstream command" — every health check fails, and Claude Code reports the MCP as broken until a human intervenes. The CLI `claude mcp add` doesn't validate that the configured command launches a working stdio MCP, so the bad registration silently lands.
+- **Context**: when adding caveman, the upstream installer auto-registers `claude mcp add caveman-shrink -- npx -y caveman-shrink` and prints "registered. wrap an upstream by editing the mcpServers entry". Following that flow leaves the user with a permanently failing MCP entry until they realize they have to edit `~/.claude.json` manually.
+- **Future application**:
+  - For any MCP that is a proxy/middleware (read package docs for "upstream", "wraps", "proxy"), register it under a DERIVED name `<proxy>-<upstream>` with the upstream baked into the args. Example for caveman-shrink wrapping the filesystem server:
+    ```
+    claude mcp add caveman-shrink-fs --scope user -- \
+      npx -y caveman-shrink npx -y @modelcontextprotocol/server-filesystem /path
+    ```
+  - Detection of "is this MCP correctly set up?" must look for the derived name (`caveman-shrink-*`), not the bare proxy name. Bare-name registration is treated as broken.
+  - Default install scripts should NOT auto-register middleware MCPs — print the snippet for the user to choose an upstream. See `install-plugins.sh` STEP 5.5.
+- **Reference**: commit `9b20b84`, `lib/detect-plugins.sh:detect_caveman_shrink`, `install-plugins.sh` STEP 5.5 MCP block.