phasegate 0.264.0 → 0.315.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.
- package/CHANGELOG.md +54 -0
- package/docs/ADR/017-warning-severity-aggregation.md +17 -0
- package/docs/ADR/038-config-state-operation-permission-policy.md +78 -0
- package/docs/guide/installation.md +1 -1
- package/docs/guide/layer-model.md +2 -0
- package/docs/guide/quick-vs-full-mode.md +28 -3
- package/docs/guide/troubleshooting.md +37 -0
- package/docs/templates/agent-context/CLAUDE.md.template.md +6 -6
- package/docs/templates/ci/aidlc-gate.yml +22 -4
- package/package.json +2 -2
- package/scripts/harness/agent-integration/application/usecases/handle-pre-tool-use-usecase.ts +98 -18
- package/scripts/harness/agent-integration/domain/services/bash-write-target-extractor.ts +25 -3
- package/scripts/harness/agent-integration/infrastructure/adapters/file-system-full-mode-session-query-adapter.ts +75 -23
- package/scripts/harness/agent-integration/infrastructure/adapters/harness-config-config-query-adapter.ts +45 -27
- package/scripts/harness/agent-integration/presentation/post-tool-use-hook.ts +29 -16
- package/scripts/harness/agent-integration/presentation/pre-tool-use-hook.ts +51 -6
- package/scripts/harness/agent-integration/presentation/stop-hook.ts +35 -26
- package/scripts/harness/ci-governance/composition-root.ts +2 -2
- package/scripts/harness/ci-governance/domain/services/claude-md-composer.ts +20 -11
- package/scripts/harness/ci-governance/presentation/handlers/check-repetition-handler.ts +10 -2
- package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +5 -1
- package/scripts/harness/config-foundation/domain/harness-config.ts +10 -7
- package/scripts/harness/config-foundation/domain/services/preset-resolution-service.ts +4 -1
- package/scripts/harness/config-foundation/domain/value-objects/project-config.ts +34 -18
- package/scripts/harness/config-foundation/infrastructure/presets/minimal.json +1 -1
- package/scripts/harness/config-foundation/infrastructure/presets/standard.json +1 -1
- package/scripts/harness/config-foundation/infrastructure/presets/strict.json +1 -1
- package/scripts/harness/config-foundation/infrastructure/repositories/file-system-config-repository.ts +27 -18
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +1 -8
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +1 -8
- package/scripts/harness/harness-api/domain/ports/config-query-port.ts +11 -1
- package/scripts/harness/harness-api/domain/services/command-dispatch-service.ts +135 -87
- package/scripts/harness/harness-api/domain/services/status-derivation-service.ts +31 -21
- package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +2 -22
- package/scripts/harness/harness-api/domain/value-objects/harness-status-summary.ts +23 -8
- package/scripts/harness/harness-api/infrastructure/adapters/biome-ast-engine-lint-adapter.ts +4 -4
- package/scripts/harness/harness-api/infrastructure/adapters/harness-config-query-adapter.ts +79 -34
- package/scripts/harness/harness-error/application/dto/create-harness-error-input.ts +3 -1
- package/scripts/harness/harness-error/application/dto/harness-error-contract.ts +3 -1
- package/scripts/harness/harness-error/application/mappers/harness-error-contract-mapper.ts +9 -17
- package/scripts/harness/harness-error/application/usecases/create-harness-error-use-case.ts +7 -7
- package/scripts/harness/harness-error/domain/services/harness-error-factory.ts +30 -33
- package/scripts/harness/harness-error/domain/value-objects/error-definition.ts +28 -17
- package/scripts/harness/harness-error/domain/value-objects/harness-error.ts +30 -9
- package/scripts/harness/harness-error/domain/value-objects/remediation-type.ts +30 -0
- package/scripts/harness/harness-error/infrastructure/registry/l2-error-definitions.ts +44 -29
- package/scripts/harness/harness-error/infrastructure/registry/l3-error-definitions.ts +44 -27
- package/scripts/harness/harness-error/infrastructure/registry/l4-error-definitions.ts +47 -32
- package/scripts/harness/installation/application/checks/claude-context-missing-check.ts +13 -7
- package/scripts/harness/installation/application/checks/config-status-check.ts +52 -0
- package/scripts/harness/installation/application/checks/husky-pre-commit-missing-check.ts +6 -0
- package/scripts/harness/installation/application/ports/config-status-probe-port.ts +9 -0
- package/scripts/harness/installation/application/usecases/run-doctor-diagnostics.ts +30 -8
- package/scripts/harness/installation/application/usecases/run-install.ts +234 -53
- package/scripts/harness/installation/application/usecases/run-reconcile.ts +311 -70
- package/scripts/harness/installation/composition-root.ts +13 -3
- package/scripts/harness/installation/domain/check-id.ts +2 -0
- package/scripts/harness/installation/domain/config-status.ts +17 -0
- package/scripts/harness/installation/domain/deployment-manifest.ts +43 -0
- package/scripts/harness/installation/domain/ports/heuristic-check.ts +10 -1
- package/scripts/harness/installation/infrastructure/adapters/config-status-probe-adapter.ts +79 -0
- package/scripts/harness/installation/presentation/cli/doctor-handler.ts +6 -1
- package/scripts/harness/installation/presentation/formatters/diagnostic-report-formatter.ts +19 -5
- package/scripts/harness/integrations/pre-commit.ts +17 -3
- package/scripts/harness/main.ts +83 -15
- package/scripts/harness/phase-dependency-model/infrastructure/filesystem/markdown-plan-document-reader.ts +60 -32
- package/scripts/harness/phase2-extensions/presentation/handlers/check-freshness-handler.ts +17 -9
- package/scripts/harness/quick-mode/application/ports/file-existence-port.ts +15 -0
- package/scripts/harness/quick-mode/application/usecases/classify-change-category-usecase.ts +60 -22
- package/scripts/harness/quick-mode/composition-root.ts +25 -15
- package/scripts/harness/quick-mode/domain/services/quick-mode-judgment-engine.ts +72 -3
- package/scripts/harness/quick-mode/infrastructure/adapters/fs-file-existence-adapter.ts +38 -0
- package/scripts/harness/skill-quality/infrastructure/adapters/file-system-requirement-test-matrix-adapter.ts +51 -8
- package/scripts/harness/skill-quality/presentation/handlers/check-coverage-handler.ts +13 -6
- package/scripts/harness/traceability-model/domain/value-objects/work-item-frontmatter.ts +5 -1
- package/scripts/harness/traceability-model/infrastructure/gateways/file-system-work-item-identity-gateway.ts +6 -1
- package/scripts/harness/traceability-model/infrastructure/gateways/file-system-work-item-status-gateway.ts +15 -2
- package/scripts/harness/validator-system/application/use-cases/aggregate-validation-results-usecase.ts +11 -14
- package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +32 -7
- package/scripts/harness/validator-system/composition-root.ts +4 -1
- package/scripts/harness/validator-system/domain/ports/ac-coverage-policy-port.ts +10 -1
- package/scripts/harness/validator-system/domain/services/effective-severity-policy.ts +39 -0
- package/scripts/harness/validator-system/domain/value-objects/consistency-report.ts +6 -4
- package/scripts/harness/validator-system/domain/value-objects/drift-report.ts +12 -7
- package/scripts/harness/validator-system/domain/value-objects/validation-result.ts +11 -3
- package/scripts/harness/validator-system/infrastructure/adapters/file-system-security-pattern-scanner-adapter.ts +20 -17
- package/scripts/harness/validator-system/infrastructure/adapters/harness-config-validator-config-adapter.ts +89 -3
- package/scripts/harness/validator-system/infrastructure/adapters/nyquist-ac-coverage-policy-adapter.ts +49 -20
- package/scripts/harness/validator-system/infrastructure/adapters/phase-dependency-phase-gate-policy-adapter.ts +35 -14
- package/scripts/harness/validator-system/infrastructure/adapters/traceability-metadata-policy-adapter.ts +13 -8
- package/scripts/harness/validator-system/presentation/formatters/agent-validation-result-formatter.ts +19 -10
- package/skills/quick-implementor/SKILL.md +19 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,8 +7,62 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **WI-335 — エラー案内の機械往復保証(remediationType 分類 + round-trip テスト)** — suggested action が本当に「従えば直る」かを人間レビュー頼みにせず CI で保証する仕組み。HarnessError に optional `remediationType`(mechanical / ai-assisted / manual、未設定は manual 扱い)を導入し、代表 validator を分類(L2-002 metadata=mechanical、L3-003 レポート不在=mechanical・閾値未達=ai-assisted、L3-001=manual、L4-001/002=ai-assisted)。mechanical 宣言されたエラーは「エラー → suggestion 文言を機械適用 → 再実行 → pass」の往復テストで固定し、案内文言と実挙動が乖離すると(#37/#39 型の破れ)テストが落ちる構造にした。
|
|
13
|
+
|
|
14
|
+
- **WI-329 — 実分布リリースゲート(tarball release-smoke)** — 単体テスト全 green でも新規インストール即クラッシュ(#34)・非 TS リポで fail-closed(#37/#39)・dead flag(#36)がすり抜けた教訓から、`npm pack` した tarball を実分布相当 fixture(純 Python / Go monorepo / docs のみ)にクリーンインストールし install → doctor → validate → uninstall を実走する E2E を追加。通常 suite では skip(`PHASEGATE_RELEASE_SMOKE=1` ガードで hermetic 性維持)し、CI の新 job `release-smoke` が pack job の実バイト列 artifact に対して実行する。`--with-husky` のフラグ有効性 assert で dead-flag 再発も検知。
|
|
15
|
+
|
|
10
16
|
### Fixed
|
|
11
17
|
|
|
18
|
+
- **WI-334 — CI workflow ファイルの change-category 分類取りこぼしを修正** — `.github/workflows/*.yml` の新規作成が CREATE→feature フォールバックに落ちて pre-tool-use hook にブロックされ、かつ .github/ は unit を持たないため案内される story-implementor 経路が構造的に完遂不能だった(WI-329 のドッグフードで発見。MODIFY は bugfix で通るため防御としても不整合)。WI-261(skills/**/*.md → docs)と同型の明示ルールで `.github/workflows/` 配下の yml/yaml を config に分類(内容防御は L3-006 + integrity pin が担当)。併せて CLI `check-change-category` の changeKind をファイル存在で推定し hook 判定と一致させた(hook の Bash 経路は従来挙動を完全維持)。
|
|
19
|
+
|
|
20
|
+
- **WI-333 — config 不在時の hook 全遮断デッドロックを解消(ADR-038 G1 / GitHub #40 完全解消)** — config **不在**(ENOENT)で pre-tool-use hook が未捕捉例外→exit 2 となり、config を作成する Write すら遮断される自己修復デッドロックが missing 状態に残っていた(WI-314 は invalid のみ対応)。hook 用 config adapter で ENOENT を警告+既定値の fail-open に変更(EACCES 等の真の異常は従来どおり throw)。gated パスへの書き込みは phase-gate 側が config-foundation 経由の独立 load で fail-closed を維持することをテストで固定。ADR-038 の許可表と G1 を解消済みに更新。
|
|
21
|
+
|
|
22
|
+
- **WI-330 — doctor の config 状態可視化と許可ポリシーの仕様化(GitHub #40 恒久化)** — doctor が config 不在・不正でも他項目が揃っていれば GREEN を報告していた盲点を解消。新 check `config-status` を追加し、missing → warn(「既定 fail-open で動作中、`phasegate init` で生成可」)、invalid-json / invalid-schema → red(修復の一手つき)、JSON・human 出力に `configStatus` を明示。「config 状態 × 操作クラス」の許可表を ADR-038 として仕様化し、既知ギャップも正直に記録(**G1: config 不在時に pre-tool-use hook が ENOENT 未捕捉で全ツール遮断=自己修復デッドロックが missing 状態では現存**、G2: invalid-json の検査系が fail-open で原則より緩い)。G1 は WI-333 で修正。
|
|
23
|
+
|
|
24
|
+
- **WI-331 — CLAUDE.md の user-section を managed block の外へ移す構造修正(GitHub #35 根本修正)** — #35 の根本原因は CLAUDE.md テンプレートだけ user-section が managed block の内側にあり(AGENTS.md は外側)、block 置換のたびに WI-315 の「抽出→再注入」に頼っていたこと。テンプレートを AGENTS.md と対称の外側配置に変更し、install / reconcile は旧構造(内側)を検出すると本文を保持したまま外側へ移設する冪等 migration を実行(2 回目適用で byte 同値をテスト固定)。refresh 経路(claude-md-composer)は新テンプレート採用で自動移行、`$` シーケンス保護と空行保持も強化。新構造では uninstall 後も user-section が生存する副次改善あり。
|
|
25
|
+
|
|
26
|
+
- **WI-332 — severity 実効判定の単一ソース化と横断 regression(GitHub #38 恒久化)** — 実効 severity 判定(ADR-017 の isEffectivelyPassed)が harness-api と validator-system に複製され、pre-commit は独自集約で severity を見ておらず warning-only failure が exit 1 になっていた。判定を `validator-system/domain/services/effective-severity-policy.ts` に一本化し、validate 集約・CiCheckResult(ci-check / complete-check)・pre-commit の全経路が同一実装を通る形に統一(pre-commit の warning-only は ADR-017 どおり exit 0 へ)。同一結果セットを 3 経路に通して実効判定の一致を assert する横断 regression を追加し、将来の乖離を機械検知する。
|
|
27
|
+
|
|
28
|
+
- **WI-328 — 実効言語と出所を phasegate:status に表示(GitHub #39 残課題)** — WI-319/320 の言語自動検出の結果がどこにも表示されず、どの validator が有効/SKIP になるか知る術がなかった。`phasegate:status` の JSON に `languages: { effective, source }`(source = `declared` / `detected` / `fallback`)を追加。解決ロジックは validator-system の `resolveProjectLanguages()`(WI-319 の検出テーブル)に一本化して再利用し、validator の有効/SKIP 判定と必ず同じ結果を表示する。ConfigQueryPort への追加は optional メソッドで後方互換。
|
|
29
|
+
|
|
30
|
+
- **WI-327 — 最小 config(project のみ)で動作可能に** — 手書き `phasegate.config.json` のスキーマが top-level 8 項目を required とし、`{"project": {"name": ..., "preset": ...}}` の最小構成で L1-001 が連発していた。プリセット解決(`PresetResolutionService.deepMerge`)は省略セクションの補完を既に完備していたため、v3 **と v2**(architecture キーなしの最小 config は v2 検証に振られる)の top-level required を `["project"]` に緩和し、型定義を実態に一致させた。**検証は弱めていない**: セクション内の required・型・enum・additionalProperties は不変で、書かれているが不正なキーは従来どおりエラー(spawn E2E で exit 2 を固定)。最小 config の解決結果がプリセット定義と一致することも統合テストで固定。
|
|
31
|
+
|
|
32
|
+
- **WI-326 — install フラグ状態を manifest に永続化(GitHub #36 残課題)** — `--with-husky` / `--with-ci` / personal の install 時オプションが `.phasegate/manifest.json` に記録されず、後の reconcile が「全 target 対象」を仮定して opt-out したはずの Husky / CI workflow を追加し直す食い違いがあった。manifest に optional `installationFlags` を追加して install(apply)時に実効フラグを記録し、reconcile は明示指定 > manifest 記録 > 従来挙動の優先順で target を構成する。フィールドを持たない旧 manifest は推測せず従来挙動のまま(load / save round-trip のバイト互換もテストで固定)。
|
|
33
|
+
|
|
34
|
+
- **WI-324 — フレッシュプロジェクトで L3-004 を SKIP に(オンボーディング阻害の解消)** — phasegate 導入直後(story 未作成・requirement-test-matrix 未生成)でも L3-004 が「マトリクス不在」で fail-closed FAIL になり導入体験を阻害していた。「matrix 不在 かつ StoryCatalog(user_stories.md)の story ゼロ」の場合のみ skipWithReason(WI-317 の L3-003 と同表現)で透過 SKIP に変更。**story が 1 件でも存在するのに matrix 不在なら従来どおり fail-closed**(あるべき matrix の消失事故は見逃さない)。story 数の取得に失敗した場合も判定不能として fail-closed 側に倒す。
|
|
35
|
+
|
|
36
|
+
- **WI-323 — 非ゲート hook の payload フィールド欠落を fail-open 化(GitHub #40 残課題)** — stop hook が stdin payload の `session_id` 欠落で exit 2 になり、WI-314 の「hook は開発フローを止めない」方針と不整合だった。stop(`SESSION_ID_MISSING`)と post-tool-use(`TOOL_NAME_MISSING`)を警告 + hook-skip-event 記録 + exit 0 の fail-open に変更。**pre-tool-use の `tool_name` 欠落 exit 2 は書き込みゲートのため意図的に fail-closed を維持**し、回帰ガードテストで固定。usecase 契約(空 sessionId エラー)は presentation 入口ガード方式で不変。
|
|
37
|
+
|
|
38
|
+
- **WI-322 — config なし fallback の coverageThreshold 90 を opt-out の 0 に修正(GitHub #37 残課題)** — WI-317 で「カバレッジゲートはオプトイン、0 = 正規の opt-out」と定義したのに、config なし環境で使われる validator-system の `DEFAULT_CONFIG` fallback が `coverageThreshold: 90` のままで、config を持たない環境だけ勝手に 90% が強制される矛盾を解消。fallback のみの変更でプリセット定義(minimal / standard / strict)は不変。fallback 経由で L3-003 が透過 SKIP になることを回帰テストで固定。
|
|
39
|
+
|
|
40
|
+
- **WI-321 — complete-check の JSON 出力に warning 詳細を含める(GitHub #38 残課題)** — `phasegate:complete-check` が warning を `summary.warnings` の件数のみで返し、どの validator のどんな warning かが出力に含まれなかった非対称を修正。ci-check case と同じく `CiCheckResult` を data ペイロードとして pass / fail 双方で返す(`data.validatorResults[].validatorId / errors[]`)。validator 結果 0 件時は従来どおり data なし。判定ロジック・exit code・lint 合流は不変。
|
|
41
|
+
|
|
42
|
+
- **WI-325 — load 経路の JSON パース失敗が "Failed to persist" と誤表示される問題を修正** — `FileSystemConfigRepository.load()` の JSON 構文エラーが書き込み用の `ConfigPersistenceError`("Failed to persist config")で送出され、読み込み失敗なのに永続化失敗と誤解させるメッセージになっていた。`ConfigParseError extends ConfigPersistenceError` を新設して load 経路を "Failed to parse config JSON" に分離。サブクラス化により main.ts の `instanceof ConfigPersistenceError` fail-open 判定(WI-314)は無変更で互換維持。save 経路は従来どおり。
|
|
43
|
+
|
|
44
|
+
- **WI-320 — 言語自動検出を実 CLI 経路で有効化(GitHub #39 フォローアップ)** — WI-319 の検出は adapter に実装されたが、実 CLI では preset 解決(`preset-resolution-service`)・mapper・`ProjectConfig` VO の 3 箇所が未宣言時に `["typescript"]` を注入するため dead code だった(シナリオ検証で発見)。「未宣言」シグナルを resolved config から adapter まで生存させ、純 Python リポジトリで L3-003 が unsupported-language SKIP になることを実 CLI spawn の E2E で固定。宣言時の挙動・検証は不変。
|
|
45
|
+
|
|
46
|
+
- **WI-319 — project.languages のファイルシステム自動検出(GitHub #39)** — `project.languages` 未宣言時に無条件で `["typescript"]` へフォールバックし、純 Python プロジェクトでも TS 専用 validator(L3-002 / L3-003 / L4-003)が既定で走っていた問題を修正。マーカーファイル(python: pyproject.toml 等 / go.mod / Cargo.toml / pom.xml / Gemfile / composer.json、typescript: tsconfig.json または package.json の typescript 依存のみ — phasegate 導入時の package.json を誤検出しないため存在自体は根拠にしない)から検出し、config 宣言は引き続き最優先、検出ゼロ時は従来どおり typescript フォールバック。
|
|
47
|
+
|
|
48
|
+
- **WI-318 — complete-check に severity-aware 集約を適用(GitHub #38)** — `phasegate:complete-check` が validator の生の `passed` だけを見ており、warning-only failure でも exit 1 になって `validate` / `ci-check` と乖離していた。Stop hook が叩くのは complete-check のため、L2-016 の `ungated-legacy` マーカー(error→可視 warning 降格)が実質無効化されていた。ci-check と同一の `CiCheckResult.fromResults()`(ADR-017 / WI-260 の `isEffectivelyPassed`)を通す集約に修正し、warning 件数を summary に反映。skipped validator が fail 扱いされる潜在バグも同時に解消。lint fail → exit 1 の合流は不変。
|
|
49
|
+
|
|
50
|
+
- **WI-317 — coverageThreshold の opt-out を到達可能に(GitHub #37)** — L3-003 実装は「カバレッジゲートはオプトイン」と明記しつつ、opt-out 手段(`null`)がスキーマで拒否され、`0` を設定してもレポート不在で fail-closed FAIL になる矛盾があった。ドメイン VO `L3Config.hasCoverageGate()` の「threshold > 0 でのみ有効」の意図に run-l3 を揃え、`coverageThreshold: 0`(および null)を正規の opt-out として SKIP にした(minimal preset の 0 も本来の opt-out として機能)。レポート不在 FAIL の suggestion に (a) カバレッジ付き実行 (b) `coverageThreshold: 0` での opt-out (c) 非 JS/TS プロジェクトの `project.languages` 宣言、の 3 択を案内する。
|
|
51
|
+
|
|
52
|
+
- **WI-316 — install の `--with-husky` / `--with-ci` dead flag を修正(GitHub #36)** — install コマンドがこの 2 フラグを一度も読まず、`--personal` なしでは常に `.husky/*` と `.github/workflows/phasegate-aidlc-gate.yml` を書き込んでいた。ヘルプ記載・`init` / `setup:agent` と同じ opt-in(フラグ明示時のみ書き込み)に配線を揃え、CLI 統合テストとドキュメントで固定した。usecase のプログラマティック契約(既定 true)は不変。
|
|
53
|
+
|
|
54
|
+
- **WI-315 — install / reconcile が CLAUDE.md の user-section を上書きする問題を修正(GitHub #35)** — CLAUDE.md テンプレートは user-section が managed-section の内側にあるため、`install --apply` / `reconcile` の managed block 置換がユーザー自身の記述を placeholder で消失させていた。merge 時に既存 user-section 本文を抽出して新 block に再注入する方式で保持するようにした(AGENTS.md の既存挙動は不変)。併せて、ユーザー本文が `String.replace` の置換文字列として `$` シーケンス解釈される潜在破損を replacer 関数で封じた。
|
|
55
|
+
|
|
56
|
+
- **WI-314 — 不正 config の hook / doctor 自己修復デッドロック解消(GitHub #40)** — スキーマ違反の `phasegate.config.json` が dispatch 上流の fail-closed で全コマンドを exit 2 にし、pre-tool-use hook 経由で Bash / Write / Edit を全遮断して config 自身の修復も不能になる問題を修正。`hook` / `doctor` は警告 + 既定設定で続行する fail-open とし(診断・自己修復経路の常時確保)、`validate` / `ci-check` 等の検査系は復旧手順の案内付きで fail-closed を維持。hook 内部 adapter の素 `JSON.parse` throw も同様に fail-open 化し、構文破壊 JSON での再デッドロックを塞いだ。gated スコープへの書き込みは phase-gate 判定が引き続き fail-closed でブロックする。
|
|
57
|
+
|
|
58
|
+
- **WI-313 — typescript peer range を `<7.0.0` に制限(GitHub #34)** — peer 自動インストールで typescript@7 が解決されると classic compiler API(`ts.ScriptTarget` 等)が存在せず全コマンドが `Cannot read properties of undefined (reading 'ESNext')` でクラッシュする問題を修正。TypeScript 7 系を peer range で弾き、packaging contract テストで range を固定した。
|
|
59
|
+
|
|
60
|
+
- **WI-312 — coverage-producing CI gate** — production workflowとbundled `aidlc-gate`がL3-003実行前に`coverage/coverage-summary.json`を生成せず、clean checkoutでcoverageThresholdに対してfail-closedとなる既存欠陥を修正。self-repoはcoverage付きunit / integrationとcoverage対象外E2Eを各1回だけ実行してplain full suiteの二重実行を避け、threads開始時にもforks blobを保持して両poolをmergeする。templateは採用projectの`coverage` scriptをpackage manager別に必須実行し、test / coverage → matrix → World derive二重一致 → L3 → attestation → integrityの順序を維持する。
|
|
61
|
+
|
|
62
|
+
- **WI-311 — hermetic CI-facing harness integration tests** — GitHub Actionsのtest-before-matrix順序で、real lint integrationとCLI E2Eがself-repo cwd、coverage、untracked requirement matrixを暗黙のPASS入力としていた既存欠陥を修正。lint adapterへ明示rootを注入してtracked minimal workspaceだけを実scanし、`phasegate:ci-check --json`とlegacy `complete-check`はL3 / Worldを明示無効化したtemp configで公開command契約を検証する。productionのfail-closedとCI gate順序は変更しない。
|
|
63
|
+
|
|
64
|
+
- **WI-310 — hermetic large-stdout CLI regression** — WI-308の64 KiB超assertionがuntracked requirement matrixとself-repoの604 adopted-legacy warningsへ依存していた欠陥を修正。tracked World-enabled configを使うtemp corpusに160組のduplicate fragment IDを生成し、CIのtest-before-matrix順序でも`validate --layer L2 --format json` / `--json`が決定的に64 KiBを超えて完全parseできる契約へ置換した。
|
|
65
|
+
|
|
12
66
|
- **WI-309 — WI-305 validator domain reflection repair** — `DesignChangeDeclarationPolicy`を実装・配線しながらvalidator-system `domain_model.md`へのWI-305累積反映を欠落させ、story reflection corpusが検出したbaseline外違反を正規に解消。exact `corpusRole + DeclaredKey`照合、pinned change限定、Work-Item trailer intersection、unique sorted finding、local fast-path / L3 authority境界を実コードどおり記録し、baseline / allowlistには触れていない。
|
|
13
67
|
|
|
14
68
|
- **WI-308 — CLI large stdout drain** — self-repoのWorld enforcement有効化により`validate --layer L2 --json`が604件のadopted-legacy warningを含む64 KiB超のJSONとなり、`console.log`直後の`process.exit`がpipe bufferをdrainせず末尾を切断する回帰を修正。direct stdout resultを返す全top-level CLI経路を共通graceful exitへ統合し、warningを省略せず任意サイズの完全な出力と既存exit codeを維持する。64 KiB超を必須条件とするprocess E2Eで`--format json` / `--json`双方を固定し、同E2E fileの全subprocess caseへ明示60秒timeoutを適用した。
|
|
@@ -212,3 +212,20 @@ human/agent/ci formatter で `[FAIL]` と `[WARN]` を分離:
|
|
|
212
212
|
2. **(B) warning も exit 1 を default に保つ(CI 互換性優先)** — default を変えず移行コストはゼロだが、`defaultSeverity: warning` 宣言と「常に fail」実装の semantic 乖離が残るため不採用。
|
|
213
213
|
|
|
214
214
|
採用理由の詳細は上記 Decision「採用理由」を参照。
|
|
215
|
+
|
|
216
|
+
## 追記 (WI-332, 2026-07-18): 実効判定の共有実装と全経路の経由
|
|
217
|
+
|
|
218
|
+
本 ADR の Decision(集計セマンティクス)自体は変更しない。実装配置に関する追記のみ。
|
|
219
|
+
|
|
220
|
+
github#38(`complete-check` だけが独自集約を持ち warning-only failure で exit 1 になった回帰。
|
|
221
|
+
WI-318 で修正)の恒久化として、WI-332 で「実効 severity 判定」を単一の共有実装に集約した:
|
|
222
|
+
|
|
223
|
+
- **共有実装(単一ソース)**: `scripts/harness/validator-system/domain/services/effective-severity-policy.ts` の `isEffectivelyPassed()`。本 ADR Decision の集計セマンティクス(skipped / passed=true は実質 pass、error 含みは fail、errors=[] は防御的に fail、warning-only は `failOnWarning=false` 既定で実質 pass)をそのまま実装する
|
|
224
|
+
- **全経路がこれを経由する**:
|
|
225
|
+
- `validate` 集約 — `validator-system/application/use-cases/aggregate-validation-results-usecase.ts`(WI-332 で複製実装を削除し共有実装を参照)
|
|
226
|
+
- `ci-check` / `complete-check` — `harness-api/domain/value-objects/ci-check-result.ts` の `CiCheckResult.fromResults()`(WI-318 で 2 コマンド共有化、WI-332 で複製実装を削除し共有実装を参照)
|
|
227
|
+
- `pre-commit` / `commit-msg` / `bypass-audit` — `integrations/pre-commit.ts` の `buildReport()` / `classifyValidatorFailure()`(WI-332 で手動集約 `failed === 0` を共有実装経由に変更。pre-commit 経路には `validate.failOnWarning` の config 配線が無いため、既定値 `false` に固定)
|
|
228
|
+
- **例外**: `status` コマンドは「failure を表示しつつ常に exit 0」という独立契約のため、本判定の対象外
|
|
229
|
+
- **回帰防御**: `scripts/harness/__tests__/integration/validator-system/severity-aggregation-consistency.test.ts` が同一の validator 結果セット(全 pass / warning-only / error / mixed / errors=[] / skipped)を 3 経路(validate 集約・CiCheckResult・pre-commit 集約)に通し、実効判定の一致を assert する。どれかの経路が独自判定に戻るとこのテストが落ちる
|
|
230
|
+
|
|
231
|
+
新しい集約経路を追加する場合は、必ず `isEffectivelyPassed()` を経由し、上記横断テストに経路を追加すること(ADR-021 の格下げ禁止契約も併せて参照)。
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
adr_id: "038"
|
|
3
|
+
title: "config 状態と操作クラスの許可ポリシー"
|
|
4
|
+
status: Accepted
|
|
5
|
+
date: 2026-07-18
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# config 状態と操作クラスの許可ポリシー
|
|
9
|
+
|
|
10
|
+
<!-- @work-item-id WI-330 -->
|
|
11
|
+
<!-- @work-item-id WI-333 -->
|
|
12
|
+
|
|
13
|
+
## Context
|
|
14
|
+
|
|
15
|
+
GitHub #40 は「スキーマ違反の `phasegate.config.json` があると pre-tool-use hook が exit 2 で全ツールを遮断し、config を修復する経路自体が消える(自己修復デッドロック)」という障害だった。WI-314 で hook / doctor を fail-open にする応急処置を入れ、WI-323 で hook 側 adapter の JSON parse fail-open、WI-325 で `ConfigParseError` の導入(誤メッセージ修正)を行った。しかし、
|
|
16
|
+
|
|
17
|
+
- (a) doctor は config 不在・不正でも config 状態を diagnostics に含めず、他が揃っていれば GREEN を報告していた。
|
|
18
|
+
- (b) 「config がどの状態のとき、どの操作クラスが許可されるか」を定義した仕様書が存在せず、各修正がアドホックな穴埋めになっていた。
|
|
19
|
+
- (c) config **missing** 系のテストが欠落しており、missing 状態の実挙動は未検証だった。
|
|
20
|
+
|
|
21
|
+
WI-330 で許可表を実測に基づいて仕様化し、doctor に config 状態 check(`config-status`)を追加し、欠落マスをテストで固定した。
|
|
22
|
+
|
|
23
|
+
## Decision
|
|
24
|
+
|
|
25
|
+
### 1. config 状態は 4 値で分類する
|
|
26
|
+
|
|
27
|
+
`missing` / `invalid-json` / `invalid-schema` / `valid`。判定は config-foundation の実 load 経路(`FileSystemConfigRepository` の JSON parse + AJV schema 検証 + preset 解決)をそのまま使う。エラー型との対応:
|
|
28
|
+
|
|
29
|
+
| 状態 | 検出エラー型 | 定義箇所 |
|
|
30
|
+
|------|-------------|---------|
|
|
31
|
+
| missing | `ConfigNotFoundError` | `scripts/harness/config-foundation/infrastructure/repositories/file-system-config-repository.ts:12` |
|
|
32
|
+
| invalid-json | `ConfigParseError`(`ConfigPersistenceError` のサブクラス、WI-325) | 同ファイル `:41` |
|
|
33
|
+
| invalid-schema | `ConfigValidationError`(AJV 違反・未知 preset を含む) | `scripts/harness/config-foundation/domain/errors/config-validation-error.ts:9`、throw 箇所は `load-resolved-config-use-case.ts:71-99` |
|
|
34
|
+
| valid | —(load 成功) | — |
|
|
35
|
+
|
|
36
|
+
config の解決順は project 直下 `phasegate.config.json` → `.phasegate-local/phasegate.config.json`(personal install)で、CLI は cwd から上方探索する(`file-system-config-repository.ts:58-81` `findNearestConfig`)。
|
|
37
|
+
|
|
38
|
+
### 2. 許可表(config 状態 × 操作クラス)
|
|
39
|
+
|
|
40
|
+
2026-07-18 時点の**実測**(`invalid-config-fail-open.integration.test.ts` で固定。missing 列は WI-333 で fail-open 化)。○ = 許可 / ● = 意図したゲート判定で通過・遮断 / × = 遮断。
|
|
41
|
+
|
|
42
|
+
| 操作クラス \ config 状態 | valid | missing | invalid-json | invalid-schema |
|
|
43
|
+
|---|---|---|---|---|
|
|
44
|
+
| **config 自身への編集**(hook 経由 Write/Edit) | ○ fail-open(既定では保護対象外。`protectedFiles.patterns` に含めた場合のみ CLI 誘導 block) | ○ fail-open(WI-333。自己修復経路) | ○ fail-open | ○ fail-open |
|
|
45
|
+
| **gated パス書込**(`scripts/harness/` 等、hook 経由) | ● phase-gate 判定(fail-closed) | ● 既定設定で phase-gate 判定(fail-closed 維持、WI-333) | ● 既定設定で phase-gate 判定(fail-closed 維持) | ● 既定設定で phase-gate 判定(fail-closed 維持) |
|
|
46
|
+
| **無関係パス書込**(hook 経由) | ○ | ○ fail-open(警告付き、WI-333) | ○ fail-open | ○ fail-open |
|
|
47
|
+
| **無関係 Bash**(書込抽出なし、hook 経由) | ○ | ○ fail-open(警告付き、WI-333) | ○ fail-open(警告付き) | ○ fail-open(警告付き) |
|
|
48
|
+
| **検査系コマンド**(`validate` / `ci-check` 等) | ○ 通常実行 | ○ fail-open(既定設定で実行、exit 0) | **○ fail-open(警告付き exit 0)【設計意図より緩い】** | × fail-closed(exit 2 + Recovery 手順) |
|
|
49
|
+
| **hook・doctor 起動** | ○ | ○ fail-open(doctor は `configStatus: missing` → warn。hook は警告 + 既定設定で完走、WI-333) | ○ fail-open(doctor は `configStatus: invalid-json` → red) | ○ fail-open(`CONFIG_FAIL_OPEN_COMMANDS`。doctor は `configStatus: invalid-schema` → red) |
|
|
50
|
+
|
|
51
|
+
コード根拠:
|
|
52
|
+
|
|
53
|
+
- **CLI dispatch 層の fail-open/fail-closed 分岐**: `scripts/harness/main.ts:1867` `CONFIG_FAIL_OPEN_COMMANDS = new Set(["hook", "doctor"])`、`main.ts:1869-1902` `loadResolvedConfig()` — `ConfigValidationError` は hook/doctor のみ fail-open・他は exit 2(`:1878-1889`)、`ConfigNotFoundError` は全コマンド silent fail-open(`:1891-1893`)、`ConfigParseError`(`instanceof ConfigPersistenceError`)は全コマンド警告付き fail-open(`:1894-1900`)。
|
|
54
|
+
- **hook 実行層の fail-open**: `scripts/harness/agent-integration/infrastructure/adapters/harness-config-config-query-adapter.ts` `loadConfig()` — JSON parse 失敗と `fs.readFileSync` の ENOENT(missing、WI-333)はいずれも警告 + 空 config(既定値)で続行。ENOENT **以外**の fs エラー(EACCES / EISDIR 等の真の異常)は従来どおり throw し、`scripts/harness/agent-integration/presentation/pre-tool-use-hook.ts` の outer catch で「実行エラー」exit 2 になる。fallback config path は `pre-tool-use-hook.ts:58-76` `findConfigPath()`(不在時は `startDir/phasegate.config.json` を返す)。
|
|
55
|
+
- **missing の gated パス fail-closed**: hook 側 config は既定値に縮退するが、gated パス書込は `phase-gate-query-adapter.ts` が config-foundation の `ConfigNotFoundError` を generic catch で「評価不能 = NOT passed」に落とすため、phase-gate block(フェーズゲート違反、exit 2)が維持される。
|
|
56
|
+
- **config 自身が gated/protected でない根拠**: 既定の保護パターンに `phasegate.config.json` は含まれない(`scripts/harness/agent-integration/domain/value-objects/protected-file-list.ts:17-27` `DEFAULT_PATTERNS`)。ユーザーが `protectedFiles.patterns` で保護した場合のみ CLI 誘導付き block(`handle-pre-tool-use-usecase.ts:480-495`)。
|
|
57
|
+
- **doctor の config 状態可視化(WI-330 で追加)**: `scripts/harness/installation/infrastructure/adapters/config-status-probe-adapter.ts`(分類)、`installation/application/checks/config-status-check.ts`(missing → warn / invalid-* → red)、`installation/presentation/formatters/diagnostic-report-formatter.ts`(JSON `configStatus` フィールドと human `Config:` 行)。
|
|
58
|
+
|
|
59
|
+
### 3. 設計原則
|
|
60
|
+
|
|
61
|
+
1. **自己修復例外は復旧経路の保証であり、検査系の fail-closed は緩めない。** hook(エージェントのツール遮断点)と doctor(自己診断)は config がどんな状態でも起動・完走できなければならない。逆に `validate` / `ci-check` 等の検査系は、設定が壊れた状態の検査結果を「合格」として流通させないため fail-closed を原則とする。
|
|
62
|
+
2. **fail-open は必ず可視化とセットにする。** fail-open で既定設定に落ちた事実は stderr 警告(CLI/hook)と doctor の `configStatus` + `config-status` finding(missing = warn、invalid-* = red)で必ず表面化させる。「不正 config でも doctor GREEN」は本 ADR をもって仕様違反である。
|
|
63
|
+
3. **gated スコープの fail-closed は config 状態に依存しない。** config が壊れていても、gated パスへの書込は既定設定による phase-gate 判定で引き続きブロックされる(fail-open は「遮断の解除」ではなく「既定設定への縮退」)。
|
|
64
|
+
|
|
65
|
+
### 4. 既知ギャップ(本 ADR は現状を固定し、修正は別 WI)
|
|
66
|
+
|
|
67
|
+
| # | ギャップ | 現状 | あるべき姿 |
|
|
68
|
+
|---|---------|------|-----------|
|
|
69
|
+
| G1 | **missing 状態で pre-tool-use hook が全ツール遮断**(config 自身への Write も遮断 = 自己修復デッドロック復活) | **WI-333 で解消**: `harness-config-config-query-adapter.ts` `loadConfig()` が ENOENT を捕捉し「警告 + 既定値で続行」の fail-open。config 自身への Write / 無関係 Bash は exit 0、gated パス書込は既定設定の phase-gate 判定で fail-closed 維持(`invalid-config-fail-open.integration.test.ts` の WI-333 テストで固定) | invalid-json と同じ「警告 + 既定値で続行」 — 達成済み |
|
|
70
|
+
| G2 | **invalid-json の検査系が fail-open**(invalid-schema より緩い) | `ConfigParseError` が `ConfigPersistenceError` 経路で全コマンド fail-open(exit 0) | 検査系は invalid-schema と同様 fail-closed が原則に忠実。ただし挙動変更は breaking のため要判断 |
|
|
71
|
+
|
|
72
|
+
いずれも `scripts/harness/__tests__/integration/harness-api/invalid-config-fail-open.integration.test.ts` のテストで挙動を固定済み(G1 は fail-open 期待に反転済み)。挙動を修正する際はテストの期待値反転とこの表の更新を同一変更で行うこと。
|
|
73
|
+
|
|
74
|
+
## Consequences
|
|
75
|
+
|
|
76
|
+
- doctor は config 状態を常に報告する(JSON: `configStatus`、human: `Config:` 行、finding: `config-status`)。config 不在・不正のまま GREEN を報告することはなくなる(missing は warn、invalid-* は red で exit 1)。
|
|
77
|
+
- 許可表が仕様となったため、fail-open/fail-closed の変更は本 ADR の改訂を伴う。
|
|
78
|
+
- 経緯: WI-314(hook/doctor fail-open 化)→ WI-323(hook adapter の JSON parse fail-open)→ WI-325(`ConfigParseError` 導入)→ WI-330(本 ADR・doctor 可視化・missing 系テスト固定)→ WI-333(G1 解消: hook adapter の ENOENT fail-open、github#40 完全解消)。
|
|
@@ -56,7 +56,7 @@ npx phasegate install --apply
|
|
|
56
56
|
npx phasegate doctor
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
`install --dry-run` reports whether each target will be created, merged, skipped, or refused. `install --apply` performs the merge, adds package scripts and the `phasegate` devDependency, deploys selected bundled skills to root `skills/`, creates `.claude/skills` and `.codex/skills` links, writes `CLAUDE.md` / `AGENTS.md` managed sections for selected agent targets, writes `.github/workflows/phasegate-aidlc-gate.yml` when CI is enabled, and records managed entries in `.phasegate/manifest.json`. Existing skills catalogs are merged: PhaseGate refreshes only bundled skill directories selected by `--skills core|all` and preserves user-owned skills. See [Setup Artifacts](setup-artifacts.md) for the full managed target, generated artifact, runtime state, legacy artifact, and user-level setting inventory. <!-- @work-item-id WI-152 --> <!-- @work-item-id WI-169 --> <!-- @work-item-id WI-174 --> <!-- @work-item-id WI-216 -->
|
|
59
|
+
`install --dry-run` reports whether each target will be created, merged, skipped, or refused. `install --apply` performs the merge, adds package scripts and the `phasegate` devDependency, deploys selected bundled skills to root `skills/`, creates `.claude/skills` and `.codex/skills` links, writes `CLAUDE.md` / `AGENTS.md` managed sections for selected agent targets, writes `.github/workflows/phasegate-aidlc-gate.yml` when CI is enabled, and records managed entries in `.phasegate/manifest.json`. Husky hook targets (`.husky/*`) and the GitHub Actions target are opt-in, matching `init` and `setup:agent`: pass `--with-husky` and/or `--with-ci` to include them — without these flags `install` does not touch `.husky/` or `.github/workflows/`. <!-- @work-item-id WI-316 --> Existing skills catalogs are merged: PhaseGate refreshes only bundled skill directories selected by `--skills core|all` and preserves user-owned skills. See [Setup Artifacts](setup-artifacts.md) for the full managed target, generated artifact, runtime state, legacy artifact, and user-level setting inventory. <!-- @work-item-id WI-152 --> <!-- @work-item-id WI-169 --> <!-- @work-item-id WI-174 --> <!-- @work-item-id WI-216 -->
|
|
60
60
|
|
|
61
61
|
For personal evaluation inside a team-owned repository:
|
|
62
62
|
|
|
@@ -43,6 +43,8 @@ npx phasegate hook post-tool-use < payload.json
|
|
|
43
43
|
npx phasegate hook stop < payload.json
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
> **Config 状態と許可ポリシー**: `phasegate.config.json` が不在・破損している状態で各 hook / コマンドがどう振る舞うか(fail-open / fail-closed の別、自己修復経路、doctor の `configStatus` 報告)は [ADR-038](../ADR/038-config-state-operation-permission-policy.md) の許可表で仕様化されている。
|
|
47
|
+
|
|
46
48
|
### L0-B: Husky git hooks
|
|
47
49
|
|
|
48
50
|
Deployed by `phasegate init --with-husky` into `.husky/`.
|
|
@@ -71,6 +71,31 @@ npx phasegate session end --work-item <WI-XXX>
|
|
|
71
71
|
|
|
72
72
|
The PreToolUse hook validates `.phasegate/session.json` by TTL, unit, and dominant category. The `/story-implementor` skill is still the design/TDD guide; the session marker is the authorization the hook can actually observe. <!-- @work-item-id WI-206 -->
|
|
73
73
|
|
|
74
|
+
### How a path becomes a category
|
|
75
|
+
|
|
76
|
+
The classifier is **path-based, not intent-based**. It never reads your commit message or your stated purpose; it looks at the write target's path and its change kind (`CREATE` / `MODIFY` / `DELETE`). Rules are evaluated in this order, first match wins:
|
|
77
|
+
|
|
78
|
+
| Order | Match | Category |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| 1 | `*.config.json`, `*.config.ts`, `phasegate.config.json` | `config` |
|
|
81
|
+
| 2 | `.github/workflows/*.yml`, `.github/workflows/*.yaml` | `config` |
|
|
82
|
+
| 3 | Repo-root bootstrap files: `.gitignore`, `.gitattributes`, `.editorconfig`, `.npmrc`, `.nvmrc`, `tsconfig.json`, `tsconfig.*.json`, anything under `.husky/` | `config` |
|
|
83
|
+
| 4 | Comment/whitespace-only diff | `docs` |
|
|
84
|
+
| 5 | `*port.ts`, `*adapter.ts` | `api` |
|
|
85
|
+
| 6 | `__tests__/`, `*.test.ts`, `*.spec.ts` | `test` |
|
|
86
|
+
| 7 | Anything under a `domain/` directory | `domain` |
|
|
87
|
+
| 8 | Anything under `docs/` | `docs` |
|
|
88
|
+
| 9 | `skills/**/*.md` | `docs` |
|
|
89
|
+
| 10 | Any other `CREATE` | `feature` |
|
|
90
|
+
| 11 | Any other `MODIFY` / `DELETE` | `bugfix` |
|
|
91
|
+
|
|
92
|
+
Two consequences worth internalising:
|
|
93
|
+
|
|
94
|
+
- The `config` rules are an **explicit allow-list**, not a wildcard. `.github/dependabot.yml`, `packages/app/.gitignore`, or a `Dockerfile` are *not* `config` — created fresh they fall through to `feature` and are blocked. This is deliberate fail-closed behaviour: widening the list is a config decision, not an inference the hook makes for you.
|
|
95
|
+
- `feature` is the fallback for **every new non-test source file**. "It's only a tiny helper" does not change the classification.
|
|
96
|
+
|
|
97
|
+
When the hook blocks a write, the message now names the deciding category and change kind per path (`foo.ts (category=feature, changeKind=CREATE)`), and — for `bugfix` / `docs` / `test` / `config` blocks — points at `allowedCategories` and `config:plan --intent quick-mode-relax` rather than at `/story-implementor`. <!-- @work-item-id WI-352 --> <!-- @work-item-id WI-354 -->
|
|
98
|
+
|
|
74
99
|
### Dry-running the classifier
|
|
75
100
|
|
|
76
101
|
Use `check-change-category` to evaluate an arbitrary file list without actually starting an implementation:
|
|
@@ -159,16 +184,16 @@ Codify the split in your head (or in a project CLAUDE.md note) — "`domain/**`
|
|
|
159
184
|
## FAQ
|
|
160
185
|
|
|
161
186
|
**Q. I have a one-line bugfix in `domain/`. Does "any file under `domain/`" auto-trigger Full?**
|
|
162
|
-
|
|
187
|
+
Yes — mechanically. The classifier is path-based (see [How a path becomes a category](#how-a-path-becomes-a-category)), so *any* write under a `domain/` directory lands in the `domain` category, which is outside the default `allowedCategories`, and the hook blocks it with `MIXED_CHANGES`. Judgement about "it's only an off-by-one" does not enter into it. If the fix is genuinely trivial, the sanctioned routes are a Full Mode session (`phasegate session begin --mode full --unit <unit> ...`) or, when the project has decided domain edits are routinely Quick-eligible, widening `allowedCategories` via `config:plan`. Do not expect the hook to infer intent.
|
|
163
188
|
|
|
164
189
|
**Q. What if Quick discovers the change is bigger than expected mid-flight?**
|
|
165
190
|
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.
|
|
166
191
|
|
|
167
192
|
**Q. Can I disable Quick Mode entirely?**
|
|
168
|
-
|
|
193
|
+
Not by emptying the list — `allowedCategories: []` is rejected as invalid config (`allowedCategories must not be empty`), so it breaks the hook rather than tightening it. Narrow it instead: `allowedCategories: ["docs"]` routes effectively everything through Full Mode while staying valid. Alternatively, leave the categories as-is and set every `quickMode.fullModeRequiredWhen.*` flag to `true` (the default) so any non-trivial scope automatically escalates.
|
|
169
194
|
|
|
170
195
|
**Q. Can I add custom categories?**
|
|
171
|
-
No. `allowedCategories`
|
|
196
|
+
No. `allowedCategories` accepts only values from a fixed enum — the seven `ChangeCategory` values `bugfix`, `docs`, `test`, `config`, `feature`, `domain`, `api`. The default allow-list is the first four; the remaining three exist so that a project can *deliberately* widen the gate (rarely a good idea), not so you can invent a new label. If your workflow needs a category outside this enum, that is evidence the change is probably Full Mode material.
|
|
172
197
|
|
|
173
198
|
---
|
|
174
199
|
|
|
@@ -27,6 +27,43 @@ Each finding has a severity, a repair mode, and optional next-step fields.
|
|
|
27
27
|
| `ci-workflow-missing` | `npx phasegate setup:agent --intent ci-only --with-ci --dry-run --json` |
|
|
28
28
|
| agent context drift | `npx phasegate reconcile --dry-run` |
|
|
29
29
|
|
|
30
|
+
## Phase Gate Blocked a Write and I Do Not Know What the Document Should Contain
|
|
31
|
+
|
|
32
|
+
<!-- @work-item-id WI-356 -->
|
|
33
|
+
|
|
34
|
+
Phase Gate blocks (`L2-001`, `FULL_MODE_REQUIRED`) mean a required inception or product design document is missing. Two things are needed: the file skeleton, and the section structure to fill in.
|
|
35
|
+
|
|
36
|
+
**1. Generate the skeleton.** `scaffold-design` writes a placeholder document for the missing phase:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npx phasegate scaffold-design --unit <unit-id> --phase logical --dry-run
|
|
40
|
+
npx phasegate scaffold-design --unit <unit-id> --phase logical --apply
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Accepted `--phase` values: `logical`, `domain`, `uiux`, `unit-test`, `it-test`.
|
|
44
|
+
|
|
45
|
+
**2. Read the section structure from the skill definition.** PhaseGate does not ship fill-in templates into your repo; the authoritative structure of every plan and inception artifact lives in the skill markdown that produces it. `install` / `init` copy these into `skills/` in your repository:
|
|
46
|
+
|
|
47
|
+
| Document | Skill definition |
|
|
48
|
+
|---|---|
|
|
49
|
+
| product architecture / unit decomposition | `skills/product-architect/SKILL.md` |
|
|
50
|
+
| story map | `skills/story-mapper/SKILL.md` |
|
|
51
|
+
| story descriptions / acceptance criteria | `skills/story-writer/SKILL.md` |
|
|
52
|
+
| unit plan | `skills/unit-designer/SKILL.md` |
|
|
53
|
+
| `logical_design.md` | `skills/logical-designer/SKILL.md` |
|
|
54
|
+
| `domain_model.md` | `skills/domain-designer/SKILL.md` |
|
|
55
|
+
| UI/UX design | `skills/uiux-designer/SKILL.md` |
|
|
56
|
+
| unit / IT / scenario test plans | `skills/unit-test-designer/SKILL.md`, `skills/it-test-designer/SKILL.md`, `skills/scenario-test-designer/SKILL.md` |
|
|
57
|
+
|
|
58
|
+
If `skills/` is not present in your repository — the personal install path does not copy it — read the same content from PhaseGate's own package instead:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npx phasegate skills list
|
|
62
|
+
npx phasegate skills info logical-designer
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`skills info` prints the full SKILL.md to stdout, so it works from a sandboxed agent that cannot read `node_modules/`.
|
|
66
|
+
|
|
30
67
|
## Refused Managed Target
|
|
31
68
|
|
|
32
69
|
If `install`, `reconcile`, or `uninstall` refuses a target, do not immediately force it. Inspect the diff and ask the agent to explain:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CLAUDE.md
|
|
2
2
|
|
|
3
|
-
<!-- @work-item-id WI-174, WI-176, WI-177 -->
|
|
3
|
+
<!-- @work-item-id WI-174, WI-176, WI-177, WI-331 -->
|
|
4
4
|
|
|
5
5
|
<!-- phasegate:managed-section:start -->
|
|
6
6
|
## 必読ドキュメント
|
|
@@ -38,13 +38,13 @@ If `setup:agent --apply --json` or `install --apply --json` fails with a structu
|
|
|
38
38
|
|
|
39
39
|
{{PHASEGATE_SKILLS}}
|
|
40
40
|
|
|
41
|
+
## Agent Context Refresh
|
|
42
|
+
|
|
43
|
+
Run `phasegate ci:auto-refresh-agent-context --dry-run` to preview updates and `phasegate ci:auto-refresh-agent-context --apply` to write AGENTS.md / CLAUDE.md.
|
|
44
|
+
<!-- phasegate:managed-section:end -->
|
|
45
|
+
|
|
41
46
|
## User Section
|
|
42
47
|
|
|
43
48
|
<!-- phasegate:user-section:start -->
|
|
44
49
|
{{PHASEGATE_USER_SECTION}}
|
|
45
50
|
<!-- phasegate:user-section:end -->
|
|
46
|
-
|
|
47
|
-
## Agent Context Refresh
|
|
48
|
-
|
|
49
|
-
Run `phasegate ci:auto-refresh-agent-context --dry-run` to preview updates and `phasegate ci:auto-refresh-agent-context --apply` to write AGENTS.md / CLAUDE.md.
|
|
50
|
-
<!-- phasegate:managed-section:end -->
|
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
# 実行内容:
|
|
8
8
|
# 1. bypass:audit — bypass trailer / evidence 監査
|
|
9
9
|
# 2. phasegate lint — L1 Biome AST ルール検証
|
|
10
|
-
# 3.
|
|
11
|
-
# 4.
|
|
12
|
-
# 5.
|
|
13
|
-
# 6.
|
|
10
|
+
# 3. package の coverage script を実行し、L3-003 の入力を生成
|
|
11
|
+
# 4. requirement-test matrix をcurrent testsから再生成
|
|
12
|
+
# 5. world.enabled=true の場合、world:derive を2回実行してbyte一致を検証
|
|
13
|
+
# 6. phasegate:ci-check — L3 CI バリデータ(security / performance / coverage / nyquist / World)
|
|
14
|
+
# 7. 失敗時: PR にエラーサマリーをコメント
|
|
14
15
|
|
|
15
16
|
name: AIDLC Quality Gate
|
|
16
17
|
|
|
@@ -79,6 +80,23 @@ jobs:
|
|
|
79
80
|
echo "exit_code=$EXIT_CODE" >> $GITHUB_OUTPUT
|
|
80
81
|
exit $EXIT_CODE
|
|
81
82
|
|
|
83
|
+
# L3-003 is fail-closed when coverageThreshold is configured but the
|
|
84
|
+
# report is absent. The adopting project owns this script and must emit
|
|
85
|
+
# coverage/coverage-summary.json; package-manager selection follows the
|
|
86
|
+
# lockfile used during dependency installation above.
|
|
87
|
+
- name: Test and generate coverage
|
|
88
|
+
shell: bash
|
|
89
|
+
run: |
|
|
90
|
+
set -euo pipefail
|
|
91
|
+
node -e "const scripts=require('./package.json').scripts ?? {}; if (!scripts.coverage) { throw new Error('phasegate aidlc-gate requires package.json scripts.coverage to generate coverage/coverage-summary.json'); }"
|
|
92
|
+
if [ -f pnpm-lock.yaml ]; then
|
|
93
|
+
pnpm run coverage
|
|
94
|
+
elif [ -f yarn.lock ]; then
|
|
95
|
+
yarn coverage
|
|
96
|
+
else
|
|
97
|
+
npm run coverage
|
|
98
|
+
fi
|
|
99
|
+
|
|
82
100
|
# L3-004 consumes the generated requirement-test matrix. Always rebuild it
|
|
83
101
|
# from this checkout instead of trusting a stale or persisted artifact.
|
|
84
102
|
- name: Generate requirement-test matrix
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "phasegate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.315.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": "MIT",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"vitest": "^3.0.0"
|
|
79
79
|
},
|
|
80
80
|
"peerDependencies": {
|
|
81
|
-
"typescript": ">=5.0.0"
|
|
81
|
+
"typescript": ">=5.0.0 <7.0.0"
|
|
82
82
|
},
|
|
83
83
|
"peerDependenciesMeta": {
|
|
84
84
|
"typescript": {
|
package/scripts/harness/agent-integration/application/usecases/handle-pre-tool-use-usecase.ts
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* @work-item-id WI-202 / WI-204
|
|
7
7
|
* @work-item-id WI-206
|
|
8
8
|
* @work-item-id WI-214
|
|
9
|
+
* @work-item-id WI-349
|
|
10
|
+
* @work-item-id WI-354
|
|
9
11
|
*
|
|
10
12
|
* HandlePreToolUseUseCase
|
|
11
13
|
* PreToolUse Hook処理のオーケストレーション
|
|
@@ -17,11 +19,11 @@ import type {
|
|
|
17
19
|
} from "../../domain/ports/baseline-grandfather-query-port.js";
|
|
18
20
|
import type { ConfigQueryPort } from "../../domain/ports/config-query-port.js";
|
|
19
21
|
import type { ErrorGuidance, ErrorGuidanceQueryPort } from "../../domain/ports/error-guidance-query-port.js";
|
|
22
|
+
import type { FullModeRequirementQueryPort } from "../../domain/ports/full-mode-requirement-query-port.js";
|
|
20
23
|
import type {
|
|
21
24
|
FullModeSessionQueryPort,
|
|
22
25
|
FullModeSessionQueryResult,
|
|
23
26
|
} from "../../domain/ports/full-mode-session-query-port.js";
|
|
24
|
-
import type { FullModeRequirementQueryPort } from "../../domain/ports/full-mode-requirement-query-port.js";
|
|
25
27
|
import type { PhaseGateQueryPort } from "../../domain/ports/phase-gate-query-port.js";
|
|
26
28
|
import type { StoryReflectionQueryPort } from "../../domain/ports/story-reflection-query-port.js";
|
|
27
29
|
import { AsyncHookToCliTranslator } from "../../domain/services/hook-to-cli-translator.js";
|
|
@@ -186,6 +188,7 @@ export class HandlePreToolUseUseCase {
|
|
|
186
188
|
guidance,
|
|
187
189
|
unitIdForGuidance,
|
|
188
190
|
input.callerSkill,
|
|
191
|
+
sessionResult,
|
|
189
192
|
);
|
|
190
193
|
}
|
|
191
194
|
} else {
|
|
@@ -282,21 +285,20 @@ export class HandlePreToolUseUseCase {
|
|
|
282
285
|
guidance: ErrorGuidance | null,
|
|
283
286
|
unitId: string | undefined,
|
|
284
287
|
callerSkill?: string,
|
|
288
|
+
sessionResult?: FullModeSessionQueryResult,
|
|
285
289
|
): HandlePreToolUseOutput {
|
|
286
290
|
const fp = blockedFilePath ?? "不明なファイル";
|
|
287
291
|
if (result.dominantCategory === "config" && /(?:^|\/)phasegate\.config\.json$/.test(fp)) {
|
|
288
292
|
const dryRunCommand = "phasegate config:plan --intent quick-mode-relax --dry-run --json";
|
|
289
293
|
const applyCommand = "phasegate config:plan --intent quick-mode-relax --apply --json";
|
|
290
|
-
const lines = [
|
|
291
|
-
`Full mode 必須変更が検出されました: ${fp}`,
|
|
292
|
-
"カテゴリ: config",
|
|
293
|
-
];
|
|
294
|
+
const lines = [`Full mode 必須変更が検出されました: ${fp}`, "カテゴリ: config"];
|
|
294
295
|
if (result.rejectionRule) {
|
|
295
296
|
lines.push(`判定ルール: ${result.rejectionRule}`);
|
|
296
297
|
}
|
|
297
298
|
if (result.rejectionReason) {
|
|
298
299
|
lines.push(`理由: ${result.rejectionReason}`);
|
|
299
300
|
}
|
|
301
|
+
HandlePreToolUseUseCase.appendJudgmentContextLines(lines, sessionResult);
|
|
300
302
|
lines.push(`次のアクション: ${dryRunCommand} で差分を確認し、承認後に ${applyCommand} を実行してください。`);
|
|
301
303
|
|
|
302
304
|
return {
|
|
@@ -309,22 +311,24 @@ export class HandlePreToolUseUseCase {
|
|
|
309
311
|
nextAction: `${dryRunCommand} && ${applyCommand}`,
|
|
310
312
|
};
|
|
311
313
|
}
|
|
312
|
-
if (
|
|
313
|
-
callerSkill === "quick-implementor" &&
|
|
314
|
-
result.dominantCategory !== undefined &&
|
|
315
|
-
["bugfix", "docs", "test", "config"].includes(result.dominantCategory)
|
|
316
|
-
) {
|
|
314
|
+
if (HandlePreToolUseUseCase.shouldGuideQuickModeRelax(result.dominantCategory, callerSkill)) {
|
|
317
315
|
const dryRunCommand = "phasegate config:plan --intent quick-mode-relax --dry-run --json";
|
|
318
316
|
const applyCommand = "phasegate config:plan --intent quick-mode-relax --apply --json";
|
|
319
317
|
const lines: string[] = [`Full mode 必須変更が検出されました: ${fp}`];
|
|
320
|
-
|
|
318
|
+
if (result.dominantCategory) {
|
|
319
|
+
lines.push(`カテゴリ: ${result.dominantCategory}`);
|
|
320
|
+
}
|
|
321
321
|
if (result.rejectionRule) {
|
|
322
322
|
lines.push(`判定ルール: ${result.rejectionRule}`);
|
|
323
323
|
}
|
|
324
324
|
if (result.rejectionReason) {
|
|
325
325
|
lines.push(`理由: ${result.rejectionReason}`);
|
|
326
326
|
}
|
|
327
|
-
|
|
327
|
+
HandlePreToolUseUseCase.appendJudgmentContextLines(lines, sessionResult);
|
|
328
|
+
lines.push(
|
|
329
|
+
`次のアクション: Quick Mode の許可カテゴリを確認してください。緩和する場合は ${dryRunCommand} で差分を確認し、承認後に ${applyCommand} を実行してください。`,
|
|
330
|
+
);
|
|
331
|
+
lines.push(` 分類の確認: npx phasegate check-change-category --paths ${fp}`);
|
|
328
332
|
|
|
329
333
|
return {
|
|
330
334
|
shouldBlock: true,
|
|
@@ -346,9 +350,15 @@ export class HandlePreToolUseUseCase {
|
|
|
346
350
|
if (result.rejectionReason) {
|
|
347
351
|
lines.push(`理由: ${result.rejectionReason}`);
|
|
348
352
|
}
|
|
353
|
+
HandlePreToolUseUseCase.appendJudgmentContextLines(lines, sessionResult);
|
|
349
354
|
const suggestedSkill = guidance?.suggestedSkill ?? "/story-implementor";
|
|
350
355
|
lines.push(`次のアクション: ${suggestedSkill} スキルを使用して設計フェーズから開始してください。`);
|
|
351
356
|
if (unitId !== undefined && unitId !== "") {
|
|
357
|
+
if (HandlePreToolUseUseCase.isSessionActiveButRejected(sessionResult)) {
|
|
358
|
+
lines.push(
|
|
359
|
+
` 既存 session は上記理由で今回の書き込みに使えません。張り直す場合: phasegate session end --work-item ${sessionResult?.workItemId ?? "<WI-XXX>"} を実行してから下記を実行してください。`,
|
|
360
|
+
);
|
|
361
|
+
}
|
|
352
362
|
lines.push(
|
|
353
363
|
` 実装フェーズ開始時: phasegate session begin --mode full --unit ${unitId} --work-item <WI-XXX> --reason "<reason>" --duration 1h`,
|
|
354
364
|
);
|
|
@@ -367,6 +377,56 @@ export class HandlePreToolUseUseCase {
|
|
|
367
377
|
};
|
|
368
378
|
}
|
|
369
379
|
|
|
380
|
+
/**
|
|
381
|
+
* Quick Mode スコープのカテゴリ(bugfix / docs / test / config)は quick-implementor で
|
|
382
|
+
* 完遂できる変更であり、遮断の実体は「allowedCategories が絞られている」ことである。
|
|
383
|
+
* ここで /story-implementor を案内すると、設計フェーズからやり直せという誤った指示になる。
|
|
384
|
+
*
|
|
385
|
+
* WI-354: 従来は callerSkill === "quick-implementor" を条件にしていたが、
|
|
386
|
+
* callerSkill を供給する producer(hook input の caller_skill / PHASEGATE_CALLER_SKILL)は
|
|
387
|
+
* 実運用で設定されず、この分岐は到達不能だった。カテゴリを一次条件にして
|
|
388
|
+
* skill context なしでも実用的な復旧手順を出す。
|
|
389
|
+
* feature / domain / api は従来どおり /story-implementor 誘導を維持する。
|
|
390
|
+
*/
|
|
391
|
+
private static readonly QUICK_MODE_SCOPE_CATEGORIES: readonly string[] = ["bugfix", "docs", "test", "config"];
|
|
392
|
+
|
|
393
|
+
private static shouldGuideQuickModeRelax(dominantCategory: string | undefined, callerSkill?: string): boolean {
|
|
394
|
+
if (dominantCategory !== undefined) {
|
|
395
|
+
return HandlePreToolUseUseCase.QUICK_MODE_SCOPE_CATEGORIES.includes(dominantCategory);
|
|
396
|
+
}
|
|
397
|
+
// カテゴリ不明時は skill context だけが手掛かり
|
|
398
|
+
return callerSkill === "quick-implementor";
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* WI-349: ブロック理由に判定根拠を明示する。
|
|
403
|
+
*
|
|
404
|
+
* - 判定対象は「今回の書き込み対象パス」だけであり、ワークツリー上の未コミット変更は
|
|
405
|
+
* 一切含まれない。この誤解が「無関係な変更のせいでブロックされている」という
|
|
406
|
+
* 誤った原因究明を招いていた(issue #41 症状②)。
|
|
407
|
+
* - アクティブな Full Mode session がありながら不許可だった場合は、その理由
|
|
408
|
+
* (unit 不一致 / category 不一致 / 期限切れ 等)を提示する。これがないと
|
|
409
|
+
* 「session begin せよ」と案内されながら session は既に有効、という最悪の混乱になる。
|
|
410
|
+
*/
|
|
411
|
+
private static appendJudgmentContextLines(
|
|
412
|
+
lines: string[],
|
|
413
|
+
sessionResult: FullModeSessionQueryResult | undefined,
|
|
414
|
+
): void {
|
|
415
|
+
lines.push("判定対象: 今回の書き込み対象パスのみです(ワークツリー上の他の未コミット変更は判定に含まれません)。");
|
|
416
|
+
if (!HandlePreToolUseUseCase.isSessionActiveButRejected(sessionResult)) {
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
const workItemId = sessionResult?.workItemId ?? "<unknown>";
|
|
420
|
+
const unit = sessionResult?.unit ?? "<unknown>";
|
|
421
|
+
const expiresAt = sessionResult?.expiresAt ?? "<unknown>";
|
|
422
|
+
lines.push(`アクティブな Full Mode session: ${workItemId} (unit=${unit}, expiresAt=${expiresAt})`);
|
|
423
|
+
lines.push(`session が書き込みを許可しなかった理由: ${sessionResult?.reason ?? "<unknown>"}`);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
private static isSessionActiveButRejected(sessionResult: FullModeSessionQueryResult | undefined): boolean {
|
|
427
|
+
return sessionResult !== undefined && sessionResult.active && !sessionResult.allowed;
|
|
428
|
+
}
|
|
429
|
+
|
|
370
430
|
private static appendGuidanceLines(lines: string[], guidance: ErrorGuidance | null, unitId?: string): void {
|
|
371
431
|
if (guidance === null) return;
|
|
372
432
|
if (guidance.scaffoldCommand !== null) {
|
|
@@ -377,10 +437,25 @@ export class HandlePreToolUseUseCase {
|
|
|
377
437
|
lines.push(` scaffold: ${command}`);
|
|
378
438
|
}
|
|
379
439
|
if (guidance.templatePath !== null) {
|
|
380
|
-
lines.push(`
|
|
440
|
+
lines.push(` 構成リファレンス: ${HandlePreToolUseUseCase.describeStructureReference(guidance.templatePath)}`);
|
|
381
441
|
}
|
|
382
442
|
}
|
|
383
443
|
|
|
444
|
+
/**
|
|
445
|
+
* WI-356 (issue #29): 参照先が skill 定義(skills/<name>/SKILL.md)の場合、
|
|
446
|
+
* skills/ をユーザー repo に配置しない personal install 経路でも読めるよう
|
|
447
|
+
* stdout 経路(phasegate skills info)を併記する。
|
|
448
|
+
*/
|
|
449
|
+
private static readonly SKILL_REFERENCE_PATTERN = /^skills\/([^/]+)\/SKILL\.md$/;
|
|
450
|
+
|
|
451
|
+
private static describeStructureReference(templatePath: string): string {
|
|
452
|
+
const skillMatch = HandlePreToolUseUseCase.SKILL_REFERENCE_PATTERN.exec(templatePath);
|
|
453
|
+
if (skillMatch === null) {
|
|
454
|
+
return templatePath;
|
|
455
|
+
}
|
|
456
|
+
return `${templatePath}(未配置なら: npx phasegate skills info ${skillMatch[1]})`;
|
|
457
|
+
}
|
|
458
|
+
|
|
384
459
|
private deriveUnitIdFromPaths(targetFilePaths: readonly string[]): string | undefined {
|
|
385
460
|
const projectPaths = this.configQueryPort.getProjectPaths();
|
|
386
461
|
for (const targetFilePath of targetFilePaths) {
|
|
@@ -419,8 +494,15 @@ export class HandlePreToolUseUseCase {
|
|
|
419
494
|
}
|
|
420
495
|
|
|
421
496
|
private static isUnderInception(targetFilePath: string, inceptionPath: string): boolean {
|
|
422
|
-
const normalizedTarget = targetFilePath
|
|
423
|
-
|
|
497
|
+
const normalizedTarget = targetFilePath
|
|
498
|
+
.replaceAll("\\", "/")
|
|
499
|
+
.replace(/^\.\/+/, "")
|
|
500
|
+
.replace(/\/+/g, "/");
|
|
501
|
+
const normalizedBase = inceptionPath
|
|
502
|
+
.replaceAll("\\", "/")
|
|
503
|
+
.replace(/^\.\/+/, "")
|
|
504
|
+
.replace(/\/+/g, "/")
|
|
505
|
+
.replace(/\/$/, "");
|
|
424
506
|
return normalizedTarget === normalizedBase || normalizedTarget.startsWith(`${normalizedBase}/`);
|
|
425
507
|
}
|
|
426
508
|
|
|
@@ -577,9 +659,7 @@ export class HandlePreToolUseUseCase {
|
|
|
577
659
|
lines.push("");
|
|
578
660
|
lines.push("修正方法:");
|
|
579
661
|
lines.push(" 1. cascade-updater を実行して product 文書を更新");
|
|
580
|
-
lines.push(
|
|
581
|
-
` 2. または手動で該当 product 文書に @${annotationKey} ${details?.storyId ?? "<WORK-ITEM-ID>"} を追加`,
|
|
582
|
-
);
|
|
662
|
+
lines.push(` 2. または手動で該当 product 文書に @${annotationKey} ${details?.storyId ?? "<WORK-ITEM-ID>"} を追加`);
|
|
583
663
|
lines.push("");
|
|
584
664
|
lines.push("参照: ADR-XXX");
|
|
585
665
|
|