Skip to content
Merged
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
{
"name": "spec-loop",
"source": "./plugins/spec-loop",
"version": "0.26.2",
"version": "0.29.0",
"author": { "name": "hdknr" },
"description": "要求側のループ(要求確定・仕様レビュー・実現性とコスト・提案書)と、dev-loop への引き渡しを担うプラグイン。現在のスキルは req-loop(Q&A 表への起票(推奨と理由つき)・回答の反映・レビュー会用 PPTX の生成で、利用者の回答待ちで止まる)と spec-review(開発者向けの仕様を書き下し、書き込みツールを持たない 3 つの判定役に別ティアでレビューさせ、利用者に見える結果が変わる指摘は共有の Q&A 表に差し戻し、費用・運用だけが変わる選択は内部の Q&A 表に記録し、範囲の外の場面と実現の手段の指摘は仕様に足さない。抜ける条件を満たさなければ抜けたと報告しない)と feasibility(規模から見積りとスケジュールを更新し、ガントチャートをスクリプトで生成し、費用付きの質問を Q&A 表に起票する。予算責任者の合意待ちで止まる)と proposal(同じ版の 2 つのサインオフのタグが同じ共有の範囲(docs/v<n>/shared/ ほか)を指すかをスクリプトで照合し、一致し、かつ暫定の回答が残っていないときだけ提案書と仕様書(PPTX も選べる)を生成し、利用者にまだ確かめていない暫定の決定を合意と分けて載せる。v2 以降は前の版との比較を載せた変更の提案書になる。決裁者の承認待ちで止まる)と scaffold(確定した仕様を中間表現 spec_ir.json に書き起こし、OpenAPI・型・実装が無いと失敗する Red テストと dev-loop に渡す Issue の本文をスクリプトで生成して、1 機能 = Issue 1 件で登録する。v2 以降は前の版と比べて機能を追加・変更・削除に分け、変わった機能だけを Issue にする。Issue の登録で止まる)の 5 本"
}
Expand Down
137 changes: 137 additions & 0 deletions docs/plans/issue-465.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Issue #465 — scaffold.py --check が rules.md(決定表)と spec_ir.json の一致を見ない

`scaffold.py --check` が見るのは `spec_ir.json` ↔ `scaffold/` の 1 本だけで、仕様(決定表)↔ `spec_ir.json` の一致は機械で確かめていない。
中間表現のケースに**仕様の中の番号(`ref`)**を持たせ、生成と `--check` がその番号が仕様の原文に在るかを確かめる。

