phasegate 0.67.0 → 0.69.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/CHANGELOG.md +223 -1
  2. package/docs/guide/quick-vs-full-mode.md +32 -1
  3. package/package.json +1 -1
  4. package/scripts/harness/ci-governance/application/dto/scaffold-design-input.ts +8 -0
  5. package/scripts/harness/ci-governance/application/dto/scaffold-design-output.ts +12 -0
  6. package/scripts/harness/ci-governance/application/usecases/scaffold-design-usecase.ts +56 -0
  7. package/scripts/harness/ci-governance/composition-root.ts +22 -1
  8. package/scripts/harness/ci-governance/domain/ports/design-doc-writer-port.ts +18 -0
  9. package/scripts/harness/ci-governance/domain/ports/template-repository-port.ts +15 -0
  10. package/scripts/harness/ci-governance/domain/value-objects/design-phase.ts +65 -0
  11. package/scripts/harness/ci-governance/infrastructure/adapters/file-system-design-doc-writer-adapter.ts +42 -0
  12. package/scripts/harness/ci-governance/infrastructure/adapters/file-system-template-repository-adapter.ts +35 -0
  13. package/scripts/harness/ci-governance/presentation/handlers/scaffold-design-handler.ts +97 -0
  14. package/scripts/harness/harness-error/infrastructure/registry/l2-error-definitions.ts +1 -1
  15. package/scripts/harness/main.ts +18 -0
  16. package/skills/skill-creator/SKILL.md +3 -3
  17. package/skills/skill-creator/scripts/init_skill.py +3 -3
  18. package/skills/skill-creator/scripts/package_skill.py +4 -4
  19. package/templates/domain_model.template.md +51 -0
  20. package/templates/it_test_design.template.md +38 -0
  21. package/templates/logical_design.template.md +7 -10
  22. package/templates/uiux_design.template.md +45 -0
  23. package/templates/unit_test_design.template.md +39 -0
  24. package/skills/skill-creator/scripts/__pycache__/quick_validate.cpython-312.pyc +0 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,199 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.69.0] - 2026-04-22
