CLAUDE.md · diff
git:20260827.f43a1a6 to git:20260827.dedaa66
163 added, 27 removed. Audit A to A.
# claude-code-kit
> Universal Claude Code toolkit — agents and skills for software development
## Who this is for (design north-star)
This is a **published plugin installed into other people's projects**, not a tool for this
repo alone. Every design decision is judged by: **does this work in a consumer's
environment** — where the plugin's files live in the plugin cache (not the project cwd),
where MCP servers may be absent or different, where hooks run on every session? A change
that only works in this repo is a defect. (Concrete gate in the Contributing checklist.)
**Interoperability is a first-class goal**: the kit must compose cleanly with other
plugins (e.g., superpowers) and with whatever MCP servers the user has (memory MCPs,
search MCPs, private/company servers). Three rules: never *assume* a specific plugin/MCP
is present; never *conflict* with one that is; *leverage* generically when available
(e.g., recall-before-plan / remember-after-done if memory-style tools exist — fail-open
otherwise). Guidance lives in skills, never in agent `tools:` allowlists.
## Installation
```bash
# Basic — Anthropic community catalog (read-only mirror; nightly sync — see Release Checklist)
/plugin marketplace add anthropics/claude-plugins-community
/plugin install claude-code-kit@claude-community
# Basic — direct marketplace (fastest updates)
/plugin marketplace add This-HW/claude-code-kit
/plugin install claude-code-kit@claude-code-kit
# Full (with security hooks + auto-format + pre-commit)
git clone https://github.com/This-HW/claude-code-kit && cd claude-code-kit && ./setup.sh
```
## Structure
```
plugins/
└── common/ — Core agents (33) + skills (19) + rules (13) + hooks
```
`plugins/common/` contains:
- `.claude-plugin/plugin.json` — plugin manifest
- `agents/` — agent `.md` files
- `skills/` — skill `.md` files
- `hooks/` — Python hook scripts (common only)
- `rules/` — governance rules (common only)
- `.codex-plugin/plugin.json`, `plugin.json` — **생성물**. Codex·Antigravity 타겟 매니페스트로,
`.claude-plugin/plugin.json` 을 SSOT 삼아 `scripts/build-targets.py` 가 만든다. 손으로 고치지 말 것
(`verify-done.sh` §14가 드리프트를 exit 1로 잡는다). 정책은 레포 루트 `packaging/targets.json`
## Key Skills
| Skill | Command | Description |
| ------------------------ | --------------------------- | ----------------------------------------------- |
| plan-task | `/plan-task` | Structured task planning |
| auto-dev | `/auto-dev` | Automated development pipeline |
| web-research | `/web-research` | MCP-powered research |
| review | `/review` | Code review: ruff + review-code + security-scan |
| multi-perspective-review | `/multi-perspective-review` | 3-Round Deliberation with 10 perspectives |
| doc-coauthoring | `/doc-coauthoring` | AI-assisted documentation authoring |
| debug | `/debug` | 4-Phase debug pipeline |
| test | `/test` | Run tests and auto-fix failures |
| agent-creator | `/agent-creator` | Generate plugin agents |
| skill-creator | `/skill-creator` | Generate plugin skills |
| mcp-builder | `/mcp-builder` | Scaffold MCP servers |
| agent-teams | `/agent-teams` | Large-scale parallel work — routes to native `ultracode` |
| native-watch | `/native-watch` | Audit native-feature absorption vs the kit (SSOT: docs/native-absorption.md) |
| self-improve | `/self-improve` | Propose agent/skill/rule improvements from ledger+evals (proposal-only, gated) |
| harness-export | `/harness-export` | Export host-neutral rules to AGENTS.md for Codex/OpenCode/Pi/Hermes (drift-gated) |
| eval-forge | `/eval-forge` | Forge an eval scenario from an observed defect — generated + self-validated |
| skill-forge | `/skill-forge` | Distill a solved hard problem into a reusable skill draft (proposal-only) |
## Agent Architecture
### 2-Tier Model
```
Tier 1: plugins/common/ — All projects (33 agents)
Tier 2: project-local/ — Project-specific (user-added)
```
### Agent Frontmatter
Every agent is a `.md` file with YAML frontmatter:
```yaml
---
name: agent-name # kebab-case, matches filename
description: | # Korean + English trigger conditions
MUST USE when: "keywords"
OUTPUT: result format
model: sonnet # opus | sonnet | haiku
effort: medium # low | medium | high | max
maxTurns: 20 # 20 for implementation agents, 10 for exploration/review
isolation: worktree # optional: run in isolated git worktree
tools:
- Read
- Edit
- Bash
disallowedTools:
- Task # regular agents cannot spawn sub-agents
---
```
### Model Selection
| Model | Use case | Examples |
| ---------- | -------------------------- | ------------------------------------- |
| **Opus** | Strategy, analysis, review | clarify-requirements, review-code |
| **Sonnet** | Code implementation, fixes | implement-code, fix-bugs, write-tests |
| **Haiku** | Exploration, simple checks | explore-codebase, verify-code |
### isolation: worktree
Apply to agents that **modify files** — prevents filesystem conflicts:
- ✅ implement-code, fix-bugs, write-tests, write-api-tests, implement-api, generate-boilerplate, sync-docs, optimize-logic
- ❌ explore-codebase, review-code, plan-implementation (read-only)
Merge-back protocol (exit conditions, sequential merge, conflict escalation) is
governed by `plugins/common/rules/parallel-worktree.md`.
- ### Delegation Signal
+ ### Delegation Signal — 폐기됨 (2026-08-27, W-022 R1)
- All agents end with a structured delegation signal:
+ 과거 모든 에이전트는 출력 끝에 아래 블록으로 끝나야 했다:
```
---DELEGATION_SIGNAL---
TYPE: DELEGATE_TO | TASK_COMPLETE | NEED_USER_INPUT | NEED_CLARIFICATION
TARGET: [agent-name]
REASON: [reason]
CONTEXT: [handoff context]
---END_SIGNAL---
```
- > **⚠ 이 계약은 런타임에 잘 지켜지지 않는다 (2026-08-27 실측).** 커버리지를 13종으로 넓히자
- > **관측 8종 중 6종(75%)** 이 이 마커를 간헐적으로 생략한다는 것이 드러났다
- > (`implement-code` 6/6 · `plan-implementation` 2/2 는 안정, 나머지는 1/2~2/3).
- > `implement-api` 는 모델·effort가 낮지 않은데도 실패해 "좋은 모델이면 안정"이라는 가설을 반증했다.
- >
- > **⚠ 단, 이 측정은 교란돼 있다**: `implement-code` 시나리오의 `task.md` 가 신호 형식을 직접
- > 지시한다(나머지는 안 한다). 6/6 은 대조군이 아니다. 무지시 준수 사례는 `plan-implementation`
- > 2/2(n=2)뿐이며, **"75% 미준수"는 조사 트리거였지 판정 근거가 아니다.**
- >
- > `verify-done.sh` §12는 이 계약이 **정의 파일에 적혀 있는지**를 33/33 검사해 왔지만,
- > **런타임에 실제 방출되는지는 아무도 잰 적이 없었다.** 오케스트레이션이 이미 스킬 주도 플랫
- > 위임으로 바뀐 만큼 이 신호가 사문화됐을 가능성도 있다 — 계약을 고칠 것인지 폐기할 것인지는
- > `docs/specs/2026-08-27-delegation-signal-contract-review.md` (W-021)에서 판별한다.
- > **그때까지 이 절을 "동작하는 계약"으로 읽지 말 것.**
+ **왜 있었나.** 구 순차 체인 오케스트레이션 모델에서, 서브에이전트가 이 신호로 메인
+ Claude에게 다음 에이전트를 지목했다.
+ **왜 없앴나.** 판별 결과(W-021): **hooks·skills·scripts·rules 어디에도 이 블록을
+ 파싱하는 결정론적 코드가 없었다.** 유일한 소비 지점은 `rules/agent-delegation-chain.md`가
+ 메인 Claude에게 "신호 블록을 스캔해 TARGET이 있으면 다음 에이전트를 자동 호출하라"고
+ 준 **자연어 지시**였다 — 파서가 아니라 모델 판단에 의존하는 경로였다는 점이 이 결론의
+ 무게다. 게다가 오케스트레이션은 이미 §Orchestration Model의 스킬 주도 플랫 위임으로
+ 넘어가 있었다 — 신호를 스캔해 다음 에이전트를 자동 호출하는 구 순차 체인 모델 자체가
+ 더 이상 쓰이지 않았다.
+
+ 이 조사를 시작하게 만든 트리거는 커버리지를 13종으로 넓히던 중(W-018) 드러난 준수율
+ 관측이었다 — **8종 중 6종(75%)**이 이 마커를 간헐적으로 생략했다(`implement-code`
+ 6/6·`plan-implementation` 2/2만 안정, `implement-api`는 모델·effort가 낮지 않은데도
+ 실패해 "좋은 모델이면 안정" 가설을 반증했다). 단, 이 관측은 교란돼 있다 —
+ `implement-code` 시나리오의 `task.md`가 형식을 직접 지시했으므로 6/6은 대조군이
+ 아니다. 무지시 준수 사례는 `plan-implementation` 2/2(n=2)뿐이다. 그럼에도 폐기
+ 결론(B: 사문화) 자체는 이 통계가 아니라 위 두 근거로 유지된다 — 비결정적 보조 경로는
+ 없는 것보다 나쁘다는 판단(이번 배치에서 evals `delegation_signal` 어서션을 분리한
+ 것과 같은 논리)에 따라 폐기를 실행했다. 상세 근거:
+ `docs/specs/2026-08-27-delegation-signal-contract-review.md`(W-021), ledger F-002.
+
+ **어디까지 걷어냈나** (다음 사람이 잔재를 찾을 때 기준):
+
+ - 에이전트 정의 33종 — 본문 `---DELEGATION_SIGNAL---` 블록 전부 제거
+ - 위 33종 중 32종의 frontmatter `OUTPUT:`/`MUST USE when:` — 신호 토큰만 제거,
+ 실제 산출물 서술과 무관한 트리거 문구는 보존. 산문 중 `DELEGATE_TO: git-workflow`
+ 같은 **에스컬레이션 의도 서술**은 기계 계약이 아니므로 그대로 유지
+ - `verify-done.sh` §12(에이전트 출력 계약 위치 검사) + CI 동등 스텝 — 제거(20/20 →
+ 섹션 번호는 재사용하지 않음, 아래 verify-done.sh 섹션 규약 참고)
+ - 스킬 4종(`agent-creator`·`eval-forge`·`harness-export`·`skill-forge`)의 예시
+ 블록 — 제거. `agent-creator`는 특히 중요했다: 새 에이전트 템플릿에 블록이 박혀
+ 있어 폐기를 무효화할 수 있었다
+ - 주입 규칙 2종(`plugins/common/rules/agent-system.md`,
+ `agent-delegation-chain.md`) — **외과적** 삭제. 신호 기계 계약(형식 정의·
+ TYPE→Action 매핑·자동 호출 절차)만 제거하고, 무관한 정책(Standing User
+ Authorization, "서브에이전트는 서브에이전트를 호출하지 않는다")은 보존.
+ `On Receiving Subagent Output` 절은 삭제가 아니라 스킬 주도 모델에 맞게 재작성.
+ 해설본(`docs/architecture/rules/`)도 같은 원칙으로 갱신, CHECKSUMS/MIRROR 재생성
+ - eval `delegation_signal` 체크 타입 — **삭제하지 않음.** `implement-code`·
+ `plan-implementation` 등 안정 통과 시나리오가 있어 체크 자체는 유효했다
+ (`evals/run.py`). 계약이 폐기됐으므로 그 시나리오들의 어서션은 개별 판단 대상
+ - 유지: 본문 산문의 `DELEGATE_TO: X` 같은 에스컬레이션 서술(기계 계약 아님),
+ 역사 기록(CHANGELOG, decision-log, 과거 spec)
+
## Development Conventions
### Editing an agent/skill does NOT affect the current session
Agents and skills are loaded from the **installed plugin cache**
(`~/.claude/plugins/cache/claude-code-kit/claude-code-kit/<version>/`), not from this
repo's working tree. So editing `plugins/common/agents/*.md` and immediately dispatching
that agent runs the **old** definition — the change is invisible until the version is
bumped, pushed, and the plugin updated.
This bit us in 2.14.0 development: `review-code`'s output contract was buried 496 lines
from the end of its definition, its reports came back empty twice, and the working-tree
fix could not be verified in the same session. Two consequences:
- **Never conclude "the definition change worked" from in-session behavior.** Verify by
- reading the file, or by a machine check (`verify-done.sh §12` is exactly that).
+ reading the file, or by a machine check (`verify-done.sh` § checks against the working
+ tree; note that `§12`, the check this incident originally motivated, was retired in
+ W-022 R1 — see the Delegation Signal section above).
- To actually exercise a definition change, bump the version and reinstall
(`/plugin marketplace update` → `/plugin install`), or point a scratch install at the
working tree.
Hooks and `scripts/` are different — hooks run from `${CLAUDE_PLUGIN_ROOT}` (also the
cache), but `scripts/` and `evals/` are repo-local and take effect immediately.
### Adding a New Agent
1. Create `plugins/common/agents/{category}/{name}.md`
2. Add required frontmatter (see template above)
3. Write Korean description with `MUST USE when:` trigger conditions
- 4. Add delegation chain at the end
- 5. No manifest edit needed — agents are auto-discovered from the directory
+ 4. No manifest edit needed — agents are auto-discovered from the directory
(plugin.json has no agent/skill registry)
### Adding a New Skill
1. Create `plugins/common/skills/{name}/SKILL.md`
2. Optionally add `README.md` in the same directory
3. No manifest edit needed — skills are auto-discovered from the directory
### Naming Conventions
- Agents: `verb-noun.md` (fix-bugs, plan-refactor, explore-codebase)
- Skills: `noun-action` (web-research, plan-task, auto-dev)
- All agent names must be kebab-case and match the `name:` frontmatter field
### Sub-agent Rules
- Regular agents: `disallowedTools: [Task]` — cannot spawn sub-agents
- Meta agents (facilitator, synthesizer, devil's advocate, impact-analyzer, consensus-builder, facilitator-teams — 6 total): `disallowedTools: [Bash]`
- Skills (auto-dev, etc.) drive delegation; leaf agents stay flat.
### Orchestration Model — Scale-Appropriate Primitives (Spec 2 / W-006)
오케스트레이션은 전통이 아니라 **스케일별로 올바른 프리미티브**를 쓴다. leaf 에이전트가
Task를 갖지 않는 이유는 "main만 조율" 도그마가 아니라, 우리 스케일에서 에이전트 중첩이
성능 이득 없이 예측불가능성·디버깅 부채만 더하기 때문이다.
| 작업 규모 | 오케스트레이션 |
| --------- | -------------- |
| Small / Medium | 스킬 주도 플랫 위임 (main이 Agent 병렬 dispatch → 결과 수집). 예측가능·검증된 경로 |
| Large (10~100+) | 네이티브 `ultracode`(dynamic workflow)를 **사용자가 수동 트리거** — 백그라운드 오케스트레이션. auto-dev는 Large 작업을 청크로 분할해 안내 |
> 네이티브 dynamic workflow / `/goal`은 대화형 전용이라 스킬에서 프로그래밍 트리거가
> 불가하다(2026.6 기준). 따라서 자동 위임은 검증된 Task 시스템 + 스킬 루프로 하고,
> 대규모 병렬은 사용자가 `ultracode`로 트리거한다. 실험적 자체 조율(구 agent-teams)은
> 이 네이티브 경로로 대체됐다.
### Phase Gate Pattern
```
Phase 1 (Planning) → 100% ambiguity removed via planning agents
Phase 2 (Development) → implement based on Phase 1 artifacts
Phase 3 (Validation) → review + security scan (parallel)
```
## Hooks
Located in `plugins/common/hooks/` (except `session-check.py`, which lives in
`plugins/common/setup/`):
- `session-check.py` — `SessionStart` environment/setup check (runs before
`session-start.py`; registered from `setup/`). Warns on: python below the 3.9
floor, missing global setup, `.claude/agents` dual-load, and a **stale venv**
(`.venv`/`venv` console-script shebangs still pointing at the project's old
path after a directory move/copy — `bin/python` keeps working while every
script dies with `bad interpreter`, or silently runs the old site-packages)
- `session-start.py` — injects rules + active Work status at `SessionStart`
- `protect-sensitive.py` — `PreToolUse` on Edit/Write/MultiEdit/NotebookEdit/Read:
blocks access to **sensitive file paths** (`.env`, keys, `.pem`) by path. env
templates (`.env.example`/`.sample`/`.template`/`.dist`) are exempt; writes to
them get a best-effort high-confidence secret-format content scan (W-016). It
does **not** otherwise scan file *content* or intercept `Bash`/`git commit` —
commit-time secret scanning is gitleaks + `setup/pre-commit`.
- `auto-format.py` — auto-formats code after edits (uses ruff for Python) (`PostToolUse`)
- `stop-validator.py` — on `Stop`, lints edited `.py` (ruff) and runs pytest on
the test files this session edited (never the full suite — that's CI/`/test`'s
job); on failure emits native `{"decision":"block","reason":...}` so Claude
continues and auto-fixes. Timeouts are non-blocking (`CLAUDE_STOP_TEST_TIMEOUT`)
- `utils.py` — shared utilities
Hooks are defined in `plugins/common/hooks/hooks.json` using the **exec form**
(`command` + `args[]`) so `${CLAUDE_PLUGIN_ROOT}` paths need no shell quoting.
**Python floor: 3.9** — hooks run on the *consumer's* `python3`, and macOS still
ships 3.9.x. So hook sources must stay 3.9-loadable: use
`from __future__ import annotations` and keep 3.10-only syntax out of anything
evaluated at import time. This is enforced twice, not by convention: ruff's `FA`
rules (statically, via root `ruff.toml`) and the `python39-compat` CI job (it
actually loads every hook under 3.9). Four hooks were silently dead on 3.9 until
2.12.1 — that is the failure this guards against.
> Subagent lifecycle tracking is delegated to native OpenTelemetry
> (`agent_id` / `parent_agent_id` spans, `/usage` breakdown) — the kit no longer
> ships a custom `agent-lifecycle.py` (removed in the 2.6.0 batch, Spec 1 / W-005).
### 설정값으로 경로를 만들면 반드시 봉쇄한다 (2026-08-27 확정)
- @docs/conventions/path-containment.md
+ **같은 결함이 세 번 반복됐다.** 정책·설정 파일에서 읽은 값으로 파일 경로를 조립하는 코드가
+ 그 값을 검증하지 않으면 레포 밖을 읽거나 쓴다. `pathlib` 의 `a / b` 는 **`b` 가 절대경로면 `a` 를
+ 통째로 버린다** — 이 한 줄이 세 번 모두의 원인이었다.
+ | 인스턴스 | 발견 | 증상 |
+ | --- | --- | --- |
+ | `export_harness.py` | 2.14.1 적대적 리뷰 | 심링크 탈출 + 검사/쓰기가 각각 resolve (TOCTOU) |
+ | `build-targets.py` | 2.15.0 교차 리뷰 | `manifestPath` 절대경로·`..`·심링크 3종 전부 레포 밖에 **씀** |
+ | `check_eval_coverage.py` | 2.15.0 기획 세션 전수조사 | `baseline.file` 절대경로로 레포 밖 파일을 기준선으로 **신뢰하고 green** |
+
+ **규칙**:
+
+ 1. 설정에서 온 경로는 **한 번만 resolve** 하고 그 결과를 끝까지 쓴다. 검사와 사용이 각각
+ resolve하면 그 틈이 TOCTOU다 (`_resolve_target()` / `_resolve_in_repo()` 관례)
+ 2. resolve 결과가 **레포 루트(또는 정해진 하위 디렉토리) 안**이 아니면 **exit 1**. 절대경로·`..`·심링크 전부
+ 3. **읽기 경로도 봉쇄한다.** 세 번째 인스턴스는 읽기 전용인데도 게이트가 거짓 green을 냈다
+ 4. `--check` 같은 **검사 전용 모드에도 같은 봉쇄를 건다.** 2.14.1은 쓰기에만 걸어 구멍이 남았다
+
+ 새 코드가 설정값으로 경로를 만든다면 위 두 파일의 헬퍼를 **그대로 따라라.** 관례를 새로
+ 발명하는 것이 이 결함이 반복된 이유다.
+
## Security
- `gitleaks` scans all pushes/PRs (config: `.gitleaks.toml`)
- Never hardcode secrets, API keys, internal IPs, or project names
- `protect-sensitive.py` runs as a `PreToolUse` hook on Edit/Write/MultiEdit/NotebookEdit/Read — path-based (plus a best-effort content scan only for env-template writes), not commit-based (see Hooks section)
## CI/CD
`.github/workflows/validate.yml` runs on push to `main` (and PRs):
1. Validates JSON syntax (`plugin.json`, `marketplace.json`)
2. Checks agent frontmatter completeness (`name`, `description` required)
3. Lints with `ruff check .` and runs pytest
4. Lints shell via `scripts/lint-shell.sh` (same script as the local gate §3b)
5. Verifies doc counts via `scripts/check_doc_counts.py` (same script as the local gate)
6. Runs gitleaks security scan
7. `python39-compat` job: loads every hook under Python 3.9 (consumer floor)
### 드리프트 게이트는 셋이고, 통합하지 않는다 (2026-08-27 판정)
- @docs/conventions/no-gate-integration.md
+ `verify-done.sh` 에는 "생성물이 SSOT와 일치하는가"를 묻는 게이트가 **셋** 있다.
+ | § | 대상 | 판정 방식 |
+ | --- | --- | --- |
+ | **11** | `AGENTS.md` 마커 블록 ↔ `rules/` 원문 | **sha256 대조** |
+ | **13** | eval 시나리오 ↔ 기준선 | **집합 양방향 대조** + 티어 커버리지 |
+ | **14** | 타겟 매니페스트 ↔ `.claude-plugin/plugin.json` | **파일 존재 + 내용 대조** |
+
+ 같은 질문처럼 보이지만 **입력·판정 기준·실패 메시지가 전부 다르다.** 공통 프리미티브로 묶으면
+ 추상이 세 케이스를 다 감당하지 못해 분기 파라미터가 늘고, **게이트 코드가 어려워진다.**
+ 게이트는 읽기 쉬워야 신뢰된다 — 아무도 이해하지 못하는 게이트는 red가 떴을 때 무시된다.
+
+ **그래서 통합하지 않는다.** 중복은 코드가 아니라 **규약**으로 줄인다 (위 경로 봉쇄 관례가 그 예다).
+ 네 번째 드리프트 게이트가 필요해지는 시점에 재검토한다 — rule of three는 세 번째에 묶으라는 뜻이
+ 아니라, **세 번째까지는 아직 패턴이 아닐 수 있다**는 뜻이다.
+
### Lint is one ruleset, everywhere
- @docs/conventions/lint-single-ruleset.md
+ `ruff.toml` at the repo root is the **single source** for both the rule set and
+ the lint scope; `ruff check .` is the only command (CI, `verify-done.sh §3`, and
+ the `auto-format` hook all resolve to it). Two traps it exists to close:
+ - **No project config → ruff falls back to the developer's global
+ `~/.config/ruff/ruff.toml`** (which this kit itself installs). That masked a
+ real CI failure once: local green, CI red.
+ - **Ruff's *default* rule set changes between releases** (0.15 enables E402, 0.16
+ does not), so relying on defaults makes two machines disagree. The rules are
+ therefore listed explicitly, and the version is pinned in `.ruff-version`
+ (CI installs exactly that; `verify-done.sh` warns when the local ruff differs).
+
+ Raising the ruff pin is a deliberate act: bump `.ruff-version`, fix what the new
+ version flags, land both together.
+
+ **The test runner is pinned the same way.** `.pytest-version` is the pin; CI
+ installs exactly it, and `verify-done.sh §4` warns when the local pytest differs.
+ pytest changes collection, fixture, and deprecation behavior across majors, so an
+ unpinned runner means CI silently floats to the newest release and can go red with
+ no code change — the same failure `.ruff-version` exists to prevent. Both pins are
+ also what makes a dev venv reproducible:
+
+ ```bash
+ python3 -m venv .venv
+ ./.venv/bin/python -m pip install "pytest==$(cat .pytest-version)" "ruff==$(cat .ruff-version)"
+ ```
+
### Rules have a long-form mirror — and it is checksum-guarded
- @docs/conventions/rules-mirror.md
+ `plugins/common/rules/` (13) is what gets **injected every session**, so it is compressed.
+ `docs/architecture/rules/` (9) is the long-form human explanation of nine of those rules,
+ created in W-004 — tables, worked examples, anti-patterns. The remaining four
+ (`definition-of-done`, `feedback-loop`, `loop-engineering`, `parallel-worktree`) have no
+ mirror by design; the injected rule is the whole story for them.
+ Nothing linked the two, so they drifted silently — a 2026-08-17 audit found three behind,
+ and the `planning-check` mirror still told readers to search Notion/Figma MCP in order,
+ **assuming those MCPs are installed**, which contradicts the consumer-first north-star.
+ `docs/architecture/rules/MIRROR.sha256` now records, per mirrored rule, the sha256 of the
+ injected rule the explanation last reflected. `verify-done.sh §7` fails when they diverge;
+ `scripts/sync-rule-mirror.sh --regenerate` updates it. Regeneration is deliberate on
+ purpose — auto-updating the manifest would make the check meaningless.
+
+ Normative statements live in the injected rule. The mirror explains and points at it; it
+ must not redefine anything, or the drift comes back through the front door.
+
### Shell is linted too
- @docs/conventions/shell-lint.md
+ The completion gate (`verify-done.sh`) and the installer (`setup.sh`) *are* shell —
+ linting Python rigorously while leaving them unchecked means the code that decides
+ "done" is the code nobody checks. `scripts/lint-shell.sh` is the single command
+ (CI and `verify-done.sh §3b` both call it); it owns the target list and the
+ severity threshold, so neither side can drift. Targets are resolved from
+ `git ls-files` by extension **and** shebang, so extensionless scripts like
+ `plugins/common/setup/pre-commit` are covered and new scripts need no registration.
+ shellcheck is pinned in `.shellcheck-version` and CI verifies the release tarball's
+ sha256 — bump both together or the step fails loudly.
+ One deliberate asymmetry with ruff: a *missing* shellcheck is a yellow note locally,
+ not a red. CI (pinned version) is the authoritative verdict; the local run is fast
+ feedback. It never reports green when it could not check.
+
## Release Checklist
**CRITICAL: Every commit that changes plugin behavior MUST bump the version in `plugins/common/.claude-plugin/plugin.json`.**
Plugin cache is keyed by `{plugin-name}/{version}` — same version = no update fetched = users never get the fix.
- @docs/conventions/release-process.md
+ - Patch bump (2.x.y) for bug fixes and hook changes
+ - Minor bump (2.x.0) for new agents, skills, or features
+ - Add a matching `## [x.y.z]` entry to `CHANGELOG.md` (verify-done.sh §6 fails if
+ the plugin.json version and the CHANGELOG top entry diverge)
+ - Keep README/docs version-agnostic (link to CHANGELOG) so they can't drift
+ - **버전을 올렸으면 타겟 매니페스트를 재생성한다**: `python3 scripts/build-targets.py --write`.
+ `.claude-plugin/plugin.json` 만 올리고 이것을 빠뜨리면 Codex·Antigravity 패키지에 **옛 버전이
+ 실린 채** 나간다. v2.15.0 릴리스에서 실제로 밟았고 `verify-done.sh` §14가 잡았다 —
+ 게이트가 없었다면 그대로 배포됐을 실수다
+ - Rules `.md` 변경 시 CHECKSUMS 재생성: `(cd plugins/common/rules && shasum -a 256 *.md | grep -v CHECKSUMS > CHECKSUMS.sha256)` — 이 매니페스트는 보안 경계가 아니라 우발적 드리프트 감지기다 (verify-done §7이 집합 동등성까지 강제)
+ - 그 룰에 **해설본 미러**가 있으면(아래 참조) 해설본도 함께 손보고 `scripts/sync-rule-mirror.sh --regenerate`
+ - Tag **the commit you push as the release**: `git tag -a vX.Y.Z <commit> -m "vX.Y.Z"`,
+ then `git push --tags`. Later commits that leave the version untouched (docs, repo
+ tooling) are not a new release and do not move the tag. `verify-done.sh §6` fails when
+ any past CHANGELOG release lacks a tag — the practice lapsed silently once (20 untagged
+ releases between 2.10.4 and 2.12.3), so it is a machine check now, not a convention.
+ Two caveats on existing tags: the 2026-08-17 backfill could not recover which commit was
+ actually pushed as each old release, so it used the closest approximation — the last
+ commit carrying that version; and tags predating v2.11.0 were placed ad hoc and follow
+ no single rule. Every tag does point at a commit whose `plugin.json` matches it.
+ - Run `scripts/verify-done.sh` (green) before claiming a release ready (definition-of-done)
```bash
- # Before git commit — bump version, regenerate targets, self-verify (one command):
- scripts/bump-version.sh x.y.z
+ # Before git commit — update version field:
+ # plugins/common/.claude-plugin/plugin.json → "version": "x.y.z"
```
### Distribution & catalog propagation
Two install channels propagate a pushed `main` differently — know which one a user is on:
- **Direct marketplace** (`This-HW/claude-code-kit` → `@claude-code-kit`): reflects `main`
HEAD **immediately** on `/plugin marketplace update`. This is the "fastest updates" path.
- **Anthropic community catalog** (`anthropics/claude-plugins-community` → `@claude-community`):
a **read-only mirror synced nightly** from Anthropic's internal review pipeline. Its entry
is **pinned to a commit SHA**; the pin advances **automatically** as you push to `main`,
but only after the pipeline re-runs safety screening and the nightly mirror sync — expect
**~a day, not instant**. You do **NOT** PR the catalog (direct PRs are auto-closed); the
one-time listing was via `clau.de/plugin-directory-submission`, and **version updates
need no re-submission**. (Verified 2026-07-29 against the catalog repo README.)
- **Implication**: right after a release, the fix is live on the direct marketplace but the
community catalog still serves the previous pinned SHA until the next nightly sync. Point
users who need a fix immediately to the direct marketplace path.
## Contributing
PRs welcome. Checklist:
- [ ] **Consumer-first**: works in an installing user's environment, not just this repo —
no reliance on the project cwd containing plugin files, no assumption a specific MCP
server is installed, hooks fail-open when their assumptions don't hold
- [ ] No `mcp__*` tools in any agent `tools:` allowlist (MCP lives in skills — see
`rules/mcp-usage.md`; absent MCP in an agent allowlist hallucinates, CC #13898)
- [ ] Agent frontmatter has `name`, `description`, `model`, `maxTurns`
- [ ] No forbidden fields: `permissionMode`, `context_cache`, `output_schema`, `next_agents`, inline `hooks`
- [ ] Description includes `MUST USE when:` trigger conditions
- [ ] File-modifying agents have `isolation: worktree`
- [ ] Regular agents have `disallowedTools: [Task]`
- [ ] Skill `description` field is in English
- [ ] Version bumped in `plugins/common/.claude-plugin/plugin.json` + matching `CHANGELOG.md` entry
(the manifest has **no** agent/skill registry — both are auto-discovered from their
directories; the only thing a new component must touch there is the version)
- [ ] CI passes (JSON valid, frontmatter complete, no forbidden fields, pytest green, no secrets)