Skip to content

Commit fe4784c

Browse files
masuP9claude
andcommitted
docs: add implementation plans from 2026-06-11 audit
improve スキルによる監査(commit f5abf51 時点)から生成した 実装プラン 001–007 と進捗管理 README を記録として追加。 全 7 件は実装・レビュー・マージ完了済み。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent cebe35e commit fe4784c

8 files changed

Lines changed: 1247 additions & 0 deletions

plans/001-add-shellcheck-to-ci.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
# Plan 001: CI に shellcheck を追加し、既存の指摘をトリアージする
2+
3+
> **Executor instructions**: Follow this plan step by step. Run every
4+
> verification command and confirm the expected result before moving to the
5+
> next step. If anything in the "STOP conditions" section occurs, stop and
6+
> report — do not improvise. When done, update the status row for this plan
7+
> in `plans/README.md`.
8+
>
9+
> **Drift check (run first)**: `git diff --stat f5abf51..HEAD -- .github/workflows/ci.yml scripts/ hooks/ skills/claude-collab/scripts/`
10+
> If any in-scope file changed since this plan was written, compare the
11+
> "Current state" excerpts against the live code before proceeding; on a
12+
> mismatch, treat it as a STOP condition.
13+
14+
## Status
15+
16+
- **Priority**: P1
17+
- **Effort**: S–M
18+
- **Risk**: LOW
19+
- **Depends on**: none
20+
- **Category**: dx
21+
- **Planned at**: commit `f5abf51`, 2026-06-11
22+
23+
## Why this matters
24+
25+
このリポジトリは scripts/・hooks/・skills/ 配下に合計約 2,900 行の bash を持ち、さらに commands/*.md に約 3,500 行の埋め込み bash があるが、CI(`.github/workflows/ci.yml`)は JSON/YAML lint とテスト実行のみで、シェルの静的解析が一切ない。クォート漏れや未定義変数のような bash 特有のバグはテストをすり抜けやすく、shellcheck はそれを最も安価に捕捉する手段。過去にも sed のエスケープ起因の障害(CLAUDE.md / メモリ記載の「Unmatched ( or \\(」問題)が起きており、静的解析の価値が実証されているコードベースである。
26+
27+
## Current state
28+
29+
- `.github/workflows/ci.yml``test` ジョブ(2つのテストスイート実行)と `lint` ジョブ(Python による JSON/YAML lint)のみ。shellcheck ステップは存在しない。
30+
- 対象シェルスクリプト(すべて git 管理下):
31+
- `scripts/codex-helpers.sh`(734行)— コアヘルパー
32+
- `scripts/test-helpers.sh`(1,341行)— テストスイート
33+
- `hooks/enforce-skill-usage.sh`(43行)— PreToolUse フック
34+
- `skills/claude-collab/scripts/claude-helpers.sh`
35+
- `skills/claude-collab/scripts/test-claude-helpers.sh`
36+
37+
`ci.yml` の lint ジョブは現在この形(抜粋):
38+
39+
```yaml
40+
lint:
41+
runs-on: ubuntu-latest
42+
steps:
43+
- uses: actions/checkout@v4
44+
45+
- uses: actions/setup-python@v5
46+
with:
47+
python-version: '3.x'
48+
```
49+
50+
リポジトリ規約: `scripts/codex-helpers.sh:136-138` の `sed $'s/\033\\[[0-9;]*[a-zA-Z]//g'` のような `$'...'` クォートは**意図的**(`\x1b` ヘックスエスケープは macOS sed で壊れるため)。shellcheck がこの周辺を指摘しても書き換えないこと。
51+
52+
## Commands you will need
53+
54+
| Purpose | Command | Expected on success |
55+
|---------|---------|---------------------|
56+
| shellcheck 導入(ローカル, WSL/Ubuntu) | `sudo apt-get install -y shellcheck` | exit 0(GitHub の ubuntu-latest ランナーにはプリインストール済み) |
57+
| ベースライン確認 | `shellcheck scripts/*.sh hooks/*.sh skills/claude-collab/scripts/*.sh` | 初回は指摘が出る可能性あり |
58+
| テスト | `bash scripts/test-helpers.sh` | `Results: 89 passed, 0 failed, 0 skipped` |
59+
| テスト(claude) | `bash skills/claude-collab/scripts/test-claude-helpers.sh` | `claude helper tests passed` |
60+
61+
## Scope
62+
63+
**In scope**(変更してよいファイル):
64+
- `.github/workflows/ci.yml`
65+
- `scripts/codex-helpers.sh`, `scripts/test-helpers.sh`, `hooks/enforce-skill-usage.sh`, `skills/claude-collab/scripts/claude-helpers.sh`, `skills/claude-collab/scripts/test-claude-helpers.sh` — shellcheck 指摘の修正のみ
66+
- `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json` — バージョン更新のみ
67+
68+
**Out of scope**:
69+
- `commands/*.md` の埋め込み bash(Plan 003 が扱う)
70+
- ヘルパー関数の挙動変更・リファクタリング(指摘修正は挙動を変えないこと)
71+
- `tmp/` 配下すべて
72+
73+
## Git workflow
74+
75+
- Branch: `feat/ci-shellcheck`
76+
- コミットは conventional commits 形式。例(`git log` より): `fix: ensure LF line endings for hook scripts (v0.31.1)`
77+
- push / PR 作成はオペレーターの指示があるまで行わない。
78+
79+
## Steps
80+
81+
### Step 1: ベースラインを取得する
82+
83+
`shellcheck scripts/*.sh hooks/*.sh skills/claude-collab/scripts/*.sh` を実行し、指摘の件数と SC コードの内訳を記録する。
84+
85+
**Verify**: コマンドが実行できること(指摘の有無は問わない)。指摘が 100 件を超える場合は STOP conditions 参照。
86+
87+
### Step 2: 指摘を修正する
88+
89+
- error / warning レベルを優先的に修正する。修正は挙動を変えないこと(クォート追加、`read -r`、未使用変数の削除など)。
90+
- 意図的なパターン(例: `$'...'` クォートの sed、意図的な word splitting)には行単位の `# shellcheck disable=SCxxxx` を理由コメント付きで付ける。ファイル全体の disable は使わない。
91+
- info / style レベルは、修正が自明なもののみ対応し、残りは disable で明示する。
92+
93+
**Verify**: `shellcheck scripts/*.sh hooks/*.sh skills/claude-collab/scripts/*.sh` → exit 0
94+
95+
### Step 3: 両テストスイートで回帰がないことを確認する
96+
97+
**Verify**: `bash scripts/test-helpers.sh` → `Results: 89 passed, 0 failed, 0 skipped` / `bash skills/claude-collab/scripts/test-claude-helpers.sh` → `claude helper tests passed`
98+
99+
### Step 4: CI にステップを追加する
100+
101+
`.github/workflows/ci.yml` の `lint` ジョブに、checkout の直後(Python セットアップの前)に追加:
102+
103+
```yaml
104+
- name: Shellcheck
105+
run: shellcheck scripts/*.sh hooks/*.sh skills/claude-collab/scripts/*.sh
106+
```
107+
108+
(ubuntu-latest には shellcheck がプリインストールされているため install ステップは不要。)
109+
110+
**Verify**: ローカルで同じコマンドを実行 → exit 0。`python3 -c "import yaml,sys; yaml.safe_load(open('.github/workflows/ci.yml'))"` → exit 0(YAML 構文確認)
111+
112+
### Step 5: バージョンを更新する
113+
114+
CLAUDE.md のルールに従い、`.claude-plugin/plugin.json` と `.claude-plugin/marketplace.json`(`plugins[0].version`)の **両方** をパッチバージョンアップ(変更前: `0.31.1` → 適切な次のパッチ。ただし他プランが先にマージ済みなら現行値から +1)。
115+
116+
**Verify**: `grep '"version"' .claude-plugin/plugin.json .claude-plugin/marketplace.json` → 両方が同一の新バージョン
117+
118+
## Test plan
119+
120+
新規テストは不要(静的解析の追加)。既存 2 スイートの全パスが回帰ゲート。
121+
122+
## Done criteria
123+
124+
- [ ] `shellcheck scripts/*.sh hooks/*.sh skills/claude-collab/scripts/*.sh` が exit 0
125+
- [ ] `bash scripts/test-helpers.sh` → 89 passed, 0 failed
126+
- [ ] `bash skills/claude-collab/scripts/test-claude-helpers.sh` → passed
127+
- [ ] `.github/workflows/ci.yml` に Shellcheck ステップが存在する(`grep -n 'shellcheck' .github/workflows/ci.yml` がヒット)
128+
- [ ] 2つの version ファイルが同一の新バージョン
129+
- [ ] In scope 外のファイルに変更がない(`git status` で確認)
130+
- [ ] `plans/README.md` のステータス行を更新
131+
132+
## STOP conditions
133+
134+
以下の場合は停止して報告すること(改変しない):
135+
136+
- ベースラインの指摘が 100 件を超える(disable 乱発になるため、方針判断をオペレーターに戻す)。
137+
- 指摘修正によりテストが失敗し、2回の修正試行で解消しない。
138+
- `codex_strip_ansi`(`scripts/codex-helpers.sh:134-140`)の sed パターン書き換えが必要に見える場合(このパターンは意図的。disable で対応すること。書き換えが避けられないと判断したら STOP)。
139+
140+
## Maintenance notes
141+
142+
- 以後、新しい `.sh` を scripts/・hooks/・skills/*/scripts/ に追加したら CI の shellcheck 対象 glob が拾うか確認すること(glob を `find` ベースに広げるのは将来の改善余地)。
143+
- Plan 004(ヘルパー堅牢化)はこのプランの後に実行すると、変更が自動的に lint される。
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# Plan 002: PreToolUse フック enforce-skill-usage.sh にテストを追加する
2+
3+
> **Executor instructions**: Follow this plan step by step. Run every
4+
> verification command and confirm the expected result before moving to the
5+
> next step. If anything in the "STOP conditions" section occurs, stop and
6+
> report — do not improvise. When done, update the status row for this plan
7+
> in `plans/README.md`.
8+
>
9+
> **Drift check (run first)**: `git diff --stat b5a434c..HEAD -- hooks/ .github/workflows/ci.yml`
10+
> If any in-scope file changed since this plan was written, compare the
11+
> "Current state" excerpts against the live code before proceeding; on a
12+
> mismatch, treat it as a STOP condition.
13+
14+
## Status
15+
16+
- **Priority**: P1
17+
- **Effort**: S
18+
- **Risk**: LOW
19+
- **Depends on**: none
20+
- **Category**: tests
21+
- **Planned at**: commit `b5a434c`, 2026-06-12(初版は `f5abf51`/2026-06-11。Plan 001 マージ(PR #56)によるドリフト — CI への shellcheck ステップ追加とフックへの disable コメント1行 — を反映して refresh 済み)
22+
23+
## Why this matters
24+
25+
`hooks/enforce-skill-usage.sh` は「ヘルパー関数はスキル経由でのみ実行する」というこのプラグインの唯一のガードレールだが、テストが一切ない(`scripts/test-helpers.sh` はヘルパー関数のみを対象とし、フックへの言及はゼロ)。このフックは jq 不在・JSON 不正時に fail-open(exit 0 = 許可)する設計のため、パターンマッチの regression が起きても**静かに無効化されるだけ**で誰も気づかない。過去に v0.31.1 で LF 改行問題の修正が入っており(コミット `ae17fc5`)、フックは実際に壊れた前歴がある。少数のテーブル駆動テストで回帰を恒久的に防げる。
26+
27+
## Current state
28+
29+
- `hooks/enforce-skill-usage.sh`(44行)— PreToolUse command フック。プロトコル: stdin に JSON(`.tool_input.command`)、exit 0 = 許可、exit 2 = ブロック(stderr がユーザーに表示される)。
30+
31+
全文の要点(`hooks/enforce-skill-usage.sh:11-30`):
32+
33+
```bash
34+
# Require jq for JSON parsing; fail open if unavailable
35+
command -v jq &>/dev/null || exit 0
36+
37+
# Read and extract command from tool input
38+
COMMAND=$(jq -r '.tool_input.command // empty' 2>/dev/null) || exit 0
39+
[ -z "$COMMAND" ] && exit 0
40+
41+
# Skill context marker — allow everything
42+
echo "$COMMAND" | grep -qF 'CODEX_SKILL_CONTEXT=1' && exit 0
43+
44+
# Check for codex-collab patterns
45+
PATTERN='\bcodex_[A-Za-z0-9_]+\b'
46+
PATTERN="$PATTERN"'|\bsource\b.*codex-helpers\.sh'
47+
PATTERN="$PATTERN"'|\.[ \t]+.*codex-helpers\.sh'
48+
# shellcheck disable=SC2016 # $HELPERS is a literal grep pattern (single-quoted intentionally, not a variable)
49+
PATTERN="$PATTERN"'|\$HELPERS.*codex-helpers'
50+
PATTERN="$PATTERN"'|HELPERS=.*codex-helpers'
51+
PATTERN="$PATTERN"'|\bCODEX_PROMPT\b'
52+
53+
if echo "$COMMAND" | grep -qE "$PATTERN"; then
54+
...
55+
exit 2
56+
fi
57+
exit 0
58+
```
59+
60+
- テストハーネスの規約: `scripts/test-helpers.sh` 冒頭に `pass()` / `fail()` / `skip()`(色付き echo + カウンタ)が定義され、最後に `Results: N passed, M failed, K skipped` を出力し、failed > 0 なら exit 1。新テストはこのパターンを踏襲すること(実装前に `scripts/test-helpers.sh` の先頭 60 行を読むこと)。
61+
- CI(`.github/workflows/ci.yml`)の `test` ジョブは現在 2 ステップ: `bash scripts/test-helpers.sh``bash skills/claude-collab/scripts/test-claude-helpers.sh`
62+
63+
## Commands you will need
64+
65+
| Purpose | Command | Expected on success |
66+
|---------|---------|---------------------|
67+
| 新テスト実行 | `bash hooks/test-enforce-skill-usage.sh` | 全テスト pass、exit 0 |
68+
| 既存テスト | `bash scripts/test-helpers.sh` | `Results: 89 passed, 0 failed, 0 skipped` |
69+
| jq 確認 | `command -v jq` | パスが表示される(ubuntu-latest にはプリインストール) |
70+
71+
## Scope
72+
73+
**In scope**:
74+
- `hooks/test-enforce-skill-usage.sh`(新規作成)
75+
- `.github/workflows/ci.yml` — test ジョブへの 1 ステップ追加のみ
76+
- `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json` — バージョン更新のみ
77+
78+
**Out of scope**:
79+
- `hooks/enforce-skill-usage.sh` 本体の変更(テストが現状の挙動を固定するのが目的。フックの挙動が「おかしい」と感じても変更しない — それは別プランの仕事)
80+
- `scripts/test-helpers.sh`
81+
82+
## Git workflow
83+
84+
- Branch: `feat/hook-tests`
85+
- conventional commits 形式(例: `feat: add tests for enforce-skill-usage hook (vX.Y.Z)`
86+
- push / PR 作成はオペレーターの指示があるまで行わない。
87+
88+
## Steps
89+
90+
### Step 1: テストファイルを作成する
91+
92+
`hooks/test-enforce-skill-usage.sh` を新規作成。`scripts/test-helpers.sh` の pass/fail ハーネスパターンを最小限で複製し、ヘルパー関数 `run_hook()` を定義する:
93+
94+
```bash
95+
HOOK="$(cd "$(dirname "$0")" && pwd)/enforce-skill-usage.sh"
96+
97+
# run_hook "<command string>" → フックを実行し exit code を返す
98+
run_hook() {
99+
printf '{"tool_input":{"command":%s}}' "$(printf '%s' "$1" | jq -Rs .)" \
100+
| bash "$HOOK" >/dev/null 2>&1
101+
echo $?
102+
}
103+
```
104+
105+
以下のケースを必ず含める(期待 exit code 付き):
106+
107+
| # | 入力 command | 期待 |
108+
|---|--------------|------|
109+
| 1 | `ls -la` | 0(無関係なコマンドは許可) |
110+
| 2 | `codex_run_exec prompt.txt` | 2(ヘルパー直接呼び出しをブロック) |
111+
| 3 | `source scripts/codex-helpers.sh` | 2 |
112+
| 4 | `. ./scripts/codex-helpers.sh` | 2 |
113+
| 5 | `HELPERS=scripts/codex-helpers.sh` | 2 |
114+
| 6 | `export CODEX_SKILL_CONTEXT=1; codex_run_exec x` | 0(スキルコンテキストマーカーで許可) |
115+
| 7 | `echo $CODEX_PROMPT` | 2(CODEX_PROMPT パターン) |
116+
| 8 | 空 JSON `{}`(command なし) | 0(fail open) |
117+
| 9 | 不正 JSON `not-json` | 0(fail open) |
118+
| 10 | `codex exec -s read-only - < p.txt` | 0(`codex_` プレフィックスなしの codex CLI 自体はブロックされない) |
119+
| 11 | `gh pr create --body-file body.md --title "fix codex_strip_ansi"` | 2(**既知の誤検知の固定化**。引数テキスト中の関数名への単なる言及でも `\bcodex_\w+\b` がマッチしブロックされる。2026-06-12 に実際の PR 作成で発生を確認済み) |
120+
121+
ケース 8・9 は `run_hook` を使わず生の文字列を直接 `bash "$HOOK"` にパイプすること。
122+
123+
ケース 11 は望ましい挙動ではなく**現状の挙動**を固定する characterization テスト。期待値を 0 に書き換えてフック側を「修正」しないこと(パターン改善は本プランのスコープ外 — Maintenance notes 参照)。テストコードには `# NOTE: 既知の誤検知を固定化(パターン改善時はこの期待値を 0 に変える)` のコメントを付ける。
124+
125+
**Verify**: `bash hooks/test-enforce-skill-usage.sh` → 全ケース pass、exit 0
126+
127+
### Step 2: CI に組み込む
128+
129+
`.github/workflows/ci.yml``test` ジョブ末尾に追加:
130+
131+
```yaml
132+
- name: Run hook tests
133+
run: bash hooks/test-enforce-skill-usage.sh
134+
```
135+
136+
**Verify**: `python3 -c "import yaml; yaml.safe_load(open('.github/workflows/ci.yml'))"` → exit 0
137+
138+
### Step 3: バージョンを更新する
139+
140+
`.claude-plugin/plugin.json` と `.claude-plugin/marketplace.json`(`plugins[0].version`)の両方をパッチバージョンアップ。
141+
142+
**Verify**: `grep '"version"' .claude-plugin/plugin.json .claude-plugin/marketplace.json` → 両方同一値
143+
144+
## Test plan
145+
146+
このプラン自体がテスト追加。上表 11 ケースが最低ライン。構造は `scripts/test-helpers.sh` のハーネス(pass/fail カウンタ + 最終サマリ + failed>0 で exit 1)に合わせる。
147+
148+
## Done criteria
149+
150+
- [ ] `bash hooks/test-enforce-skill-usage.sh` → 11 ケース以上すべて pass、exit 0
151+
- [ ] テスト 8・9(fail-open)とテスト 11(誤検知の characterization)が含まれている
152+
- [ ] `bash scripts/test-helpers.sh` → 89 passed(回帰なし)
153+
- [ ] CI に `Run hook tests` ステップが存在
154+
- [ ] 2つの version ファイルが同一の新バージョン
155+
- [ ] `hooks/enforce-skill-usage.sh` 本体に diff がない(`git diff hooks/enforce-skill-usage.sh` が空)
156+
- [ ] `plans/README.md` のステータス行を更新
157+
158+
## STOP conditions
159+
160+
- ケース 1〜10 のいずれかで、フックの実際の挙動が上表の期待値と異なる場合(= フック本体のバグ発見)。テストを期待値側に書き換えて誤魔化さず、STOP して実挙動を報告すること。
161+
- jq がローカルにない場合はインストール(`sudo apt-get install -y jq`)し、それも不可なら STOP。
162+
163+
## Maintenance notes
164+
165+
- フックの `PATTERN` に新パターンを追加する際は、このテストにケースを追加するのが規約となる。
166+
- ケース 6 が示すとおり、マーカー文字列はコマンド内のどこにあっても許可になる。これはスキル設計上の意図(LLM への誘導であり、セキュリティ境界ではない — `docs/bash-usage.md` 参照)。テストで「バイパス可能」を発見扱いしないこと。
167+
- **既知の誤検知(ケース 11)**: `\bcodex_[A-Za-z0-9_]+\b` はコマンドの**引数テキスト**に関数名が現れただけでマッチする。実例: 2026-06-12、PR #56 作成時に PR 本文へ `codex_strip_ansi` 等を含む `gh pr create --body "..."` がブロックされた(`--body-file` で回避)。パターン改善(例: `^gh |^git ` で始まるコマンドの除外、またはヘルパー名が「実行位置」にある場合のみマッチさせる)は挙動変更なので本プランのスコープ外。改善する場合は別プランを起こし、ケース 11 の期待値を 0 に反転させること。

0 commit comments

Comments
 (0)