11
+
12
+ ### Added
13
+
14
+ - ISSUE-007 Wave 4 / Phase C — `npx phasegate scaffold-design --unit <id> --phase <logical|domain|uiux|unit-test|it-test> [--force] [--json]` CLI を追加。`templates/*.template.md` を読み取り `{{unit}}` プレースホルダを置換して `docs/product/construction/{unit}/*.md` に書き込む。既存ファイルは `--force` なしでは保護。Wave 3 の pre-tool-use hook エラーで emit される `scaffold:` 行が実動作するようになった。
15
+ - `templates/{domain_model,uiux_design,unit_test_design,it_test_design}.template.md` を追加(5 phase すべてに minimum viable template)。
16
+
17
+ ### Changed
18
+
19
+ - Wave 3 の L2-001 `defaultTemplatePath` を `docs/templates/logical_design.template.md` → `templates/logical_design.template.md` に修正(配布物と整合)。
20
+
21
+ ## [0.68.0] - 2026-04-22
22
+
23
+ ### Changed
24
+
25
+ - `skills/` 同梱物のクリーンアップ — skill-creator の `scripts/__pycache__/` Python バイトコンパイルキャッシュが npm 配布物に混入していたため除去。`.gitignore` / `.npmignore` に `__pycache__/` と `*.pyc` を追加。skill-creator の使用例パスを Anthropic 原本の `skills/public` / `skills/private` から PhaseGate レイアウトに合わせた `skills` に統一。
26
+
27
+ ## [0.67.0] - 2026-04-22
28
+
29
+ ### Changed
30
+
31
+ - ISSUE-007 Wave 3 / Phase B — `phase-gate` の HarnessError をアクショナブル化(足りない設計文書のパスと推奨アクションを `fix_example` に明示)。
32
+
33
+ ## [0.66.0] - 2026-04-22
34
+
35
+ ### Added
36
+
37
+ - ISSUE-007 Wave 2 / Phase A-2 — `.phasegate/baseline.json` に登録済みかつ sha1 が一致するファイルを `phase-gate` 対象から除外する **baseline grandfather** を pre-tool-use hook に統合。レガシーリポジトリへの後付け導入時の摩擦を解消する。
38
+
39
+ ## [0.65.0] - 2026-04-21
40
+
41
+ ### Added
42
+
43
+ - ISSUE-007 Wave 1 / Phase A-1 — `npx phasegate baseline [--dry-run|--force|--paths|--json]` CLI を追加。`.phasegate/baseline.json` スナップショットを生成し、phasegate.config.json に `baseline.{enabled, path}` スキーマを追加。
44
+
45
+ ## [0.64.0] - 2026-04-21
46
+
47
+ ### Added
48
+
49
+ - ISSUE-006 Story B — `quickMode.fullModeRequiredWhen` の判定を pre-tool-use hook に統合。`mixedCategories` / `newDomainFile` / `apiContractChange` のいずれかが立つと書き込み時点で同期的に Full Mode へエスカレートしブロックする(block reason: `FULL_MODE_REQUIRED`)。
50
+
51
+ ## [0.63.0] - 2026-04-21
52
+
53
+ ### Added
54
+
55
+ - ISSUE-006 Story A — `quickMode.fullModeRequiredWhen` 設定キー(`mixedCategories` / `newDomainFile` / `apiContractChange`、いずれもデフォルト `true`)を導入し、Quick Mode → Full Mode のエスカレート条件を設定駆動化。
56
+ - `npx phasegate check-change-category --paths <csv> [--format json] [--fail-on-full-required]` CLI — 任意のファイルリストを Quick Mode カテゴリに分類し、Full Mode が必要かを返す。CI gate での使用を想定。
57
+
58
+ ## [0.62.0] - 2026-04-21
59
+
60
+ ### Added
61
+
62
+ - ISSUE-013 C-6(軽量版)— UserPromptSubmit hook に violation detection を追加。
63
+
64
+ ## [0.61.0] - 2026-04-21
65
+
66
+ ### Added
67
+
68
+ - ISSUE-013 C-5 — UserPromptSubmit hook で動的状態(現在の Quick/Full モード等)をプロンプトに注入。
69
+
70
+ ## [0.60.0] - 2026-04-21
71
+
72
+ ### Added
73
+
74
+ - ISSUE-013 C-4 — SessionStart hook を追加し、セッション開始時に静的ルール(CLAUDE.md 等)を注入する仕組みを実装。
75
+
76
+ ## [0.59.0] - 2026-04-21
77
+
78
+ ### Added
79
+
80
+ - ISSUE-013 A-1 / A-2 / B-3 — `phasegate init --agent <claude|codex|both>` オプションで Codex CLI 向けの `.codex/hooks.json` を自動配置。Codex dogfood セットアップを README に追記。
81
+
82
+ ## [0.58.0] - 2026-04-21
83
+
84
+ ### Added
85
+
86
+ - ISSUE-013 Wave 2 — Codex CLI 統合の本体実装。`PreToolUse(Bash)` / `PostToolUse(Bash)` / `Stop` フックを Codex 向けに配線。
87
+
88
+ ## [0.57.0] - 2026-04-20
89
+
90
+ ### Added
91
+
92
+ - ISSUE-013 Wave 1 — `BashWriteTargetExtractor` が Bash 経由 `apply_patch <<'PATCH'` heredoc の書き込み先パスを抽出するよう拡張。Codex の Bash ルートを pre-tool-use hook で押さえられるようになる。
93
+
94
+ ## [0.56.0] - 2026-04-20
95
+
96
+ ### Fixed
97
+
98
+ - ISSUE-011 Wave 3 / P3-4 / HF2-04 — `initial-creation-expiration-checker` バリデータを修正。
99
+
100
+ ## [0.55.0] - 2026-04-20
101
+
102
+ ### Fixed
103
+
104
+ - ISSUE-011 Wave 2 — Markdown parser の code-span / code-fence 内に書かれた `@unit` / `@layer` 等のメタタグを誤検出していたバグを修正(コードフェンス内をスキップするよう変更)。
105
+
106
+ ## [0.54.0] - 2026-04-20
107
+
108
+ ### Fixed
109
+
110
+ - ISSUE-011 Wave 1 / P2-2 — CLI のエラー伝播を修正(内部エラーが exit code 0 で握り潰されていた問題)。
111
+ - ISSUE-011 Wave 1 / P2-3 — `.mdx` / `.markdown` 拡張子を Markdown ドキュメント検証の対象に追加。
112
+
113
+ ## [0.53.0] - 2026-04-19
114
+
115
+ ### Added
116
+
117
+ - ISSUE-008 Phase C-1〜C-3 + D — テストファイルへの `@story` メタデータ end-to-end 検証を完成。`templates/` 配下のサンプルファイルを実体化し、生成コードへのメタデータ付与を保証する経路を確立。
118
+
119
+ ## [0.52.0] - 2026-04-19
120
+
121
+ ### Changed
122
+
123
+ - ISSUE-008 Phase B-3 — pre-commit フローに `.md` 設計文書の検証を接続。`logical_design.md` 等の frontmatter / メタデータが欠けたままコミットされるのを防ぐ。
124
+
125
+ ## [0.51.0] - 2026-04-19
126
+
127
+ ### Added
128
+
129
+ - ISSUE-011 起票(`validate-metadata` UX / parser / drift 検出に関する改善集)。
130
+
131
+ ## [0.50.0] - 2026-04-19
132
+
133
+ ### Added
134
+
135
+ - ISSUE-008 Phase B-2 — `validate-metadata` CLI に `.md` 分岐を追加。Markdown 設計文書のメタデータ(frontmatter)も検証対象になる。
136
+
137
+ ## [0.49.0] - 2026-04-19
138
+
139
+ ### Changed
140
+
141
+ - ISSUE-008 Phase B-1 撤回 + P1-2 前提更新 — 設計文書 frontmatter 必須化(v0.48.0)の方針を再検討し前提を更新。
142
+
143
+ ## [0.48.0] - 2026-04-19
144
+
145
+ ### Added
146
+
147
+ - ISSUE-008 Phase B-1 / P1-2 — 設計文書(`logical_design.md` / `domain_model.md` 等)の frontmatter を必須化。
148
+
149
+ ## [0.47.0] - 2026-04-18
150
+
151
+ ### Added
152
+
153
+ - ISSUE-008 Phase A / P1-1 — 生成コードに `@unit` / `@layer` メタデータを必ず付与するよう、各実装スキル(`story-implementor` / `quick-implementor`)に指示を追加。
154
+
155
+ ## [0.46.0] - 2026-04-18
156
+
157
+ ### Added
158
+
159
+ - ISSUE-007 起票(リトロフィット導入障壁 — レガシーリポジトリでの初回 phase-gate ブロック問題)。
160
+ - ISSUE-008 起票(メタデータ emit 欠落 — 生成コードに `@unit` / `@layer` が付かないケース)。
161
+
162
+ ## [0.45.0] - 2026-04-18
163
+
164
+ ### Added
165
+
166
+ - ISSUE-006 起票 + Phase P2-3 — `docs/guide/quick-vs-full-mode.md`(Quick Mode と Full Mode の選択ガイド)を新設。
167
+
168
+ ## [0.44.0] - 2026-04-18
169
+
170
+ ### Fixed
171
+
172
+ - ISSUE-005 Phase D / P3-8 — Markdown のメタ見出し(`---` で囲まれた frontmatter 等)をパース時に正しくスキップするよう修正。
173
+ - ISSUE-005 Phase D / P3-9 — ファイルパスから `@unit` を推定するロジックを改善。
174
+ - ISSUE-005 Phase D / P3-10 — `list-errors` と `render-errors` の境界をドキュメント化(`list-errors` は定義駆動 / `render-errors` はランタイム駆動)。
175
+
176
+ ## [0.43.0] - 2026-04-18
177
+
178
+ ### Fixed
179
+
180
+ - ISSUE-005 Phase C / P2-6 — `phasegate:check-phase` の `--help` / `--json` フラグが positional 引数として食われ unit 名扱いされていたバグを修正。
181
+ - ISSUE-005 Phase C / P2-7 — `regression:*` 系コマンドの出力先を整理。
182
+
183
+ ## [0.42.0] - 2026-04-18
184
+
185
+ ### Fixed
186
+
187
+ - ISSUE-005 Phase B-2 / P1-5 — `detect-drift` と L4-001 バリデータを統合し、設計-コード乖離検出の経路を一本化。
188
+
189
+ ## [0.41.0] - 2026-04-18
190
+
191
+ ### Fixed
192
+
193
+ - ISSUE-005 Phase B-1 / P1-3 — fresh repo(履歴がない初期化直後のリポジトリ)での git 解析が失敗するバグを fallback 経路で修正。
194
+ - ISSUE-005 Phase B-1 / P1-4 — `validate --layer` フィルタが効かないケースを修正。
195
+
196
+ ## [0.40.0] - 2026-04-18
197
+
198
+ ### Fixed
199
+
200
+ - ISSUE-005 Phase A / P0-1 — pre-commit 経路の復旧(一部バリデータが pre-commit から呼ばれていなかった問題)。
201
+ - ISSUE-005 Phase A / P0-2 — `ci:generate-template` の UX 改善(`--preset` 省略時のエラーメッセージを実用的に)。
202
+
10
203
  ## [0.39.0] - 2026-04-18
