From 9119e82145d34aa5b9601d87fc2768030e34fb5b Mon Sep 17 00:00:00 2001 From: fla9ua Date: Sun, 21 Jun 2026 00:29:40 +0900 Subject: [PATCH] =?UTF-8?q?=E6=A9=9F=E8=83=BD:=20=E3=82=B2=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=82=92=E5=AE=9F=E6=A9=9F=E5=88=A4=E5=AE=9A=E3=81=AB?= =?UTF-8?q?=E6=8B=A1=E5=BC=B5=EF=BC=8Bdango-run=20=E3=81=AB=20autoplan=20?= =?UTF-8?q?=E3=83=A2=E3=83=BC=E3=83=89=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ゲートが「文書(レビュー)」だけでなく「動作(実機チェック)」でも関門を閉じる ようにし、計画フェーズを無確認で連鎖する autoplan を加えた。gstack の 「実機で検証して自動で進む」挙動に寄せつつ、破壊的操作の手前で止める線引きは維持。 - dango-state.mjs: `check -- pass|fail` を追加し check.json に記録。 gate は high>0 または check の fail を阻害要因として continue|loop-back|escalate を判定。gate --reset は check も初期化。decisions.log に checks_failed= を追記。 check 未記録時は従来どおりレビューのみで判定(後方互換)。 - dango-run/SKILL.md: フローに 3.5 実機チェックを挿入。autoplan(計画一気通し) モードと対応トリガを追加。check を非破壊の自動実行側に明記。 - バージョンを 0.2.0 に。CHANGELOG を更新。 Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 15 +++++++++- bin/dango-state.mjs | 63 +++++++++++++++++++++++++++++++++------ package.json | 2 +- skills/dango-run/SKILL.md | 48 ++++++++++++++++++++++++----- 4 files changed, 110 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96cf6dd..a537566 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,18 @@ ## [Unreleased] +## [0.2.0] - 2026-06-21 +### Added +- ゲートの実機化: `bin/dango-state.mjs` に `check -- pass|fail` を追加し、 + ビルド/起動/スモークの合否を `check.json` に記録。`gate` は「レビュー『高』が残る」 + **または**「実機チェックに `fail` がある」を阻害要因として `continue|loop-back|escalate` + を判定する(文書だけでなく動作で関門を閉じる)。`check` 未記録時は従来どおりレビューのみで判定。 +- `dango-run` に **autoplan(計画一気通し)モード**。計画フェーズ(`dango-plan`→`dango-spec` + →自己レビュー)を無確認で連鎖し、実装(`Edit`)の手前で人間に渡す。 +### Changed +- `gate --reset` が往復カウンタに加えて前回の `check.json` も初期化(feature 跨ぎの誤ブロック防止)。 +- `decisions.log` のゲート行に `checks_failed=` を追記。 + ## [0.1.3] - 2026-06-17 ### Added - OSS 運用の環境整備: `CODE_OF_CONDUCT.md`、`SECURITY.md`、Issue/PR テンプレート、 @@ -32,7 +44,8 @@ - 核となる日本語レビュー役(全角半角・和暦・禁則・郵便番号/住所・IME/組版)。 - インストール/アンインストール、状態・決定ログ helper(Node・ビルド不要)。 -[Unreleased]: https://github.com/fla9ua/dango-pack/compare/v0.1.3...HEAD +[Unreleased]: https://github.com/fla9ua/dango-pack/compare/v0.2.0...HEAD +[0.2.0]: https://github.com/fla9ua/dango-pack/compare/v0.1.3...v0.2.0 [0.1.3]: https://github.com/fla9ua/dango-pack/compare/v0.1.2...v0.1.3 [0.1.2]: https://github.com/fla9ua/dango-pack/compare/v0.1.1...v0.1.2 [0.1.1]: https://github.com/fla9ua/dango-pack/compare/v0.1.0...v0.1.1 diff --git a/bin/dango-state.mjs b/bin/dango-state.mjs index 6345ae6..6b07012 100644 --- a/bin/dango-state.mjs +++ b/bin/dango-state.mjs @@ -8,7 +8,10 @@ // node bin/dango-state.mjs log 決定ログを表示 // node bin/dango-state.mjs verdict --high N [--mid N --low N] [--target T --branch B] // レビュー結果を構造化して保存(verdict.json) -// node bin/dango-state.mjs gate [--reset] verdict.json と往復回数から +// node bin/dango-state.mjs check -- pass|fail [...] +// 実機チェック(ビルド/起動/スモーク等)の +// 合否を記録(check.json)。gate がこれも見る +// node bin/dango-state.mjs gate [--reset] verdict.json + check.json + 往復回数から // continue|loop-back|escalate をコード判定 import { execSync } from 'node:child_process'; @@ -113,16 +116,44 @@ switch (cmd) { process.stdout.write(`verdict saved: high=${high} mid=${mid} low=${low}\n`); break; } + case 'check': { + // 実機チェック(ビルド通過/起動/主要導線スモーク等)の合否を記録する。 + // gate はこれも阻害要因として見る → 「文書」だけでなく「動作」で関門を閉じる。 + const f = parseFlags(rest); + const checks = {}; + for (const [k, v] of Object.entries(f)) { + if (k === '_' || k === 'reset') continue; + const val = String(v).toLowerCase(); + if (val !== 'pass' && val !== 'fail') { + console.error(`check: --${k} には pass か fail を渡してください(受領: ${v})`); + process.exit(1); + } + checks[k] = val; + } + mkdirSync(base, { recursive: true }); + const checkPath = join(base, 'check.json'); + if (f.reset || Object.keys(checks).length === 0) { + writeFileSync(checkPath, JSON.stringify({ checks: {}, at: new Date().toISOString() }, null, 2) + '\n'); + process.stdout.write('check reset: (記録なし)\n'); + break; + } + writeFileSync(checkPath, JSON.stringify({ checks, at: new Date().toISOString() }, null, 2) + '\n'); + const summary = Object.entries(checks).map(([k, v]) => `${k}=${v}`).join(' '); + process.stdout.write(`check saved: ${summary}\n`); + break; + } case 'gate': { - // 状態(verdict + 往復回数)を読んで、continue|loop-back|escalate をコードで決める。 + // 状態(verdict + check + 往復回数)を読んで、continue|loop-back|escalate をコードで決める。 // モデルはこの結果に従うだけ。3回上限の判定もここで強制する。 mkdirSync(base, { recursive: true }); const f = parseFlags(rest); const gatePath = join(base, 'gate.json'); if (f.reset) { + // 新しい feature の頭で往復カウンタも前回の実機チェックも初期化する。 writeFileSync(gatePath, JSON.stringify({ roundtrips: 0 }, null, 2) + '\n'); - process.stdout.write('gate reset: roundtrips=0\n'); + writeFileSync(join(base, 'check.json'), JSON.stringify({ checks: {}, at: new Date().toISOString() }, null, 2) + '\n'); + process.stdout.write('gate reset: roundtrips=0, checks cleared\n'); break; } @@ -134,8 +165,16 @@ switch (cmd) { const gateState = readJson(gatePath, { roundtrips: 0 }); const high = Number(verdict.high ?? 0); + // 実機チェック: check.json があれば fail を阻害要因として扱う(無ければ従来どおり無視)。 + const check = readJson(join(base, 'check.json'), null); + const failedChecks = check && check.checks + ? Object.entries(check.checks).filter(([, v]) => v === 'fail').map(([k]) => k) + : []; + + const blocked = high > 0 || failedChecks.length > 0; + let action; - if (high === 0) { + if (!blocked) { action = 'continue'; } else if (gateState.roundtrips < MAX_ROUNDTRIPS) { gateState.roundtrips += 1; @@ -146,18 +185,24 @@ switch (cmd) { writeFileSync(gatePath, JSON.stringify(gateState, null, 2) + '\n'); const iter = gateState.roundtrips; - const line = `${new Date().toISOString()}\tgate\tstep=review\titer=${iter}\thigh=${high}\tmid=${verdict.mid ?? 0}\tlow=${verdict.low ?? 0}\taction=${action}\n`; + const checksField = failedChecks.length ? failedChecks.join(',') : '-'; + const line = `${new Date().toISOString()}\tgate\tstep=review\titer=${iter}\thigh=${high}\tmid=${verdict.mid ?? 0}\tlow=${verdict.low ?? 0}\tchecks_failed=${checksField}\taction=${action}\n`; appendFileSync(join(base, 'decisions.log'), line); + // 阻害要因を人間語でまとめる(レビュー指摘・実機 fail の両方を明示)。 + const reasons = []; + if (high > 0) reasons.push(`重大度「高」${high}件`); + if (failedChecks.length) reasons.push(`実機チェック fail: ${failedChecks.join(', ')}`); + const reasonText = reasons.join(' / '); const human = { - continue: `重大度「高」なし → 次の工程へ進む (continue)`, - 'loop-back': `重大度「高」${high}件 → 実装へ戻す (loop-back ${iter}/${MAX_ROUNDTRIPS})`, - escalate: `往復上限 ${MAX_ROUNDTRIPS} 回に達し「高」${high}件が残存 → 人間にエスカレート (escalate)`, + continue: `阻害要因なし(レビュー「高」0・実機チェック fail なし) → 次の工程へ進む (continue)`, + 'loop-back': `${reasonText} → 実装へ戻す (loop-back ${iter}/${MAX_ROUNDTRIPS})`, + escalate: `往復上限 ${MAX_ROUNDTRIPS} 回に達し ${reasonText} が残存 → 人間にエスカレート (escalate)`, }[action]; process.stdout.write(`${human}\naction=${action}\n`); break; } default: - console.error('usage: dango-state.mjs [args]'); + console.error('usage: dango-state.mjs [args]'); process.exit(1); } diff --git a/package.json b/package.json index 00a5763..566c3ce 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dango-pack", - "version": "0.1.3", + "version": "0.2.0", "description": "日本のチーム開発に最適化した、Claude Code / Codex 用の仮想開発組織", "type": "module", "bin": { diff --git a/skills/dango-run/SKILL.md b/skills/dango-run/SKILL.md index 37ac0e9..2739582 100644 --- a/skills/dango-run/SKILL.md +++ b/skills/dango-run/SKILL.md @@ -1,6 +1,6 @@ --- name: dango-run -version: 0.1.3 +version: 0.2.0 description: "dango-packのオーケストレータ — 計画→実装→日本語レビュー→出荷を順に回し、足りない役割をAIが演じる。(dango-pack)" allowed-tools: - Read @@ -15,6 +15,9 @@ triggers: - dango-packで進めて - dango-pack run - チームで開発して + - dango-pack autoplan + - 計画を一気に + - 計画だけ先に --- ## この役割は何者か @@ -53,19 +56,48 @@ triggers: 全角半角・和暦・禁則・郵便番号/住所・IME/組版を点検。`reviews/{日付}.md` と `verdict.json`(重大度の件数)を出力させる。 +3.5. **実機チェック(動作で関門を閉じる)** — レビュー(文書)だけでなく、 + **実際に動かした結果**をゲートに渡す。最低限「ビルド通過 / 起動 / 主要導線の + スモーク」を試し、合否を記録する: + ```bash + node bin/dango-state.mjs check --build pass --start pass --smoke fail + ``` + - 名前(`--build` 等)は対象に合わせて自由に付けてよい。値は `pass`/`fail` のみ。 + - 実行手段は対象次第(`npm test` / `npm run build` / 起動しての手動確認 / ブラウザ + 確認など)。**合否は自分の感覚で決めず、走らせた事実をそのまま記録する**。 + - 試せる手段が無いときは記録を省いてよい(その場合ゲートは従来どおりレビューだけで + 判定する)。これは非破壊なので**確認なしで自動実行**してよい。 + 4. **ゲート判定** — 次の工程に進むかどうかは **自分で判断せず、コードに決めさせる**: ```bash - node bin/dango-state.mjs gate # verdict.json と往復回数から判定 + node bin/dango-state.mjs gate # verdict.json + check.json + 往復回数から判定 ``` - 出力末尾の `action=` に従う: + ゲートは「レビュー重大度『高』が残る」**または**「実機チェックに `fail` がある」を + 阻害要因として扱う。出力末尾の `action=` に従う: - `continue` → 5(出荷)へ進む - - `loop-back` → 2(実装)へ戻り、レビュー指摘を直してから再度 3→4 + - `loop-back` → 2(実装)へ戻り、レビュー指摘と実機 fail を直してから再度 3→3.5→4 - `escalate` → 自動で回さず、残課題を要約して人間に渡す 往復上限(実装⇄レビュー 3 回)はコード側で強制されるので、自分で数えない。 5. **出荷 (`dango-ship`)** 日本語のコミット規約・PR本文で締める。 +## 計画一気通し(autoplan モード) + +「計画を一気に」「dango-pack autoplan」「計画だけ先に」等のとき、または feature の +頭で計画を素早く固めたいとき、**計画フェーズだけを無確認で連鎖**させる: + +- 対象は **非破壊の文書生成のみ**:`dango-plan`(要件・受け入れ条件・作業分解)→ + `dango-spec`(要件定義書/基本設計書/詳細設計書)→ 生成物の自己レビュー、を + **途中で止めずに**一気に通し、`plan.md` 一式を仕上げる。 +- ここで止まるのは **本物の方針分岐だけ**(後で覆すと手戻りが大きい選択)。それは + `AskUserQuestion` で確認する。文書の体裁・粒度など覆しやすい判断では止まらない。 +- **実装(`Edit`)には踏み込まない。** autoplan のゴールは「実装着手の一歩手前まで + 計画を仕上げる」こと。計画が揃ったら要点を要約し、「このまま実装(2.)へ進めますか?」 + と人間に渡す。 +- 通常モード(feature 丸ごと)との違いは**計画工程で逐一確認しない**点だけ。実装以降の + 線引き(Edit/ship の手前で止める・ゲートはコード判定)は通常モードと同じ。 + ## ゲート(関門) - 進む/戻る/エスカレートの判断は **`dango-state.mjs gate` の `action=` が一次情報**。 @@ -80,9 +112,11 @@ triggers: ユーザーに毎回コマンドを叩かせない。一度この流れに入ったら、**確認なしで自分で 回す工程**と、**必ず人間に確認する工程**を分ける: -- **確認なしで自動実行(非破壊):** `gate --reset` / `verdict` / `gate` / `decide` / - `log`、および `dango-review` の点検そのもの。これらは `~/.dango-pack` 配下を - 読み書きするだけなので、いちいち聞かずに走らせてよい。 +- **確認なしで自動実行(非破壊):** `gate --reset` / `verdict` / `check` / `gate` / + `decide` / `log`、および `dango-review` の点検・実機チェックの実行そのもの。これらは + `~/.dango-pack` 配下の読み書きと読み取り専用の動作確認なので、いちいち聞かずに + 走らせてよい(ただし実機チェックの手段が `git push` 等の外向き操作を伴う場合は、 + その操作だけ「必ず人間に確認」側に従う)。 - **必ず人間に確認(外向き・破壊的):** `Edit` でのコード修正、`dango-ship`(コミット/ PR)、外部公開を伴う操作。loop-back でレビュー指摘を直すときも、修正の適用前に 一言告げる。