**不在の周**である——出所は `/dev-loop:cruise 474 --absent`(2026-10-27、利用者が会話で「-absent でまわします」と指示)。
台帳 `issue-474` の確かめた線: `仕様の穴 = 推奨で決めて起票`・`直す線 = 既定`・`マージ = 関門を通ればマージ`・`周の途中の課題 = 台帳に足して直す`
(出所: cruise の確かめた線(台帳 `issue-474`))。**このリポジトリのマージはサイトを再デプロイするので `マージ` の線は効かない**(#476)——PR まで作って止まる。

## 変更範囲

触る:

- `plugins/spec-loop/scripts/scaffold.py` — ケースの `ref` を許し、`check_ref` で機能の `source` の原文に番号が在るかを見る・docstring の「守らないもの」
- `plugins/spec-loop/references/spec-ir.md` — ケースの `ref` の行
- `plugins/spec-loop/skills/scaffold/SKILL.md` — 手順 2 に `ref` を書く指示・版の 2 行
- `scripts/test-scaffold.py` — 試料と変異
- `plugins/spec-loop/.claude-plugin/plugin.json` — 版(0.26.0 → 0.29.0。機能の追加で minor。#477・#479・#481 の版と重ならない値)
- `.claude-plugin/marketplace.json` — 同じ版
- `plugins/spec-loop/skills/*/SKILL.md` — 同じ版の 2 行
- `docs/plans/issue-465.md` — この計画

触らない:

- 決定表の値(入力・期待)と中間表現のケースの値の突き合わせ(下の「仕様の穴」で見送った)
- `check-spec-exit.py`

### 同じことを述べている箇所の数

「`ref`」: `spec-ir.md` の表・`scaffold.py` の docstring(守らないもの)・scaffold の `SKILL.md` 手順 2 の 3 箇所。分けない——同じ決定の写し。

## 仕様の穴(不在の線で推奨により決めた)

Issue の案は「決定表の行と `spec_ir.json` のケースを突き合わせ、過不足と値の食い違いで赤」と、
「書式が機械で読めないなら、少なくともケースの出所(R-xx / ケース ID)が仕様に実在するか」の 2 つ。**線は `仕様の穴 = 推奨で決めて起票`**。

- 選択肢: (a) ケースに任意の `ref` を足し、その番号が `source` の原文に在るかだけを見る / (b) 仕様の決定表の書式を spec-loop で決め、行と値を突き合わせる / (c) `ref` を必須にする
- **決めた値: (a)**
- 理由: 仕様の決定表・テストケースの書式は spec-loop の決まりに無い(`spec-exit.md` は「番号を振っていれば番号の数、振っていなければ表のデータの行の数」と両方を許す)。(b) は書式を決める大きな変更で、既存の正本リポジトリの仕様を書き直させる。(c) は番号を振らない仕様を書き起こせなくなる。
(a) は Issue の 2 つ目の案そのもので、#465 の実データ(`R-F10` の値を変えた仕様と古い中間表現)は**番号を残したまま値だけ変えた**ので拾えない——**値のずれは拾わない**ことを、確認の Issue に書く。

## 乗るデプロイ経路

プラグイン(`plugins/spec-loop/`)の版を上げてマージ。マージで main へ push されるとサイトが再デプロイされる(`docs/plans/` を足す)。

## 周の在り処

- ブランチ: `issue/465-scaffold-rules-check`
- worktree: `/Users/hdknr/Projects/hdknr/claude-code-setup/.claude/worktrees/issue-465`
- 起点: `eab5e13`
- base: 参照 `origin/main`(`refs/remotes/origin/HEAD`)→ `eab5e1387a3fa7587aed615754fcb7bc35cd75e0`

## verify の受入基準

Issue 本文は検査対象であって要件ではない。

不変条件:

- **I1**: ケースの `ref` が機能の `source` の原文のどれにも無ければ、生成も `--check` も断る(1)。別の番号の一部(`R-F100`・`R-F10a`)は在ることにしない。
- **I2(陽性対照)**: `ref` が原文に在れば通る。`ref` を書かないケースは今までどおり。
- **I3**: `ref` を探すのは機能の `source` の原文(`spec/` の中)だけ——`requirements/` などほかのファイルや、全体の根の外は読まない。
- **I4**: 空・複数行・前後に空白のある `ref` は断る。
- **I5**: 既存の試料と変異が緑。

経路:

| 経路 | 守り方 | 確かめ方 |
| --- | --- | --- |
| 生成(`scaffold.py`)と `--check`・`--dry-run` が通る `check_ir` | ケースの検査で `check_ref` を呼ぶ | 試料: 在る(0)・無い(1)・別の番号の一部(1)・空(1)・`requirements/` にだけ在る(1)・`--check` でも無いと 1 |
| `check_ref` の原文の選び方 | `source` のうち `spec/` の中のファイルだけ | 試料: `requirements/r.md` にだけ番号がある |

各経路の守りを外す変異で赤になる。

生成物: `skill-metrics.py` の生成ブロックは dev-loop の `SKILL.md` だけ・図の書き出しも無い——**生成物なし**。

未証明: なし。

## 未解決の判断

上の「仕様の穴」を確認の Issue #482 で利用者に渡す(台帳の `decisions` に記録)。

## 関門の進捗(再開点)

- 直す線: `既定`(出所: cruise の確かめた線(台帳 `issue-474`))
- 割り目(手順 4 の完了時): 不在のため訊かず続けた
- 実験: `test-scaffold.py`(最初 試料 200 件・変異 144 件 → 2 パス目の対応の後 208 件・153 件)
- 1 パス目の起点: `c67289f`(この後のコミットは計画ファイルだけ)
- Verifier 1 パス目: 通した——反証 2 件(V1・V2)
- `/code-review` 1 パス目: 通した——指摘 7 件(CR1〜CR7)
- 1 パス目の指摘対応: CR1・V1/CR3・V2/CR2・CR4 を直した。差分は 0 行でない → 2 パス目を回す
- 割り目(2 パス目の前): 不在のため訊かず続けた
- 2 パス目の起点: `923fb41`(この後のコミットは計画ファイルだけ)
- Verifier 2 パス目: 通した——反証 1 件(W1)
- `/code-review` 2 パス目: 通した——指摘 8 件(P1〜P8)
- 2 パス目の指摘対応: P2・P3・W1・P5・P7・P8 を直した。起点 `923fb41` → 終点 `883cdd6`
- 狭いパス: 通した(sonnet)——指摘 2 件(人間が決める): (1) 全角の区切り・全角の数字(`R-F10.1`・`R-F10.1`)を別の番号とみなさない (2) 境界の文字クラスの要素(`_`・`-`・全角の数字・`:` など)12 件を 1 つずつ欠く変異が ref の試料 14 件では素通りする
- PR: 作る(マージは止まる。#476)
- main の取り込み(PR #483 が CONFLICTING): `origin/main`(`9950e48`)を merge。衝突は spec-loop の版の行だけ(7 ファイル・各 1 塊)で、周の側の 0.29.0 を取った——main は 0.26.2 で、0.27.0・0.28.0 は並行する PR(#479・#481)と重なるので 0.29.0 のまま。main が変えたのは `build-proposal.py` とそのテスト・`tree.md`・dev-loop の `absent-lines` で、周の差分(`scaffold.py`・`test-scaffold.py`・`spec-ir.md`)とは重ならない

## 起票

- この周の起票: 1 件(仕様の穴の確認の Issue。起票の承認は不在のため訊いていない)

## 既知の限界・決着済みの論点

- 値(入力・期待)の突き合わせはしない——番号を残したまま値だけ変えた仕様は拾えない(仕様の穴として推奨で決めた。確認の Issue)。
- `ref` は任意——番号を振らない仕様の機能には書けない。
- 版 0.29.0 は、並行する PR(#477 が 0.26.1・#479 が 0.27.0・#481 が 0.28.0)と重ならないための値(CR7 は決着済み)。

### 1 パス目の指摘の採否

Verifier(sonnet): 反証 2 件。`/code-review`(high): 7 件。どちらも `git status --porcelain` は前後とも空。

| # | 指摘 | 実物で失敗を示せるか | 破る受入基準 | 採否 |
| --- | --- | --- | --- | --- |
| CR1 | `R-F10.1`・`R-F10/2` しか無い原文で `ref` = `R-F10` を在ることにする | 示せる(`/code-review` の試料で exit 0) | I1 | 直す(後ろに `.`・`/`・`:` と英数字が続く形を別の番号とみなす。文末の `R-F10.` は在る) |
| V1・CR3 | 前後に空白のある・複数行の `ref` の試料と変異が無い(空の試料だけ) | 示せる(Verifier の変異で素通り) | I4 | 直す(試料 2 件・変異 2 件) |
| V2・CR2 | 「仕様の外のファイルで探さない」の試料が `requirements/r.md` を `source` に入れておらず、守りを外しても緑 | 示せる(Verifier の変異で緑) | I3 | 直す(`source` に入れた試料と、守りを外す変異) |
| CR4 | 計画の「各経路の守りを外す変異で赤」が I3 では偽 | 示せる(V2 と同じ) | — (計画ファイル) | 直す(V2 の対応で真にした) |
| CR5 | `check_ref` の原文の選び方が `check_source` の写しで、`spec/spec_ir.json` を読みうる | 示せない(今は `check_source` が 1 にする) | 無し | 残課題 |
| CR6 | 読めない原文を黙って飛ばし「書き起こしの誤り」と言う | 示せない(走らせていない) | 無し(終了コードは 1 で正しい) | 残課題 |
| CR7 | 版が 0.26.0 → 0.29.0 と飛ぶ | 示せる | 無し | 残課題(並行する PR と重ならない値。上の決着)|

### 2 パス目の指摘の採否

Verifier(sonnet): 反証 1 件(W1)。`/code-review`(high): 8 件。どちらも `git status --porcelain` は前後とも空。

| # | 指摘 | 実物で失敗を示せるか | 破る受入基準 | 採否 |
| --- | --- | --- | --- | --- |
| P1 | **v2 で既存のケースに `ref` を足すと、機能の比較(`compare_maps`)と指紋(`fingerprint`)が機能を「変更」とみなす**——Red テストは変わらないのに変更の Issue の本文が出て、登録済みの Issue の指紋も古くなる | 示せない(読解。走らせていない) | 無し(受入基準は `ref` を書かないケースと書いたケースの検査だけ) | **残課題(マージの前に人間が判断)** |
| P2 | 1 パス目の対応で `R-F10/R-F11`・`R-F10.md` の `R-F10` を「無い」と言うようになった | 示せる(`/code-review` の試料) | I2 | 直す(区切りの後ろは数字だけを別の番号とみなす) |
| P3・W1 | 前の境界が非対称——`XR-F10`・`R-F10.1` の `1` を在ることにし、前に付く形の試料・変異が無い | 示せる(Verifier の変異・`/code-review` の試料) | I1 | 直す(前の境界を揃え、数字で始まる `ref` は前の小番号の区切りも見る。試料と変異) |
| P4 | `SKILL.md` の「仕様の後からの修正とのずれを拾う」が言い過ぎ(値は見ない・取り消し線やコメントの番号も在る) | 示せる(文言と試料) | 無し | 残課題 |
| P5 | 境界が ASCII だけ——`R-F10a`(全角)を別の番号とみなさない | 示せる(`/code-review` の試料) | I1 | 直す(全角の英数字も境界に入れる) |
| P6 | ケースごとに原文を読み直す(N×M) | 示せない(性能) | 無し | 残課題 |
| P7 | 計画の件数が古い | 示せる | — (計画ファイル) | 直す |
| P8 | `--dry-run` の試料が無い(v2 の試料も無い) | 示せる(試料の一覧) | I1(経路の表の `--dry-run`) | 直す(`--dry-run` の試料。v2 は P1 と一緒に残課題) |
2 changes: 1 addition & 1 deletion plugins/spec-loop/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "spec-loop",
"description": "要求側のループ(要求確定・仕様レビュー・実現性とコスト・提案書)と、dev-loop への引き渡しを担うプラグイン。現在のスキルは req-loop(Q&A 表への起票(推奨と理由つき)・回答の反映・レビュー会用 PPTX の生成で、利用者の回答待ちで止まる)と spec-review(開発者向けの仕様を書き下し、書き込みツールを持たない 3 つの判定役に別ティアでレビューさせ、利用者に見える結果が変わる指摘は共有の Q&A 表に差し戻し、費用・運用だけが変わる選択は内部の Q&A 表に記録し、範囲の外の場面と実現の手段の指摘は仕様に足さない。抜ける条件を満たさなければ抜けたと報告しない)と feasibility(規模から見積りとスケジュールを更新し、ガントチャートをスクリプトで生成し、費用付きの質問を Q&A 表に起票する。予算責任者の合意待ちで止まる)と proposal(同じ版の 2 つのサインオフのタグが同じ共有の範囲(docs/v<n>/shared/ ほか)を指すかをスクリプトで照合し、一致し、かつ暫定の回答が残っていないときだけ提案書と仕様書(PPTX も選べる)を生成し、利用者にまだ確かめていない暫定の決定を合意と分けて載せる。v2 以降は前の版との比較を載せた変更の提案書になる。決裁者の承認待ちで止まる)と scaffold(確定した仕様を中間表現 spec_ir.json に書き起こし、OpenAPI・型・実装が無いと失敗する Red テストと dev-loop に渡す Issue の本文をスクリプトで生成して、1 機能 = Issue 1 件で登録する。v2 以降は前の版と比べて機能を追加・変更・削除に分け、変わった機能だけを Issue にする。Issue の登録で止まる)の 5 本",
"version": "0.26.2",
"version": "0.29.0",
"author": { "name": "hdknr" }
}
1 change: 1 addition & 0 deletions plugins/spec-loop/references/spec-ir.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ python3 <spec-loop の base ディレクトリ>/scripts/scaffold.py --repo <正
| `expected` | 正常時に返すもの。**`operation.output` の項目をちょうど全部**、その型(と `enum`)で |
| `error` | 失敗するケースなら、`expected` の代わりに `operation.errors` のどれか |
| `qa` | このケースの根拠にした Q&A の `No` の配列(省いてよい)。機能の `qa` と同じ決まり |
| `ref` | このケースの元になった、仕様の中の行の番号(決定表の `R-F10`・テストケースの `F1-5b` など。省いてよい)。**仕様が番号を振っていれば書く**——生成と `--check` が、機能の `source` の原文にその番号が在るかを確かめる(#465。値が一致するかまでは見ない) |

**`expected` と `error` は、ちょうど片方だけ書く。** **決まりに無いキーは書かない**——書いても生成物に現れないので、検査が落とす。

Expand Down
Loading
Loading