11
204
 
12
205
  ### Fixed
@@ -176,7 +369,36 @@ Pre-reset era (formerly v1.0.0 - v1.1.1). Initial release and early bug fixes:
176
369
  - ajv v8互換対応
177
370
  - 3件のバグ修正
178
371
 
179
- [Unreleased]: https://github.com/junpei-9898/phasegate/compare/v0.38.0...HEAD
372
+ [Unreleased]: https://github.com/junpei-9898/phasegate/compare/v0.67.0...HEAD
373
+ [0.67.0]: https://github.com/junpei-9898/phasegate/compare/v0.66.0...v0.67.0
374
+ [0.66.0]: https://github.com/junpei-9898/phasegate/compare/v0.65.0...v0.66.0
375
+ [0.65.0]: https://github.com/junpei-9898/phasegate/compare/v0.64.0...v0.65.0
376
+ [0.64.0]: https://github.com/junpei-9898/phasegate/compare/v0.63.0...v0.64.0
377
+ [0.63.0]: https://github.com/junpei-9898/phasegate/compare/v0.62.0...v0.63.0
378
+ [0.62.0]: https://github.com/junpei-9898/phasegate/compare/v0.61.0...v0.62.0
379
+ [0.61.0]: https://github.com/junpei-9898/phasegate/compare/v0.60.0...v0.61.0
380
+ [0.60.0]: https://github.com/junpei-9898/phasegate/compare/v0.59.0...v0.60.0
381
+ [0.59.0]: https://github.com/junpei-9898/phasegate/compare/v0.58.0...v0.59.0
382
+ [0.58.0]: https://github.com/junpei-9898/phasegate/compare/v0.57.0...v0.58.0
383
+ [0.57.0]: https://github.com/junpei-9898/phasegate/compare/v0.56.0...v0.57.0
384
+ [0.56.0]: https://github.com/junpei-9898/phasegate/compare/v0.55.0...v0.56.0
385
+ [0.55.0]: https://github.com/junpei-9898/phasegate/compare/v0.54.0...v0.55.0
386
+ [0.54.0]: https://github.com/junpei-9898/phasegate/compare/v0.53.0...v0.54.0
387
+ [0.53.0]: https://github.com/junpei-9898/phasegate/compare/v0.52.0...v0.53.0
388
+ [0.52.0]: https://github.com/junpei-9898/phasegate/compare/v0.51.0...v0.52.0
389
+ [0.51.0]: https://github.com/junpei-9898/phasegate/compare/v0.50.0...v0.51.0
390
+ [0.50.0]: https://github.com/junpei-9898/phasegate/compare/v0.49.0...v0.50.0
391
+ [0.49.0]: https://github.com/junpei-9898/phasegate/compare/v0.48.0...v0.49.0
392
+ [0.48.0]: https://github.com/junpei-9898/phasegate/compare/v0.47.0...v0.48.0
393
+ [0.47.0]: https://github.com/junpei-9898/phasegate/compare/v0.46.0...v0.47.0
394
+ [0.46.0]: https://github.com/junpei-9898/phasegate/compare/v0.45.0...v0.46.0
395
+ [0.45.0]: https://github.com/junpei-9898/phasegate/compare/v0.44.0...v0.45.0
396
+ [0.44.0]: https://github.com/junpei-9898/phasegate/compare/v0.43.0...v0.44.0
397
+ [0.43.0]: https://github.com/junpei-9898/phasegate/compare/v0.42.0...v0.43.0
398
+ [0.42.0]: https://github.com/junpei-9898/phasegate/compare/v0.41.0...v0.42.0
399
+ [0.41.0]: https://github.com/junpei-9898/phasegate/compare/v0.40.0...v0.41.0
400
+ [0.40.0]: https://github.com/junpei-9898/phasegate/compare/v0.39.0...v0.40.0
401
+ [0.39.0]: https://github.com/junpei-9898/phasegate/compare/v0.38.0...v0.39.0
180
402
  [0.38.0]: https://github.com/junpei-9898/phasegate/compare/v0.37.0...v0.38.0
181
403
  [0.37.0]: https://github.com/junpei-9898/phasegate/compare/v0.36.0...v0.37.0
182
404
  [0.36.0]: https://github.com/junpei-9898/phasegate/compare/v0.35.0...v0.36.0
