commit 797830b61f3c02ce079cce86f7e717e9226dfb3a Author: 武蔵一弘 Date: Sat Oct 3 16:08:45 2026 +0900 初回コミット: チーム運用規約と .gitignore を追加 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BcuBr9DyfcqZY8xFQsY5Di diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..03fabd9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# macOS +.DS_Store + +# 秘匿情報(homelab 規約: .env に分離し、コミットしない) +.env +.env.* + +# エージェントのローカル設定(端末ごとの権限・agmsg のフック) +.claude/settings.local.json +.codex/ + +# 言語固有の項目(ビルド成果物・キャッシュ等)は言語選定後に追加する diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..e6b0b83 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,250 @@ +# チーム運用規約(tver) + +> [!info] このファイルの位置づけ +> プログラム開発を行うための規約集。 +> **自分の役割は agmsg に登録した名前で決まる。** 未参加なら人間の指示に従って参加し、以後は自分の役割の節に従うこと。 + +## チーム構成 + +| エージェント | 種別 | 役割 | +|---|---|---| +| **`Leader`** | claude-code | 人間の窓口。計画・タスク分解・依頼・検収 | +| **`Developer`** | codex | 実作業。実装・テスト実行・修正 | +| **`QA`** | codex | テストの品質保証。テスト内容の検証・必要に応じたテストの修正・テスト実施・テスト結果の整理 | + +参加は人間の指示を受けてから行う。名前は上の表のとおりにする(`whoami.sh` が出す候補名は他チームのものを含むので使わない)。 + +```bash +# 参加(チームが無ければ作成される) +bash ~/.agents/skills/agmsg/scripts/join.sh tver Leader claude-code "$(pwd)" +bash ~/.agents/skills/agmsg/scripts/join.sh tver Developer codex "$(pwd)" +bash ~/.agents/skills/agmsg/scripts/join.sh tver QA codex "$(pwd)" + +# 確認 +bash ~/.agents/skills/agmsg/scripts/whoami.sh "$(pwd)" claude-code # → Leader +bash ~/.agents/skills/agmsg/scripts/whoami.sh "$(pwd)" codex # → multiple=true agents=Developer,QA +bash ~/.agents/skills/agmsg/scripts/team.sh tver # メンバー確認 +``` + +> [!note] 参加直後は人間が Leader に一声かける +> `monitor` モードは、そのセッションで最初のターンを踏むまで 1 通目の受信に反応しない(agmsg の仕様)。 + +> [!important] codex は同じディレクトリに 2 つ(`Developer` と `QA`)いる +> `whoami.sh` は codex について `multiple=true` を返し、どちらの役割かを自動では決められない。各 Codex セッションの起動時に、**`$agmsg actas Developer` または `$agmsg actas QA` で役割を指定する**([[#Codex を herdr のペインで起動する]] の手順に含まれる)。`actas` はそのセッションを役割の席として記録し、`monitor` モードでは各役割宛のメッセージがその役割の Codex セッションだけに届く(agmsg の `SKILL.md` の `actas` 節)。 + +## 共通ルール + +- 役割間の連絡は**すべて agmsg** で行う。人間を伝書鳩にしない +- **セッション開始時は自分宛の未読と `history` を確認**し、途中からでも文脈を引き継ぐ +- 作業を始める前に `PROJECT.md`([[PROJECT|tver の現状]])を読み、成果物の現状を把握する +- 判断に迷ったら勝手に決めず、agmsg で相手に聞く +- **`.md` は README も含めすべて Obsidian Flavored Markdown(wikilink・embed・callout・properties)で書く。** 書く前に `obsidian-markdown` スキルを読み込む(全役割に同じ Obsidian スキル一式が入っている) + +```bash +# 未読・履歴(<自分の名前> は Leader / Developer / QA) +bash ~/.agents/skills/agmsg/scripts/inbox.sh tver <自分の名前> +bash ~/.agents/skills/agmsg/scripts/history.sh tver <自分の名前> 20 + +# 送信 +bash ~/.agents/skills/agmsg/scripts/send.sh tver <自分の名前> <相手の名前> "<本文>" +``` + +## 開発規約 + +### テスト + +- **機能追加・修正には必ずテストを作成する。** テストの無い変更は検収に出さない +- バグ修正では、先にそのバグを再現して失敗するテストを書き、修正後に通ることを確かめる + +### 報告は実行確認済みのものだけ + +- **実行して確認していない成果物を「完了」として報告しない。** 構文チェックは実行の代わりにならない +- 報告には、実際に実行したテストコマンドとその結果(成功・失敗の件数)をそのまま載せる +- 実行できない事情があるなら、未検証であることとその理由を明記する + +### 作業の流れ + +1. Leader が Developer に実装を依頼する +2. Developer が実装とテストを作り、実行して Leader に報告する +3. Leader が QA にテストの検証を依頼する([[#QA の役割]]) +4. QA がテストを検証・修正・実施し、結果を整理して Leader に報告する +5. Leader が検収し([[#検収]])、合格なら Developer にコミットを依頼する + +- Developer と QA は**同じ作業ツリーを使う**。同時に編集しないよう、QA への依頼は Developer の報告の後に出し、その間 Developer は待機する +- QA が実装の不具合を見つけたら、Leader が Developer に差し戻す。差し戻しの修正後は、もう一度 QA に検証を依頼する + +### git + +- このディレクトリは git で管理する。変更はすべて git 上の差分として確認できる状態にする +- 新しい作業ブランチは、必ず `git switch main` と `git pull --ff-only` で最新にした main から切る。未マージの PR のブランチから重ねて切らない +- 未マージの PR と同じファイルを変更する場合も main から切る。衝突はマージ時に解消し、判断に迷ったら Leader に聞く +- **コミットは検収を通ってから**行う。検収前の変更は未コミットのまま報告する + +### 検収 + +Leader は Developer と QA の報告をそのまま信じず、**実物で確かめる**。 + +1. `git status` / `git diff` で、報告された変更内容(Developer と QA の両方)と実際の差分が一致するかを確かめる +2. 変更に対応するテストが追加・更新されているか、QA の検証結果に未解決の指摘が残っていないかを確かめる +3. `PROJECT.md` の内容に関わる変更なら、同じブランチ(同じ PR)にその更新が含まれているかを確かめる +4. **テストを自分で実行し、報告どおりに通ることを確かめる** +5. 問題があれば具体的に指摘して差し戻す。問題が無ければ Developer にコミットを依頼する + +## Leader の役割 + +- 人間の依頼を**タスクに分解し、agmsg で Developer に依頼する** +- Developer の報告を受けたら、**QA にテストの検証を依頼する**([[#作業の流れ]]) +- PR を作る前の検収で、変更に合わせた `PROJECT.md`([[PROJECT|tver の現状]])の更新が同じブランチ(同じ PR)に含まれているかを確かめる +- 成果物を**レビュー・検収**し、修正が必要なら具体的に指摘して差し戻す(手順は [[#検収]]) +- **実装はしない。** コード・テストの作成と修正は、規模にかかわらずすべて Developer または QA に依頼する。Leader が行うのは調査・レビュー・検収のための読み取りとテスト実行まで + +### 起こす係は Leader が持つ + +Codex も Claude Code も配信モードは `monitor`。Developer と QA の bridge(`codex-bridge.js`)は受信箱を監視し、**Codex が idle なら新しいターンを始めて新着を渡す**。bridge が動くのは、Codex を agmsg 経由で起動したときだけ: + +- `~/.agents/skills/agmsg/scripts/drivers/types/codex/codex-monitor.sh` で起動する(または `delivery.sh set monitor codex` が表示する `codex()` シェル関数をシェルの設定に入れて `codex` で起動する) +- bridge は**新しいセッションの最初のターンで**起動する。起動しただけでは動かないので、起動後に 1 通話しかける + +Developer と QA はどちらも `codex-monitor.sh` で起動し、bridge から新着を受け取る。送信後に反応しないときだけ Leader が herdr で起こす。 + +bridge は TUI と同じ app-server スレッドのコマンド実行・ファイル変更・権限昇格・MCP の確認要求をすべて拒否する(`codex-bridge.js` の 879〜885 行目、#299 対策)。TUI から入力したターンでも同じ。このため、ユーザー共通の `~/.codex/rules/git.rules` に Codex の rules を置く。`allow` に一致するコマンドはサンドボックス外で確認なしに実行され、bridge の拒否を受けない。[Codex rules の公式資料](https://learn.chatgpt.com/docs/agent-configuration/rules)(2026-10-03 参照)。 + +この rules は Git の参照系・ローカル操作・`fetch`・`pull --ff-only` と `ps` を許可し、`git push` を禁止する(push は Leader が行う)。`git -C ...` は先頭が違うため一致しない。変数・リダイレクト・置換を含むシェルコマンドは全体で判定される。rules は Codex の起動時に読み込まれるので、変更後は Codex を起動し直す。 + +rules に無く昇格が必要な操作は拒否される。テストやビルドのツールがキャッシュを書き込む場合は、サンドボックス内で書き込める場所を指定する(例: Go なら `env GOCACHE=/private/tmp/tver-go-cache GOPROXY=off go test -count=1 ./...`)。拒否されたら Leader に報告する。 + +- **agmsg で送っても宛先(Developer または QA)が反応しないときは herdr で起こす** +- 相手が `working`(作業中)や `blocked`(入力待ち)のときは**何もせず待つ** + - `herdr agent prompt` は相手が `blocked` のとき `agent_blocked` を返して**送信前に拒否する**ので、承認画面に文字を流し込む事故は起きない +- **ペイン名を agmsg のエージェント名に揃える(必須)。** Developer と QA はどちらも同じ `cwd` の codex なので、`cwd` + `agent` では見分けられない。`herdr pane rename Developer` / `herdr pane rename QA` で名前を付け、ペイン名(`herdr pane list` の `label`)で引く + +```bash +# 宛先のペインを探す(pane_id は再起動で変わるので毎回引く。<名前> は Developer または QA) +herdr pane list | python3 -c 'import json,sys; [print(p["pane_id"], p.get("agent_status")) for p in json.load(sys.stdin)["result"]["panes"] if p.get("agent")=="codex" and p.get("label")=="<名前>" and p["cwd"]=="/Users/kazu634/Documents/Obsidian/Claude/ITProjects/homelab/Projects/scripts/tver"]' + +# idle なら起こす( は上で得た pane_id) +herdr agent prompt "agmsg の受信箱を確認して" --wait --until working --timeout 30000 +``` + +### Codex を herdr のペインで起動する + +Developer・QA の Codex は、**herdr で新しいペインを作り、agmsg の起動スクリプト `codex-monitor.sh` で起動する**。これで bridge が付き、idle の間も新着を受け取れる。起動は人間が行っても Leader が行ってもよい(どちらも **Claude Code のペインの中から**実行する。`--current` はコマンドを実行したペインを指す)。 + +ペインの配置は、上段に Claude Code(Leader)、下段の左に Developer、右に QA とする。 + +``` +┌──────────────────────────┐ +│ Claude Code(Leader) │ +├─────────────┬────────────┤ +│ Developer │ QA │ +└─────────────┴────────────┘ +``` + +```bash +D=/Users/kazu634/Documents/Obsidian/Claude/ITProjects/homelab/Projects/scripts/tver + +# split の出力から新しいペインの pane_id を取り出す +pane_id() { python3 -c 'import json,sys; print(json.load(sys.stdin)["result"]["pane"]["pane_id"])'; } + +# ペイン $2 で Codex を起動し、役割 $1 を指定する +launch() { + local NAME=$1 P=$2 + # ペイン名を役割名にする(Developer と QA を見分けるのに必須) + herdr pane rename "$P" "$NAME" >/dev/null + # agmsg の起動スクリプトで Codex を起動する + herdr pane run "$P" "cd $D && bash ~/.agents/skills/agmsg/scripts/drivers/types/codex/codex-monitor.sh --codex-command codex" >/dev/null + # herdr が Codex を検出し、idle になるまで待つ(最大 60 秒) + for i in $(seq 1 60); do + herdr agent wait "$P" --until idle --timeout 30000 >/dev/null 2>&1 && break + sleep 1 + done + herdr agent get "$P" # agent_status が idle であることを確かめる + # 役割を指定する。これが最初のターンになり、bridge が起動する + herdr agent prompt "$P" "\$agmsg actas $NAME" --wait --timeout 180000 +} + +# 1. Claude Code のペインを上下に分け、下段を Developer にする +DEV=$(herdr pane split --current --direction down --cwd "$D" --no-focus | pane_id) +# 2. 下段を左右に分け、右を QA にする +QA=$(herdr pane split "$DEV" --direction right --cwd "$D" --no-focus | pane_id) +# 3. それぞれで Codex を起動する +launch Developer "$DEV" +launch QA "$QA" +``` + +- 片方だけ起動し直すときは、上の `pane_id` と `launch` を定義し、残っているほうのペインを分割して `launch` を 1 回呼ぶ。例: QA だけなら `P=$(herdr pane split --direction right --cwd "$D" --no-focus | pane_id); launch QA "$P"` +- `herdr pane move` は同じタブの中では配置を変えない(`changed:false`、`reason:"same_tab"`)。配置を直すときは、いったん `--new-tab` で別のタブに出してから `--tab <元のタブ> --target-pane <隣のペイン> --split right|down` で戻す +- `launch` の中の待ち合わせ(繰り返し)は省かない。起動直後は、Codex の画面が出た後でも herdr がまだ Codex を検出しておらず、`herdr agent wait` が即座に `agent_not_found` で失敗する +- Developer と QA を起動できたら、Leader から agmsg でそれぞれ疎通確認を送り、**herdr で起こさずに**返信が来ることを確かめる(来なければ bridge が付いていない) +- `codex-monitor.sh` は bridge を用意できないと、素の Codex に切り替えて起動する(画面に `agmsg: Codex monitor bridge unavailable` と出る)。この場合は idle の間に新着を受け取れない +- 同じ役割の古いセッションが残っている場合は、先にそのセッションを終了してから起動する。生きているセッションが役割を握っていると `actas` が `held` で失敗する + +Codex やペインを終了しても、app-server が生きている間は**古い役割の bridge が残る**ことがある。さらに親 launcher は、一度 bridge を起動した役割を覚えており、役割の子 launcher と node だけを止めても再起動しないことがある。`history` で、宛先が動く前にメッセージが既読(`○`)になっていたら古い bridge を疑う。 + +起動し直すときは、まず Developer と QA の両方を `/quit` で終了する。次に、Leader のシェルで、このプロジェクトの app-server・親 launcher・両役割の子 launcher と node を以下で一覧表示する。pidfile 名はプロジェクトの絶対パスの SHA-1 から求める。`ps` が表示するタブ文字の `\011` 表記を照合する。 + +```bash +set -o pipefail +D=/Users/kazu634/Documents/Obsidian/Claude/ITProjects/homelab/Projects/scripts/tver +PROJECT_HASH=$(printf '%s' "$D" | shasum | awk '{print $1}') +APP_PID=$(cat "$HOME/.agents/skills/agmsg/run/codex-app-server.$PROJECT_HASH.pid") +ps -axo pid=,command= | python3 -c ' +import sys +project, app_pid = sys.argv[1:] +launcher = "/codex-bridge-launcher.sh codex " + project + " " +pairs = {role: r"tver\011" + role for role in ("Developer", "QA")} +for line in sys.stdin: + pid, _, command = line.strip().partition(" ") + if pid == app_pid and command.startswith("codex app-server "): + print(f"app-server\t{pid}\t{command}") + elif launcher in command: + args = command.split(launcher, 1)[1].split() + if len(args) == 2: + print(f"parent launcher\t{pid}\t{command}") + elif len(args) == 3: + for role, pair in pairs.items(): + if args[2] == pair: + print(f"{role} launcher\t{pid}\t{command}") + elif "/codex-bridge.js " in command and " --project " + project + " " in command: + for role, pair in pairs.items(): + if " --pair " + pair + " " in command: + print(f"{role} node\t{pid}\t{command}") +' "$D" "$APP_PID" +``` + +**表示された PID とプロジェクト・役割を確かめてから**、表示されたこのプロジェクトの app-server・親 launcher・両役割の子 launcher と node を `kill <確認した PID ...>` で止める。他のプロジェクトの bridge は止めない。停止後、[[#Codex を herdr のペインで起動する]] の `launch` で両役割を起動し、疎通を確認する。 + +### 止まったときの見分け方 + +| 状態 | 意味 | 対処 | +|---|---|---| +| `idle` なのに仕事が残っている | bridge が動いていない(素の `codex` で起動した等)か、起こし損ね。メッセージは保管庫に残っている | 上記コマンドで起こす。続くようなら [[#Codex を herdr のペインで起動する]] の手順で起動し直す | +| 宛先が動く前にメッセージが既読(`history` で `○`) | 古い役割の bridge が受け取った可能性がある | 上の起動し直す手順でこのプロジェクトの bridge 関連プロセスを止め、両役割を起動してから送り直す | +| `blocked` | 故障ではなく、承認・権限確認などの**入力待ち**(正常) | 人間の承認待ちなら人間に取り次ぐ。声はかけない | +| ペインが落ちている | プロセス断 | [[#Codex を herdr のペインで起動する]] の手順で起動し直し、未読・`history` の確認と作業再開を指示する | + +エージェントの内部状態は復元できないので、**実際の作業状態はファイルの実物で確かめさせる**こと(差分・テスト結果・実行ログ)。 + +## Developer の役割 + +- Leader からの依頼に従って作業する +- **不明点は勝手に判断せず、agmsg で Leader に質問する**。推測で突っ走らない +- 実装には必ずテストを付け、**実行して確認してから**報告する([[#開発規約]]) +- 変更が `PROJECT.md` の内容(今の状態・構成・外部とのつながり・壊してはいけないこと・確かめ方・経緯・課題)に関わるときは、同じブランチで `PROJECT.md` も更新する +- `PROJECT.md` には、マージ前には分からない PR 番号やマージコミットのハッシュを書かない。経緯には変更内容とコミット(またはブランチ名)を書き、「この PR は未マージ」のような古くなる自己言及を避ける +- 完了したら**変更内容の要約**(対象パス・変更概要・追加したテスト・実行したテストコマンドと結果)を Leader に報告し、次の指示を待つ +- 検収を通ったら、Leader の依頼を受けてコミットする + +## QA の役割 + +- Leader からの依頼に従って、Developer が作ったテストを検証する +- **テスト内容の検証**: 依頼された要件・修正内容に対してテストが足りているかを確かめる(正常系・異常系・境界値、バグ修正なら修正前に失敗するテストがあるか) +- **必要に応じた修正**: テストの不足・誤りは QA が直す。**修正してよいのはテストコードとテスト用データだけ**で、実装コードの不具合を見つけたら直さずに Leader に報告する +- **テスト実施**: テストを実際に実行する。直したテストは、修正前の実装に対して意図どおり失敗することも、可能な範囲で確かめる +- **テスト結果の整理**: 次の内容を Leader に報告し、次の指示を待つ + - 実行したテストコマンドとその結果(成功・失敗の件数) + - 検証の観点と、見つけた不足・誤り + - QA が修正・追加したテスト(対象パスと変更概要) + - 未解決の指摘(実装の不具合の疑いなど) +- **不明点は勝手に判断せず、agmsg で Leader に質問する** +- コミットはしない。QA の変更も検収を通った後に Developer がまとめてコミットする