From 92529cd2075e0e7487490647a912283788482f36 Mon Sep 17 00:00:00 2001 From: ozzy-3 Date: Sun, 17 May 2026 22:57:51 +0900 Subject: [PATCH] chore: sync skills to 27ae9621 ozzy-labs/skills@27ae96210fee135e608b16e938cb338c9b2b45ae --- .agents/skills/drive/SKILL.md | 166 +++++++++++++++++++++++++++++++-- .agents/skills/health/SKILL.md | 35 ++++++- .claude/skills/drive/SKILL.md | 6 +- .commons/sync.yaml | 2 +- 4 files changed, 198 insertions(+), 11 deletions(-) diff --git a/.agents/skills/drive/SKILL.md b/.agents/skills/drive/SKILL.md index 1eb1dfc..8f2e58f 100644 --- a/.agents/skills/drive/SKILL.md +++ b/.agents/skills/drive/SKILL.md @@ -102,6 +102,7 @@ review skill の観点別 `exit_criteria.drive_loop` を集約して終了判定 `--merge` 指定時に実行する。 1. **Auto-merge の有効化:** `gh pr merge --auto --squash --delete-branch` を実行する + - **subagent として単一モードを実行する場合は `--delete-branch` を省略する:** 自 worktree が当該 branch を握っているため `fatal: '' is already used by worktree at ...` エラーになる。ローカル branch / worktree の整理は親側 Phase Final で一括処理する([Issue #69](https://github.com/ozzy-labs/skills/issues/69)) 2. **成否の確認:** - 成功(Auto-merge がセットされた、または即時マージされた)→ 次へ - 失敗(Auto-merge がリポジトリで無効など)→ ユーザーに通知し、手動マージを促す(状態を `merge-ready` にする) @@ -169,6 +170,8 @@ wave を順に実行する。 - **隔離:** worktree 隔離で起動する(必須。並列実行時の作業ディレクトリ衝突防止) - **委譲粒度:** subagent には `.agents/skills/drive/SKILL.md` を Read させ、target #N について単一モードのワークフロー(Phase 1-5)を実行するよう指示する。slash command は subagent からは呼べないため、SKILL.md を直接実行する - **main への checkout 禁止:** subagent は自 worktree branch で完結する。`git checkout main` / `git switch main` / `git checkout HEAD~` 等で worktree の HEAD を移動させない。worktree は親の Phase Final で削除されるため、main へ戻す必要はない。共有 git directory 経由で親 worktree の `HEAD` / `index` が汚染されるリスクを避けるため、自 branch 以外を触らないこと +- **`--delete-branch` 禁止:** subagent が `gh pr merge` を呼ぶ際、`--delete-branch` フラグは使わない。自 worktree が当該 branch を握っているため `fatal: '' is already used by worktree at ...` エラーになり、リモート merge は成功するがローカル branch が中途半端に残る。ローカル branch / worktree の整理は親の Phase Final で一括処理する([Issue #69](https://github.com/ozzy-labs/skills/issues/69)) +- **scope 外波及の最低限チェック:** 自 issue で schema enum / field / CLI flag を追加した場合、リポ全体で対応する help 文字列・エラーメッセージ・サンプル/docs を grep し、同期されているか確認する(例: `rg -n '' src/ docs/`)。同期されていなければ **可能なら自 PR に含める**(自 scope の自然な拡張として)。判断に迷う / 自 scope を明確に超える場合は修正せず、戻り値 `cross_cutting_gaps` に `: not synced` 形式で記録し、親の Phase Final-3 audit に集約する([Issue #70](https://github.com/ozzy-labs/skills/issues/70)) - **ベースブランチ:** - 依存元 wave がない target → main からブランチを作る - 依存元 wave がある target → 依存元 PR の `headRefName` をベースにブランチを作る(stacked PR)。`--merge` 指定時は依存元がマージ済みのため main をベースにできるが、未指定時はこの stacked 構造が必須 @@ -189,10 +192,16 @@ wave を順に実行する。 "total": {"critical": 0, "warning": 0, "info": 0}, "iterations": }, + "cross_cutting_gaps": [ + "src/cli/foo.ts:213 — help text missing new kind 'html-js'", + "src/cli/foo.ts:299 — validation error message lists old enum set" + ], "error": "" } ``` +`cross_cutting_gaps` は subagent が「scope 外波及の最低限チェック」で気付いたが自 PR では修正しなかった項目を記録する任意フィールド(空配列でも可)。フィールドが欠落している戻り値も後方互換のためエラーにせず、`[]` として扱う。親は Phase Final-3 post-merge audit でこれを集約し、独自検出した gap と統合して warning として list-up する。 + #### 観測性 - `Agent` tool は subagent 完了時に最終結果のみを返すため、ストリーム的な中間報告は不可 @@ -220,7 +229,11 @@ wave を順に実行する。 ### Phase Final: 集約レポート -集約レポートを出力する前に、**親 worktree の整合性を確認する**。subagent が共有 git directory 経由で親の `HEAD` / `index` を汚染するケースに備えるための fail-safe([Issue #66](https://github.com/ozzy-labs/skills/issues/66) 由来)。 +Phase Final は次の 3 ステップで構成する。順に実行する。 + +#### Phase Final-1: 親 worktree 整合性チェック + +subagent が共有 git directory 経由で親の `HEAD` / `index` を汚染するケースに備えるための fail-safe([Issue #66](https://github.com/ozzy-labs/skills/issues/66) 由来)。 1. `git rev-parse HEAD` と `git rev-parse $(git symbolic-ref HEAD)` が一致するか(HEAD が detached でないこと) 2. `git diff HEAD --stat` が空か(index が HEAD と乖離していないか) @@ -241,7 +254,142 @@ wave を順に実行する。 git reset --hard origin/main ``` -整合性チェックが通った場合は、通常どおり集約レポートを出力する: +#### Phase Final-2: subagent worktree cleanup + +**今回の drive 実行で起動した subagent** の worktree と関連 local branch をクリーンアップする([Issue #69](https://github.com/ozzy-labs/skills/issues/69) 由来)。今回の実行外の orphan worktree (前回の異常終了で残ったもの等) は対象外。orphan の検出・整理は `/health` 領域 #7 に委譲する([Issue #71](https://github.com/ozzy-labs/skills/issues/71))。 + +1. 今回起動した subagent のリストを保持する。各 subagent の worktree パス(`.claude/worktrees/agent-/`)と戻り値 `status` をひとまず控える +2. 各 subagent について `status` を参照して扱いを分岐: + - **`merged`**: cleanup 対象(リモート merge 完了済み、ローカル iterate 不要) + - `git worktree list --porcelain` で当該 worktree が握っている branch を取得する(パターンマッチに頼らない) + - `git worktree remove -f -f ` を実行する(`-f -f` の二重 force は Claude Code harness の `lock` 解除のため必須) + - 取得した branch を `git branch -D ` で削除する + - **`auto-merge enabled`**: cleanup **しない**(後で実マージされるまで状態保留、マージ後にユーザーが手動 / `/health` で整理) + - **`merge-ready`**: cleanup **しない**(ユーザーが手動マージ前にローカル iterate する余地を残す) + - **`failed`**: cleanup **しない**(再実行で resume できるよう残置) +3. 補助的に `worktree-agent-` 形式の synthetic branch が残っていれば併せて `git branch -D` する(Claude Code harness 実装由来のパターン依存。検出失敗時は warning に留め fail しない) +4. cleanup 結果を集計し、Phase Final-3 の集約レポートに含める + +`merged` 以外で残置された worktree がある場合、または cleanup 自体に失敗した worktree がある場合、集約レポート末尾に warning を出す: + +```text +⚠️ Stale worktrees / branches detected: + preserved (not yet merged): + .claude/worktrees/agent- [] ← # auto-merge enabled; マージ後に手動削除 + .claude/worktrees/agent- [] ← # merge-ready; iterate 用に残置 + preserved (failed): + .claude/worktrees/agent- [] ← # failed; resume 可能 + cleanup failed: + .claude/worktrees/agent- [] reason: + Manual cleanup: + git worktree remove -f -f + git branch -D +``` + +#### Phase Final-3: post-merge audit (cross-cutting) + +複数 subagent が自 sub-issue scope に閉じて並列実行する結果、**scope を跨ぐ波及 (cross-cutting) が構造的に漏れる**ことがある(enum/field/CLI flag の help・エラーメッセージ・サンプルへの未反映、ステータス系文言の取り残し、lockfile drift 等)。集約レポート出力前に best-effort で検出する([Issue #70](https://github.com/ozzy-labs/skills/issues/70) 由来)。 + +検出された gap は **すべて warning 扱い**で集約レポート末尾に list-up し、ユーザーに follow-up PR の要否を問う。critical / info への severity 分類はしない(best-effort 性質に合わせシンプルに保つ)。 + +**前提**: Phase Final-2 cleanup 後の main ブランチで実施する(merged PR の変更が main に取り込まれている状態)。`merged` 以外(`auto-merge enabled` / `merge-ready` / `failed`)の subagent は対象外(main に未反映のため)。 + +各検査項目内の `gh pr diff` 呼び出しは独立しているため、複数 PR の diff 取得は並列実行してよい。 + +##### 0. subagent からの報告を集約 + +検査の起点として、各 subagent 戻り値の `cross_cutting_gaps` フィールドをすべて集約する。subagent が自前で気付き、自 PR では修正しなかった gap がここに含まれる([Issue #70](https://github.com/ozzy-labs/skills/issues/70) B2/B3)。 + +```text +subagent #N の cross_cutting_gaps: + - src/cli/foo.ts:213 — help text missing new kind 'html-js' + - ... +``` + +集約した gap は後続の独自検出と重複排除して最終的な warning list を構成する。重複判定キーは `file:line` を基本とし、同一 `file:line` で複数の異なる message がある場合は両方併記する(情報を捨てない)。 + +##### 1. cross-cutting symbol の同期確認 (heuristic) + +各 subagent の戻り値 `pr_number` に対し `gh pr diff ` で差分を取得し、新規追加された enum 値・field 名・CLI flag らしき symbol を heuristic に抽出する。例: + +```bash +# 追加された enum 値 / case 文 / object literal の値 を抽出 +gh pr diff | grep -E '^\+' | grep -oE '(case\s+["'\'']\w[\w-]+["'\''])|(--[a-z][a-z0-9-]+)|(["'\''][a-z][a-z0-9-]+["'\''])' | sort -u +``` + +抽出した symbol を repo 全体で grep し、help 文字列・エラーメッセージ・サンプル・docs に同期されているか確認する: + +```bash +rg -n --no-heading '' src/ docs/ +``` + +抽出に偽陽性は許容する(grep は AI 判断のサポートツール)。AI は抽出結果を見て「この symbol は CLI 層の help にも追加されるべきか」を判断し、未同期と思われるものを gap として list-up する。 + +##### 2. 古い文言の残骸検出 + +ステータス系 keyword (`alpha`, `beta`, `Phase \d+`, `pending`, `TODO`, `FIXME` 等) が merged PR 群で削除されている場合、同じ文字列が他ファイルに残骸として残っていないか確認する: + +```bash +# 各 merged PR で削除された status keyword 行を抽出 +for PR in ; do + gh pr diff $PR | grep -E '^-' | grep -iE '(alpha|beta|phase\s+[0-9]+|pending)' +done + +# 残骸を repo 全体で grep +rg -n --no-heading -iE '(alpha|beta|phase\s+[0-9]+|pending)' src/ docs/ README.md +``` + +固有名詞として正当な使用 (例: `alpha` がリリースチャネル名として残るべき) は AI が判断して除外する。 + +##### 3. lockfile drift + +`pnpm-lock.yaml` / `package-lock.json` / `yarn.lock` / `uv.lock` 等が merged PR で変更されているが対応する manifest (`package.json` / `pyproject.toml` 等) が変更されていない、またはその逆を検出する: + +```bash +# 今 drive 実行で merged になった PR の数だけ遡って変更ファイルを取得 +# (1 PR = squash merge で 1 commit のため、merged_pr_count == commit 数) +git diff --name-only origin/main~..origin/main +``` + +manifest と lockfile の対応関係を確認し、不整合があれば gap として list-up する。 + +##### 4. docs ⇄ code grep 整合 (スコープ縮小版) + +docs 系の merged PR (タイトルが `docs:` / `docs():` 等) の diff から追加された CLI 呼び出し文字列を抽出し、code 側に対応する文字列が存在するか grep で確認する。 + +```bash +# docs PR の追加された code block 内 CLI 文字列を抽出 +gh pr diff | grep -E '^\+' | grep -oE '`[a-z][a-z0-9-]+\s+[a-z][^`]*`|--[a-z][a-z0-9-]+' + +# code 側で対応する文字列を grep +rg -n --no-heading '' src/ +``` + +「実行ベース」検証(実際に ` --help` を実行して確認)は行わない(リポによって CLI 構成が違うため汎用化困難)。grep ベースの整合確認のみ。 + +##### 検出結果の出力 + +gap が検出されたら、Phase Final-4 集約レポート末尾に warning として追記する。gap が 0 件なら warning ブロックは出力せず、集計行 `cross-cutting:` のみ `none` 表示にする: + +```text +⚠️ Cross-cutting gaps detected: + enum/field/flag sync: + src/cli/foo.ts:213 — help text missing new kind 'html-js' (PR #N で追加) + src/cli/foo.ts:299 — validation error message lists old enum set (PR #N で追加) + stale status text: + src/cli/index.ts:52 — outdated "Status: alpha" (PR #M で他から削除済み) + lockfile drift: + pnpm-lock.yaml changed but package.json not changed in this drive run + docs/code mismatch: + docs/user-guide.md:42 references `--flag-x` but not found in src/cli/ + Recommended: follow-up PR(s) to fix +``` + +gap が 0 件の場合は warning ブロックを省略し、Phase Final-4 集計行のみ `cross-cutting: none` と表示する(ノイズ抑制)。 + +#### Phase Final-4: 集約レポート + +整合性チェック・worktree cleanup・post-merge audit の結果を踏まえ、集約レポートを出力する: ```text drive 完了 (3/5 merged, 1 merge-ready, 1 skipped): @@ -252,13 +400,17 @@ drive 完了 (3/5 merged, 1 merge-ready, 1 skipped): #5 refactor: ... | failed (test loop) 集計: - merged: 2 - merge-ready: 1 - skipped: 1 - failed: 1 - 総レビュー反復: 5 回 + merged: 2 + merge-ready: 1 + skipped: 1 + failed: 1 + 総レビュー反復: 5 回 + worktree cleanup: 2/5 removed (3 preserved: 1 merge-ready, 1 failed, 1 skipped) + cross-cutting: 2 gaps detected (warning) ``` +`cross-cutting:` 行は Phase Final-3 で gap が検出された場合は ` gaps detected (warning)` を表示し、詳細は前述の warning ブロックを参照する。gap が 0 件なら `cross-cutting: none` と表示し warning ブロックは出力しない。 + ## 失敗 semantics | 状況 | 扱い | downstream への影響 | diff --git a/.agents/skills/health/SKILL.md b/.agents/skills/health/SKILL.md index a46a3ee..3320e29 100644 --- a/.agents/skills/health/SKILL.md +++ b/.agents/skills/health/SKILL.md @@ -80,6 +80,9 @@ description: リポジトリ改修中に意図せず残る状態(working tree, - コマンド: `git branch -vv` および `git for-each-ref --sort=committerdate --format='%(refname:short) %(upstream:track) %(committerdate:relative)' refs/heads/`(古い順) - PR 検出(**1 度だけ batch 取得**): `gh pr list --state all --json number,state,mergedAt,headRefName --limit 100` を 1 回実行し、client side で local branch 名と `headRefName` を join する(branch ごとに gh を呼ばない) - 各 branch について: + - `worktree-agent-` パターン(Claude Code harness が作る合成 branch): + - 対応する `.claude/worktrees/agent-/` worktree が **存在する** → 推奨なし(使用中) + - 対応する worktree が **存在しない** → タグ `drive synthetic` を付与し、`prune`(`git branch -D `。親 worktree が削除済みの orphan synthetic branch。[Issue #71](https://github.com/ozzy-labs/skills/issues/71)) - merged 済みの PR が存在し、かつ merge base 以降に追加 commit が **ない** → `delete`(PR 番号を表示) - merged 済みの PR が存在し、かつ merge base 以降に追加 commit が **ある** → `要確認`(PR 番号と追加 commit 数を表示。merge 後に作業継続したケース) - upstream なし、かつ最終 commit から 14 日以上 → `要確認` @@ -89,6 +92,22 @@ description: リポジトリ改修中に意図せず残る状態(working tree, 「追加 commit の有無」の判定: PR の merge commit と local branch の `git rev-list --count ..` を比較し、結果が 0 なら追加なし、1 以上なら追加あり。 +`worktree-agent-` の id 部分は `.claude/worktrees/agent-/` の id と 1:1 対応する。判定手順: + +1. #7 worktree チェックで取得した `git worktree list --porcelain` 結果から、`.claude/worktrees/agent-/` パスの `` を抽出して集合を作る(例: `{a33c59a3504b6dc3e, a6658497dd970cd6d}`) +2. 各 local branch 名のうち `worktree-agent-` で始まるものから prefix を除いた残り部分を id 候補として抽出 +3. id 候補が手順 1 の集合に **含まれない** branch を orphan synthetic と判定する + +branch ごとに `git worktree list` を呼ばない(#7 の結果を使い回す)。 + +表示例: + +```text +#5 local branch: + worktree-agent-a33c59... drive synthetic, parent worktree missing → prune (git branch -D) + ci/playwright-... merged (PR #117) → delete +``` + #### 6. remote tracking - コマンド: `git remote prune origin --dry-run` @@ -97,7 +116,21 @@ description: リポジトリ改修中に意図せず残る状態(working tree, #### 7. worktree - コマンド: `git worktree list --porcelain` -- main worktree 以外を列挙する。関連 branch が merged または存在しない → `prune`、それ以外 → 推奨なし +- main worktree 以外を列挙する。`locked` 状態の worktree も漏らさず表示する(`git worktree list --porcelain` は `locked` も含めて出力するため通常通り扱えばよい) +- 各 worktree について判定: + - パスが `.claude/worktrees/agent-*` パターン → タグ `drive orphan` を付与し、`prune` 推奨(`/drive` skill の Phase Final-2 cleanup を通らなかった残骸の可能性。異常終了 / interrupt / 手動 kill 等) + - `locked` 状態 → タグ `locked` を併記。`prune` 推奨コマンドは `git worktree remove -f -f ` を提示する(`-f -f` の二重 force は Claude Code harness の `lock` 解除のため必須。[Issue #71](https://github.com/ozzy-labs/skills/issues/71)) + - 関連 branch が merged または存在しない → `prune` + - それ以外 → 推奨なし + +タグは推奨アクションとは別軸の **分類ラベル** として表示する(推奨アクション語彙は固定のまま)。表示例: + +```text +#7 worktree: + /home/.../repo/.claude/worktrees/agent-a33c59... [ci/playwright-...] drive orphan, locked → prune (git worktree remove -f -f) + /home/.../repo/.claude/worktrees/agent-a6658... [docs/html-js-...] drive orphan, locked → prune (git worktree remove -f -f) + /home/.../repo/../old-feature [feat/old] → prune +``` #### 8. submodule diff --git a/.claude/skills/drive/SKILL.md b/.claude/skills/drive/SKILL.md index 44699a2..8503e2e 100644 --- a/.claude/skills/drive/SKILL.md +++ b/.claude/skills/drive/SKILL.md @@ -38,6 +38,8 @@ allowed-tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, AskUser - **subagent_type:** `general-purpose` - **prompt:** subagent から slash command は呼べないため、`.agents/skills/drive/SKILL.md` を Read させ、target #N について単一モードのワークフロー(Phase 1-5)を実行するよう指示する。`--merge` 指定時は Phase 4 まで完了し、自 PR の merged まで polling して終了させる。最終結果は JSON で返させる - **main への checkout 禁止(必ず prompt に明記):** subagent は自 worktree branch で完結する。`git checkout main` / `git switch main` / `git checkout HEAD~` 等で HEAD を移動させない。worktree は親側で削除されるため main へ戻す必要はない。これを怠ると共有 git directory 経由で親 worktree の `HEAD` / `index` が汚染される([Issue #66](https://github.com/ozzy-labs/skills/issues/66) 参照) +- **`--delete-branch` 禁止(必ず prompt に明記):** subagent が auto-merge をセットする際、`gh pr merge --auto --squash` までに留め、`--delete-branch` は付けない。自 worktree が握る branch を削除しようとして `fatal: '' is already used by worktree at ...` エラーになる。ローカル branch / worktree の整理は親側 Phase Final で一括処理する([Issue #69](https://github.com/ozzy-labs/skills/issues/69)) +- **scope 外波及チェック(必ず prompt に明記):** subagent が enum / field / CLI flag を追加した場合、リポ全体で対応する help 文字列・エラーメッセージ・サンプル/docs を grep し、同期を確認する。同期されていなければ可能なら自 PR に含める。自 scope を明確に超える場合は戻り値 JSON の `cross_cutting_gaps: string[]` フィールドに `: not synced` 形式で記録し、親の Phase Final-3 audit に集約する([Issue #70](https://github.com/ozzy-labs/skills/issues/70)) - **依存元 wave がある場合のベースブランチ:** - `--merge` 指定 + 依存元が merged → main から作成 - `--merge` 指定 + 依存元が auto-merge enabled(未マージ)→ main を pull してから作成(取り込まれていれば main ベース、未取り込みなら依存元 headRefName ベース) @@ -74,5 +76,5 @@ allowed-tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch, AskUser 1. **`--merge` 指定時:** 各 subagent が自 PR のマージまで完了させているため、Phase Final 集約レポートを出力して終了する 2. **`--merge` 未指定時:** Phase Final レポート出力後、AskUserQuestion を呼び出す - - **「全 PR を一括マージする」** → 各 PR に対し順次 `gh pr merge --squash --delete-branch` を依存順に実行 - - **「個別に対応する」** → 終了する + - **「全 PR を一括マージする」** → 各 PR に対し順次 `gh pr merge --squash --delete-branch` を依存順に実行する。すべての PR が merged になった後、Phase Final-2 cleanup を **`merge-ready` だった subagent worktree に対して再度実行する**(マージ完了で cleanup 条件 `merged` を満たすようになるため)。cleanup 結果を追加レポートとして出力する + - **「個別に対応する」** → 終了する。`merge-ready` の worktree は残置されたまま。ユーザーがマージ後に `/health` 領域 #7 または手動で整理する diff --git a/.commons/sync.yaml b/.commons/sync.yaml index fd24362..0ea3741 100644 --- a/.commons/sync.yaml +++ b/.commons/sync.yaml @@ -2,7 +2,7 @@ # 'pinned' is user-editable — add or remove paths freely commit: 5d2e324b9caa3d8501aa3599ac3ad332f749a28e synced_at: 2026-05-06T09:21:36Z -skills_commit: 6e6c81a65b80ad46fd4e06d6a786b73b54594f70 +skills_commit: 27ae96210fee135e608b16e938cb338c9b2b45ae skills_adapters: - claude-code - codex-cli