@@ -50,6 +50,37 @@ Both modes keep **L1 in full strength** and **L2 metadata / test-quality** — `
50
50
 
51
51
  ---
52
52
 
53
+ ## Automatic Escalation: `fullModeRequiredWhen`
54
+
55
+ Even when you launch `/quick-implementor`, the harness re-checks whether the in-flight change set actually qualifies as Quick. Three triggers force escalation back to Full Mode at the **pre-tool-use hook** (synchronous block, not just a post-hoc warning):
56
+
57
+ | Trigger | `quickMode.fullModeRequiredWhen.*` flag | Default | Block reason on hook |
58
+ |---|---|---|---|
59
+ | Change set spans multiple categories (e.g. `bugfix` + `api`) | `mixedCategories` | `true` | `FULL_MODE_REQUIRED` |
60
+ | New file added under any `domain/` directory | `newDomainFile` | `true` | `FULL_MODE_REQUIRED` |
61
+ | Modification to a Port (`*port.ts`) or Adapter (`*adapter.ts`) | `apiContractChange` | `true` | `FULL_MODE_REQUIRED` |
62
+
63
+ Introduced in v0.63.0 (ISSUE-006 Story A — config-driven flags + `check-change-category` CLI) and wired into the hook in v0.64.0 (Story B). Each flag can be flipped to `false` only when a project intentionally accepts the risk of merging that category of change without the design ceremony — e.g. early-stage prototypes where new domain files churn freely.
64
+
65
+ ### Dry-running the classifier
66
+
67
+ Use `check-change-category` to evaluate an arbitrary file list without actually starting an implementation:
68
+
69
+ ```bash
70
+ # Inspect what category each file lands in
71
+ npx phasegate check-change-category --paths src/foo.ts,src/bar.ts
72
+
73
+ # CI gate: hard-fail the build if Quick Mode would have to escalate
74
+ npx phasegate check-change-category \
75
+ --paths "$(git diff --name-only origin/main...HEAD | paste -sd, -)" \
76
+ --fail-on-full-required \
77
+ --format json
78
+ ```
79
+
80
+ `--fail-on-full-required` is opt-in; without it the command is purely informational (always exit 0).
81
+
82
+ ---
83
+
53
84
  ## Worked Examples
54
85
 
55
86
  ### Full Mode candidates
@@ -125,7 +156,7 @@ Not mechanically — Quick Mode does not inspect paths. A one-line bugfix inside
125
156
  Stop, commit nothing, and re-launch `/story-implementor`. Don't try to "finish in Quick" — you'll skip the design gate that would otherwise catch the scope creep.
126
157
 
127
158
  **Q. Can I disable Quick Mode entirely?**
128
- Yes — remove all entries from `quickMode.allowedCategories` in `phasegate.config.json`. All changes will route through Full Mode. This is reasonable for high-compliance projects.
159
+ Yes — remove all entries from `quickMode.allowedCategories` in `phasegate.config.json`. All changes will route through Full Mode. This is reasonable for high-compliance projects. Alternatively, leave the categories as-is and set every `quickMode.fullModeRequiredWhen.*` flag to `true` (the default) so any non-trivial scope automatically escalates.
129
160
 
130
161
  **Q. Can I add custom categories?**
131
162
  No. `allowedCategories` is a fixed enum (`bugfix`, `docs`, `test`, `config`). If your workflow needs a fifth category, that is evidence the change is probably Full Mode material.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phasegate",
3
- "version": "0.67.0",
3
+ "version": "0.69.0",
4
4
  "packageManager": "pnpm@10.30.1",
5
5
  "description": "Phasegate — AI-agnostic quality defense toolkit. Enforces structural integrity between design intent and code.",
6
6
  "license": "Apache-2.0",
@@ -0,0 +1,8 @@
1
+ // @unit ci-governance
2
+ // @layer application
3
+
4
+ export interface ScaffoldDesignInput {
5
+ readonly unit: string;
6
+ readonly phase: string;
7
+ readonly force?: boolean;
8
+ }
@@ -0,0 +1,12 @@
1
+ // @unit ci-governance
2
+ // @layer application
3
+
4
+ export interface ScaffoldDesignOutput {
5
+ readonly targetPath: string;
6
+ readonly templatePath: string;
7
+ readonly unit: string;
8
+ readonly phase: string;
9
+ readonly written: boolean;
10
+ readonly alreadyExists: boolean;
11
+ readonly overwritten: boolean;
12
+ }
@@ -0,0 +1,56 @@
1
+ // @unit ci-governance
2
+ // @layer application
3
+
4
+ import { DesignPhase } from '../../domain/value-objects/design-phase.js';
5
+ import type { TemplateRepositoryPort } from '../../domain/ports/template-repository-port.js';
6
+ import type { DesignDocWriterPort } from '../../domain/ports/design-doc-writer-port.js';
7
+ import type { ScaffoldDesignInput } from '../dto/scaffold-design-input.js';
8
+ import type { ScaffoldDesignOutput } from '../dto/scaffold-design-output.js';
9
+
10
+ const UNIT_PLACEHOLDER = /\{\{\s*unit\s*\}\}/g;
11
+
12
+ export class ScaffoldDesignUseCase {
13
+ constructor(
14
+ private readonly templates: TemplateRepositoryPort,
15
+ private readonly writer: DesignDocWriterPort,
16
+ ) {}
17
+
18
+ async execute(input: ScaffoldDesignInput): Promise<ScaffoldDesignOutput> {
19
+ if (!input.unit || input.unit.trim().length === 0) {
20
+ throw new Error('--unit は必須です');
21
+ }
22
+ const phase = DesignPhase.create(input.phase);
23
+ const unit = input.unit.trim();
24
+ const force = input.force === true;
25
+
26
+ const templatePath = this.templates.resolvePath(phase);
27
+ const targetPath = this.writer.resolvePath(unit, phase);
28
+ const alreadyExists = await this.writer.exists(unit, phase);
29
+
30
+ if (alreadyExists && !force) {
31
+ return {
32
+ targetPath,
33
+ templatePath,
34
+ unit,
35
+ phase: phase.value,
36
+ written: false,
37
+ alreadyExists: true,
38
+ overwritten: false,
39
+ };
40
+ }
41
+
42
+ const raw = await this.templates.read(phase);
43
+ const rendered = raw.replace(UNIT_PLACEHOLDER, unit);
44
+ await this.writer.write(unit, phase, rendered);
45
+
46
+ return {
47
+ targetPath,
48
+ templatePath,
49
+ unit,
50
+ phase: phase.value,
51
+ written: true,
52
+ alreadyExists,
53
+ overwritten: alreadyExists && force,
54
+ };
55
+ }
56
+ }
@@ -40,11 +40,17 @@ import { GlobFileScannerAdapter } from './infrastructure/adapters/glob-file-scan
40
40
  import { FileSystemSha1HasherAdapter } from './infrastructure/adapters/file-system-sha1-hasher-adapter.js';
41
41
  import { BaselineJsonRepositoryAdapter } from './infrastructure/adapters/baseline-json-repository-adapter.js';
42
42
 
43
+ import { ScaffoldDesignUseCase } from './application/usecases/scaffold-design-usecase.js';
44
+ import { FileSystemTemplateRepositoryAdapter } from './infrastructure/adapters/file-system-template-repository-adapter.js';
45
+ import { FileSystemDesignDocWriterAdapter } from './infrastructure/adapters/file-system-design-doc-writer-adapter.js';
46
+ import { ScaffoldDesignHandler } from './presentation/handlers/scaffold-design-handler.js';
47
+
43
48
  export interface CiGovernanceCompositionRoot {
44
49
  generateCiTemplateHandler: GenerateCiTemplateHandler;
45
50
  migrateAgentsMdHandler: MigrateAgentsMdHandler;
46
51
  checkRepetitionHandler: CheckRepetitionHandler;
47
52
  createBaselineHandler: CreateBaselineHandler;
53
+ scaffoldDesignHandler: ScaffoldDesignHandler;
48
54
  // Use cases exposed for direct access
49
55
  recordErrorOccurrenceUseCase: RecordErrorOccurrenceUseCase;
50
56
  checkEscalationUseCase: CheckEscalationUseCase;
@@ -52,9 +58,13 @@ export interface CiGovernanceCompositionRoot {
52
58
  aggregateLessonsUseCase: AggregateLessonsUseCase;
53
59
  validatePointersUseCase: ValidatePointersUseCase;
54
60
  createBaselineUseCase: CreateBaselineUseCase;
61
+ scaffoldDesignUseCase: ScaffoldDesignUseCase;
55
62
  }
56
63
 
57
- export function buildCiGovernance(baseDir: string): CiGovernanceCompositionRoot {
64
+ export function buildCiGovernance(
65
+ baseDir: string,
66
+ harnessRoot: string = baseDir,
67
+ ): CiGovernanceCompositionRoot {
58
68
  // Infrastructure adapters
59
69
  const validatorIdRegistryAdapter = new ValidatorIdRegistryAdapter();
60
70
  const presetConfigAdapter = new PresetConfigAdapter();
@@ -98,6 +108,14 @@ export function buildCiGovernance(baseDir: string): CiGovernanceCompositionRoot
98
108
  baselineRepository,
99
109
  );
100
110
 
111
+ // Scaffold design adapters & use case (ISSUE-007 Wave 4)
112
+ const templateRepository = new FileSystemTemplateRepositoryAdapter(harnessRoot);
113
+ const designDocWriter = new FileSystemDesignDocWriterAdapter(baseDir);
114
+ const scaffoldDesignUseCase = new ScaffoldDesignUseCase(
115
+ templateRepository,
116
+ designDocWriter,
117
+ );
118
+
101
119
  // Handlers
102
120
  const generateCiTemplateHandler = new GenerateCiTemplateHandler(
103
121
  generateCiTemplateUseCase,
@@ -106,17 +124,20 @@ export function buildCiGovernance(baseDir: string): CiGovernanceCompositionRoot
106
124
  const migrateAgentsMdHandler = new MigrateAgentsMdHandler(migrateAgentsMdUseCase);
107
125
  const checkRepetitionHandler = new CheckRepetitionHandler(checkEscalationUseCase);
108
126
  const createBaselineHandler = new CreateBaselineHandler(createBaselineUseCase);
127
+ const scaffoldDesignHandler = new ScaffoldDesignHandler(scaffoldDesignUseCase);
109
128
 
110
129
  return {
111
130
  generateCiTemplateHandler,
112
131
  migrateAgentsMdHandler,
113
132
  checkRepetitionHandler,
114
133
  createBaselineHandler,
134
+ scaffoldDesignHandler,
115
135
  recordErrorOccurrenceUseCase,
116
136
  checkEscalationUseCase,
117
137
  resetRepetitionUseCase,
118
138
  aggregateLessonsUseCase,
119
139
  validatePointersUseCase,
120
140
  createBaselineUseCase,
141
+ scaffoldDesignUseCase,
121
142
  };
122
143
  }
@@ -0,0 +1,18 @@
1
+ // @unit ci-governance
2
+ // @layer domain
3
+
4
+ import type { DesignPhase } from '../value-objects/design-phase.js';
5
+
6
+ export interface DesignDocWriterPort {
7
+ /** 書き込み先の絶対パスを返す(副作用なし) */
8
+ resolvePath(unit: string, phase: DesignPhase): string;
9
+
10
+ /** 書き込み先が既に存在するか */
11
+ exists(unit: string, phase: DesignPhase): Promise<boolean>;
12
+
13
+ /**
14
+ * 設計文書を書き込む(親ディレクトリが無ければ作成)。
15
+ * 書き込んだ絶対パスを返す。
16
+ */
17
+ write(unit: string, phase: DesignPhase, content: string): Promise<string>;
18
+ }
@@ -0,0 +1,15 @@
1
+ // @unit ci-governance
2
+ // @layer domain
3
+
4
+ import type { DesignPhase } from '../value-objects/design-phase.js';
5
+
6
+ export interface TemplateRepositoryPort {
7
+ /**
8
+ * 指定した phase に対応するテンプレートファイルを読み込む。
9
+ * 存在しない場合は例外を投げる。
10
+ */
11
+ read(phase: DesignPhase): Promise<string>;
12
+
13
+ /** テンプレートの絶対パスを返す(UI 出力用) */
14
+ resolvePath(phase: DesignPhase): string;
15
+ }
@@ -0,0 +1,65 @@
1
+ // @unit ci-governance
2
+ // @layer domain
3
+
4
+ export const DESIGN_PHASES = [
5
+ 'logical',
6
+ 'domain',
7
+ 'uiux',
8
+ 'unit-test',
9
+ 'it-test',
10
+ ] as const;
11
+
12
+ export type DesignPhaseValue = (typeof DESIGN_PHASES)[number];
13
+
14
+ export class DesignPhase {
15
+ private constructor(readonly value: DesignPhaseValue) {}
16
+
17
+ static create(input: string): DesignPhase {
18
+ if (!DESIGN_PHASES.includes(input as DesignPhaseValue)) {
19
+ throw new Error(
20
+ `未知の設計 phase: "${input}"。許容値: ${DESIGN_PHASES.join(', ')}`,
21
+ );
22
+ }
23
+ return new DesignPhase(input as DesignPhaseValue);
24
+ }
25
+
26
+ static isValid(input: string): input is DesignPhaseValue {
27
+ return (DESIGN_PHASES as readonly string[]).includes(input);
28
+ }
29
+
30
+ equals(other: DesignPhase): boolean {
31
+ return this.value === other.value;
32
+ }
33
+
34
+ /** このフェーズに対応するテンプレートファイル名(`docs/templates/` 配下) */
35
+ get templateFileName(): string {
36
+ switch (this.value) {
37
+ case 'logical':
38
+ return 'logical_design.template.md';
39
+ case 'domain':
40
+ return 'domain_model.template.md';
41
+ case 'uiux':
42
+ return 'uiux_design.template.md';
43
+ case 'unit-test':
44
+ return 'unit_test_design.template.md';
45
+ case 'it-test':
46
+ return 'it_test_design.template.md';
47
+ }
48
+ }
49
+
50
+ /** このフェーズで書き込む設計文書のファイル名(`docs/product/construction/{unit}/` 配下) */
51
+ get designDocFileName(): string {
52
+ switch (this.value) {
53
+ case 'logical':
54
+ return 'logical_design.md';
55
+ case 'domain':
56
+ return 'domain_model.md';
57
+ case 'uiux':
58
+ return 'uiux_design.md';
59
+ case 'unit-test':
60
+ return 'unit_test_design.md';
61
+ case 'it-test':
62
+ return 'it_test_design.md';
63
+ }
64
+ }
65
+ }
@@ -0,0 +1,42 @@
1
+ // @unit ci-governance
2
+ // @layer infrastructure
3
+
4
+ import * as fs from 'node:fs/promises';
5
+ import * as path from 'node:path';
6
+ import type { DesignDocWriterPort } from '../../domain/ports/design-doc-writer-port.js';
7
+ import type { DesignPhase } from '../../domain/value-objects/design-phase.js';
8
+
9
+ export class FileSystemDesignDocWriterAdapter implements DesignDocWriterPort {
10
+ private readonly baseDir: string;
11
+ private readonly constructionDir: string;
12
+
13
+ constructor(
14
+ baseDir: string,
15
+ constructionSubDir: string = path.join('docs', 'product', 'construction'),
16
+ ) {
17
+ this.baseDir = baseDir;
18
+ this.constructionDir = path.isAbsolute(constructionSubDir)
19
+ ? constructionSubDir
20
+ : path.join(baseDir, constructionSubDir);
21
+ }
22
+
23
+ resolvePath(unit: string, phase: DesignPhase): string {
24
+ return path.join(this.constructionDir, unit, phase.designDocFileName);
25
+ }
26
+
27
+ async exists(unit: string, phase: DesignPhase): Promise<boolean> {
28
+ try {
29
+ await fs.access(this.resolvePath(unit, phase));
30
+ return true;
31
+ } catch {
32
+ return false;
33
+ }
34
+ }
35
+
36
+ async write(unit: string, phase: DesignPhase, content: string): Promise<string> {
37
+ const targetPath = this.resolvePath(unit, phase);
38
+ await fs.mkdir(path.dirname(targetPath), { recursive: true });
39
+ await fs.writeFile(targetPath, content, 'utf-8');
40
+ return targetPath;
41
+ }
42
+ }
@@ -0,0 +1,35 @@
1
+ // @unit ci-governance
2
+ // @layer infrastructure
3
+
4
+ import * as fs from 'node:fs/promises';
5
+ import * as path from 'node:path';
6
+ import type { TemplateRepositoryPort } from '../../domain/ports/template-repository-port.js';
7
+ import type { DesignPhase } from '../../domain/value-objects/design-phase.js';
8
+
9
+ export class FileSystemTemplateRepositoryAdapter implements TemplateRepositoryPort {
10
+ private readonly templatesDir: string;
11
+
12
+ constructor(harnessRoot: string, subDir: string = 'templates') {
13
+ this.templatesDir = path.isAbsolute(subDir)
14
+ ? subDir
15
+ : path.join(harnessRoot, subDir);
16
+ }
17
+
18
+ resolvePath(phase: DesignPhase): string {
19
+ return path.join(this.templatesDir, phase.templateFileName);
20
+ }
21
+
22
+ async read(phase: DesignPhase): Promise<string> {
23
+ const filePath = this.resolvePath(phase);
24
+ try {
25
+ return await fs.readFile(filePath, 'utf-8');
26
+ } catch (err) {
27
+ if ((err as NodeJS.ErrnoException).code === 'ENOENT') {
28
+ throw new Error(
29
+ `テンプレートが見つかりません: ${filePath} (phase=${phase.value})`,
30
+ );
31
+ }
32
+ throw err;
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,97 @@
1
+ // @unit ci-governance
2
+ // @layer presentation
3
+
4
+ import { DesignPhase } from '../../domain/value-objects/design-phase.js';
5
+ import type { ScaffoldDesignUseCase } from '../../application/usecases/scaffold-design-usecase.js';
6
+
7
+ export interface ScaffoldDesignHandlerArgs {
8
+ readonly unit?: string;
9
+ readonly phase?: string;
10
+ readonly force?: boolean;
11
+ readonly format?: 'human' | 'json';
12
+ }
13
+
14
+ export interface ScaffoldDesignHandlerResult {
15
+ readonly exitCode: number;
16
+ readonly output: string;
17
+ }
18
+
19
+ export class ScaffoldDesignHandler {
20
+ constructor(private readonly useCase: ScaffoldDesignUseCase) {}
21
+
22
+ async handle(args: ScaffoldDesignHandlerArgs): Promise<ScaffoldDesignHandlerResult> {
23
+ const format = args.format ?? 'human';
24
+
25
+ if (!args.unit || args.unit.trim().length === 0) {
26
+ return this.fail(format, '--unit <unit-id> は必須です', 2);
27
+ }
28
+ if (!args.phase || args.phase.trim().length === 0) {
29
+ return this.fail(
30
+ format,
31
+ `--phase <name> は必須です(許容値: logical, domain, uiux, unit-test, it-test)`,
32
+ 2,
33
+ );
34
+ }
35
+ if (!DesignPhase.isValid(args.phase)) {
36
+ return this.fail(
37
+ format,
38
+ `未知の phase: "${args.phase}"(許容値: logical, domain, uiux, unit-test, it-test)`,
39
+ 2,
40
+ );
41
+ }
42
+
43
+ let result;
44
+ try {
45
+ result = await this.useCase.execute({
46
+ unit: args.unit,
47
+ phase: args.phase,
48
+ force: args.force,
49
+ });
50
+ } catch (err) {
51
+ const msg = err instanceof Error ? err.message : String(err);
52
+ return this.fail(format, `scaffold 失敗: ${msg}`, 2);
53
+ }
54
+
55
+ if (format === 'json') {
56
+ return {
57
+ exitCode: result.alreadyExists && !result.written ? 2 : 0,
58
+ output: JSON.stringify(result, null, 2),
59
+ };
60
+ }
61
+
62
+ if (result.alreadyExists && !result.written) {
63
+ return {
64
+ exitCode: 2,
65
+ output: [
66
+ `既に存在します: ${result.targetPath}`,
67
+ '上書きするには --force を指定してください。',
68
+ ].join('\n'),
69
+ };
70
+ }
71
+
72
+ const headerVerb = result.overwritten ? '上書き保存しました' : '生成しました';
73
+ return {
74
+ exitCode: 0,
75
+ output: [
76
+ `設計文書を${headerVerb}: ${result.targetPath}`,
77
+ `テンプレ: ${result.templatePath}`,
78
+ `Unit: ${result.unit} / phase: ${result.phase}`,
79
+ 'TODO プレースホルダを実体で埋めてください。',
80
+ ].join('\n'),
81
+ };
82
+ }
83
+
84
+ private fail(
85
+ format: 'human' | 'json',
86
+ message: string,
87
+ exitCode: number,
88
+ ): ScaffoldDesignHandlerResult {
89
+ if (format === 'json') {
90
+ return {
91
+ exitCode,
92
+ output: JSON.stringify({ error: message }, null, 2),
93
+ };
94
+ }
95
+ return { exitCode, output: message };
96
+ }
97
+ }
@@ -44,7 +44,7 @@ export const L2_ERROR_DEFINITIONS = Object.freeze([
44
44
  "const requiredPlanPath = 'docs/inception/harness-error/it_test_logic_plan.md';",
45
45
  defaultSuggestedSkill: '/story-implementor',
46
46
  defaultScaffoldCommand: 'npx phasegate scaffold-design --unit <unit-id> --phase logical',
47
- defaultTemplatePath: 'docs/templates/logical_design.template.md',
47
+ defaultTemplatePath: 'templates/logical_design.template.md',
48
48
  }),
49
49
  createDefinition({
50
50
  code: 'L2-002',
@@ -89,6 +89,7 @@ Commands:
89
89
  ci:migrate-agents-md Migrate AGENTS.md (--dry-run, --validate-only, --json)
90
90
  ci:check-repetition Check error repetition (--code <errorCode>, --reset, --json)
91
91
  baseline Create retrofit baseline snapshot (--dry-run, --force, --paths <glob,glob,...>, --json)
92
+ scaffold-design Scaffold a design doc (--unit <id>, --phase <logical|domain|uiux|unit-test|it-test>, --force, --json)
92
93
 
93
94
  skill:execute-tdd-cycle Execute TDD cycle (--unit, --story, --desc, --phase RED|GREEN|REFACTOR, --passed)
94
95
  skill:check-coverage Check coverage (--story <storyId>, --json)
@@ -917,6 +918,23 @@ Examples:
917
918
  break;
918
919
  }
919
920
 
921
+ case 'scaffold-design': {
922
+ const mod = buildCiGovernance(rootDir, harnessRoot);
923
+ const unit = parseFlag(args, '--unit') ?? '';
924
+ const phase = parseFlag(args, '--phase') ?? '';
925
+ const force = hasFlag(args, '--force');
926
+ const format = json ? 'json' : 'human';
927
+ const result = await mod.scaffoldDesignHandler.handle({
928
+ unit,
929
+ phase,
930
+ force,
931
+ format,
932
+ });
933
+ console.log(result.output);
934
+ process.exit(result.exitCode);
935
+ break;
936
+ }
937
+
920
938
  // ── skill-quality ──
921
939
  case 'skill:execute-tdd-cycle': {
922
940
  const mod = createSkillQualityHandlers();
@@ -278,9 +278,9 @@ scripts/init_skill.py <skill-name> --path <output-directory> [--resources script
278
278
  Examples:
279
279
 
280
280
  ```bash
281
- scripts/init_skill.py my-skill --path skills/public
282
- scripts/init_skill.py my-skill --path skills/public --resources scripts,references
283
- scripts/init_skill.py my-skill --path skills/public --resources scripts --examples
281
+ scripts/init_skill.py my-skill --path skills
282
+ scripts/init_skill.py my-skill --path skills --resources scripts,references
283
+ scripts/init_skill.py my-skill --path skills --resources scripts --examples
284
284
  ```
285
285
 
286
286
  The script:
@@ -6,9 +6,9 @@ Usage:
6
6
  init_skill.py <skill-name> --path <path> [--resources scripts,references,assets] [--examples]
7
7
 
8
8
  Examples:
9
- init_skill.py my-new-skill --path skills/public
10
- init_skill.py my-new-skill --path skills/public --resources scripts,references
11
- init_skill.py my-api-helper --path skills/private --resources scripts --examples
9
+ init_skill.py my-new-skill --path skills
10
+ init_skill.py my-new-skill --path skills --resources scripts,references
11
+ init_skill.py my-api-helper --path skills --resources scripts --examples
12
12
  init_skill.py custom-skill --path /custom/location
13
13
  """
14
14
 
@@ -6,8 +6,8 @@ Usage:
6
6
  python utils/package_skill.py <path/to/skill-folder> [output-directory]
7
7
 
8
8
  Example:
9
- python utils/package_skill.py skills/public/my-skill
10
- python utils/package_skill.py skills/public/my-skill ./dist
9
+ python utils/package_skill.py skills/my-skill
10
+ python utils/package_skill.py skills/my-skill ./dist
11
11
  """
12
12
 
13
13
  import sys
@@ -87,8 +87,8 @@ def main():
87
87
  if len(sys.argv) < 2:
88
88
  print("Usage: python utils/package_skill.py <path/to/skill-folder> [output-directory]")
89
89
  print("\nExample:")
90
- print(" python utils/package_skill.py skills/public/my-skill")
91
- print(" python utils/package_skill.py skills/public/my-skill ./dist")
90
+ print(" python utils/package_skill.py skills/my-skill")
91
+ print(" python utils/package_skill.py skills/my-skill ./dist")
92
92
  sys.exit(1)
93
93
 
94
94
  skill_path = sys.argv[1]
@@ -0,0 +1,51 @@
1
+ ---
2
+ traceability:
3
+ initial_creation: true
4
+ ---
5
+
6
+ # ドメインモデル: {{unit}}
7
+
8
+ > **対応ストーリー**: <HXX-XX, HYY-YY>
9
+ > **作成日**: <YYYY-MM-DD>
10
+ > **Unit**: {{unit}}
11
+
12
+ ---
13
+
14
+ ## 集約 / エンティティ / 値オブジェクト
15
+
16
+ @story-id <HXX-XX>
17
+ ### <集約名>
18
+
19
+ **責務**: TODO: この集約が持つ責務
20
+
21
+ **不変条件**:
22
+ - TODO: 不変条件 1
23
+ - TODO: 不変条件 2
24
+
25
+ **関連**:
26
+ - TODO: 他集約・エンティティへの参照
27
+
28
+ ---
29
+
30
+ ## ドメインサービス
31
+
32
+ @story-id <HXX-XX>
33
+ ### <ドメインサービス名>
34
+
35
+ **責務**: TODO: このサービスが担う domain ロジック
36
+
37
+ **入出力**:
38
+ - 入力: TODO
39
+ - 出力: TODO
40
+
41
+ ---
42
+
43
+ ## ポート(依存反転インタフェース)
44
+
45
+ @story-id <HXX-XX>
46
+ ### <Port 名>
47
+
48
+ **役割**: TODO: domain が infrastructure に求める契約
49
+
50
+ **メソッド**:
51
+ - `methodName(arg: Type): ReturnType` — TODO
@@ -0,0 +1,38 @@
1
+ ---
2
+ traceability:
3
+ initial_creation: true
4
+ ---
5
+
6
+ # IT(Integration)テスト設計: {{unit}}
7
+
8
+ > **対応ストーリー**: <HXX-XX, HYY-YY>
9
+ > **作成日**: <YYYY-MM-DD>
10
+ > **Unit**: {{unit}}
11
+
12
+ ---
13
+
14
+ ## 結合スコープ
15
+
16
+ - TODO: 結合対象(UseCase + Repository、Handler + UseCase など)
17
+ - TODO: 外部依存(DB / API / FileSystem)の扱い
18
+
19
+ ---
20
+
21
+ ## テストケース一覧
22
+
23
+ @story-id <HXX-XX>
24
+ ### IT-{{unit}}-001: <ケース名>
25
+
26
+ **対象**: TODO: 結合対象
27
+ **Arrange**: TODO: テンポラリ環境/フィクスチャ
28
+ **Act**: TODO: 実行する操作
29
+ **Assert**: TODO: 検証内容
30
+ **副作用検証**: TODO: 書き込み/ログ等の副作用確認
31
+
32
+ ---
33
+
34
+ ## テスト環境
35
+
36
+ - テンポラリディレクトリ: `fs.mkdtemp` ベース
37
+ - ネットワーク: 禁止(外部 API は fake/stub)
38
+ - クリーンアップ: afterEach で確実に破棄
@@ -3,17 +3,17 @@ traceability:
3
3
  initial_creation: true
4
4
  ---
5
5
 
6
- # 論理設計: <Unit名>
6
+ # 論理設計: {{unit}}
7
7
 
8
8
  > **対応ストーリー**: <HXX-XX, HYY-YY>
9
9
  > **作成日**: <YYYY-MM-DD>
10
- > **Unit**: <UNIT_NAME>
10
+ > **Unit**: {{unit}}
11
11
 
12
12
  ---
13
13
 
14
14
  ## 概要
15
15
 
16
- <このUnitが解決するビジネス課題を 2〜3 行で記述>
16
+ TODO: Unit `{{unit}}` が解決するビジネス課題を 2〜3 行で記述
17
17
 
18
18
  ---
19
19
 
@@ -22,7 +22,7 @@ traceability:
22
22
  @story-id <HXX-XX>
23
23
  ### <集約 / エンティティ / 値オブジェクト名>
24
24
 
25
- <不変条件・責務・関連の説明>
25
+ TODO: 不変条件・責務・関連の説明
26
26
 
27
27
  ---
28
28
 
@@ -36,13 +36,13 @@ traceability:
36
36
  **So that** <得られる価値>
37
37
 
38
38
  #### 事前条件
39
- - <前提>
39
+ - TODO: 前提
40
40
 
41
41
  #### 基本フロー
42
- 1. <ステップ>
42
+ 1. TODO: ステップ
43
43
 
44
44
  #### 例外フロー
45
- - <エラーケース>
45
+ - TODO: エラーケース
46
46
 
47
47
  ---
48
48
 
@@ -74,6 +74,3 @@ presentation/ : CLI Handler / Controller
74
74
  - 直後に設計要素(空行を挟まない)
75
75
  - `HXX-XX` は `docs/product/user_stories.md` に存在する ID
76
76
  - 複数ストーリー時は連続並列で並べ、最後の直後に設計要素
77
-
78
- これらは `MetadataValidator.validateDesignDocument` で検証され、
79
- ISSUE-008 Phase B-2 / B-3 以降は `npx phasegate validate-metadata` / pre-commit で自動チェックされる。
@@ -0,0 +1,45 @@
1
+ ---
2
+ traceability:
3
+ initial_creation: true
4
+ ---
5
+
6
+ # UI/UX 設計: {{unit}}
7
+
8
+ > **対応ストーリー**: <HXX-XX, HYY-YY>
9
+ > **作成日**: <YYYY-MM-DD>
10
+ > **Unit**: {{unit}}
11
+
12
+ ---
13
+
14
+ ## ユーザーフロー
15
+
16
+ @story-id <HXX-XX>
17
+ ### <フロー名>
18
+
19
+ 1. TODO: ユーザー操作 1
20
+ 2. TODO: システム応答
21
+ 3. TODO: 画面遷移
22
+
23
+ ---
24
+
25
+ ## 画面構成
26
+
27
+ @story-id <HXX-XX>
28
+ ### <画面名>
29
+
30
+ **役割**: TODO: この画面が提供する価値
31
+
32
+ **主要コンポーネント**:
33
+ - TODO: コンポーネント 1
34
+ - TODO: コンポーネント 2
35
+
36
+ **状態**:
37
+ - loading / success / error / empty
38
+
39
+ ---
40
+
41
+ ## アクセシビリティ
42
+
43
+ - TODO: キーボード操作
44
+ - TODO: スクリーンリーダ対応
45
+ - TODO: カラーコントラスト
@@ -0,0 +1,39 @@
1
+ ---
2
+ traceability:
3
+ initial_creation: true
4
+ ---
5
+
6
+ # Unit テスト設計: {{unit}}
7
+
8
+ > **対応ストーリー**: <HXX-XX, HYY-YY>
9
+ > **作成日**: <YYYY-MM-DD>
10
+ > **Unit**: {{unit}}
11
+
12
+ ---
13
+
14
+ ## テストケース一覧
15
+
16
+ @story-id <HXX-XX>
17
+ ### UT-{{unit}}-001: <ケース名>
18
+
19
+ **対象**: TODO: target クラス/関数
20
+ **Arrange**: TODO: 前提条件セットアップ
21
+ **Act**: TODO: 実行する操作
22
+ **Assert**: TODO: 検証内容
23
+ **期待結果**: TODO: 期待値
24
+
25
+ ---
26
+
27
+ ## カバレッジ方針
28
+
29
+ - 分岐カバレッジ 90% 以上
30
+ - ドメイン層のモック禁止
31
+ - 値オブジェクトの不変条件は個別にテスト
32
+
33
+ ---
34
+
35
+ ## テスト命名規約
36
+
37
+ - ケース名: 日本語で記述
38
+ - ファイル名: kebab-case(例: `user-entity.test.ts`)
39
+ - AAA パターン準拠