phasegate 0.121.0 → 0.122.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 +16 -0
- package/docs/guide/configuration.md +22 -0
- package/docs/guide/hooks-integration.md +2 -1
- package/package.json +1 -1
- package/scripts/harness/agent-integration/application/dto/handle-stop-dto.ts +1 -0
- package/scripts/harness/agent-integration/application/usecases/handle-stop-usecase.ts +4 -0
- package/scripts/harness/agent-integration/domain/ports/config-query-port.ts +3 -0
- package/scripts/harness/agent-integration/infrastructure/adapters/harness-config-config-query-adapter.ts +16 -0
- package/scripts/harness/agent-integration/presentation/stop-hook.ts +9 -0
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +15 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.122.0] - 2026-05-08
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **WI-087 Phase C-2 — Stop hook strict mode (`agentIntegration.stopHook.enforce`)** — 外部レポーター nakataj-mti が GitHub Issue [#3](https://github.com/junpei-9898/phasegate/issues/3) finding #4 で報告した「`phasegate hook stop` が Complete Check 失敗時に exit 1 を返すが、Claude Code の Stop hook block には exit 2 が必要」問題を解消。新 config flag `agentIntegration.stopHook.enforce: boolean` (default `false`) を v3 schema に追加し、true セット時のみ Complete Check 失敗で **exit 2 + `{"decision":"block","reason":"Complete Check failed (exitCode=N)"}`** を出力する strict mode に切り替わる。default は従来挙動 (exit cliResult.exitCode、stderr メッセージのみ) を完全維持。
|
|
15
|
+
- **schema 拡張**: `scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json` に top-level `agentIntegration.stopHook.enforce: boolean` を追加 (`additionalProperties: false`)。boolean 以外 / 未定義 key は AJV validator が reject。
|
|
16
|
+
- **新ポートメソッド**: `ConfigQueryPort.getStopHookEnforce(): Promise<boolean>` を追加 (`scripts/harness/agent-integration/domain/ports/config-query-port.ts`)。
|
|
17
|
+
- **infrastructure 実装**: `HarnessConfigConfigQueryAdapter.getStopHookEnforce()` が `agentIntegration.stopHook.enforce === true` のときのみ true を返す strict 一致判定。
|
|
18
|
+
- **application 拡張**: `HandleStopOutput` DTO に `shouldEnforceFailure?: boolean` を追加。`HandleStopUseCase.execute` 内で `enforce && cliResult.exitCode !== 0` の合成条件を populate。reentry 検出時は populate されない (現行挙動保持)。
|
|
19
|
+
- **presentation 分岐拡張**: `stop-hook.ts` で `output.shouldEnforceFailure === true` のとき stdout に decision JSON 出力 + stderr に reason 付きメッセージ + exit 2。それ以外は現行通り cliResult.exitCode で抜ける。
|
|
20
|
+
- **テスト追加**: 11 ケース (`ajv-config-schema-validator-v3.test.ts` +5、`harness-config-config-query-adapter.test.ts` +2、`handle-stop-usecase.test.ts` +4)。全 3491 テスト (前回 3480 + 新規 11) グリーン。
|
|
21
|
+
- **dogfood 検証**: phasegate 自身の `phasegate.config.json` に `enforce: true` を一時注入し `stop-hook.ts` を手動実行 → exit 2 + decision JSON 出力を確認。enforce 削除後は exit 1 + decision JSON なしの後方互換挙動を確認。
|
|
22
|
+
- **既存テストへの影響**: `ConfigQueryPort` インターフェース拡張に伴い、`handle-pre-tool-use-usecase.test.ts` / `handle-post-tool-use-usecase.test.ts` / `handle-stop-usecase.test.ts` / `ci-governance-baseline-grandfather-adapter.test.ts` の mock literal に `getStopHookEnforce: vi.fn().mockResolvedValue(false)` を追加 (default 後方互換を維持しつつ TypeScript 型契約を満たす)。
|
|
23
|
+
- **ドキュメント整合**: `docs/guide/configuration.md` に `agentIntegration` セクションを追加 (sub-field 表 + 用途説明)、`docs/guide/hooks-integration.md` の Stop hook 説明に enforce オプションを追記。
|
|
24
|
+
- **互換性**: 既存 v2/v3 config (agentIntegration セクション不在) は default false で v0.121.0 以前と完全同一挙動。schema validator 経路 / ConfigQueryPort 既存メソッド経路への影響なし。
|
|
25
|
+
|
|
10
26
|
## [0.121.0] - 2026-05-07
|
|
11
27
|
|
|
12
28
|
### Fixed
|
|
@@ -416,6 +416,28 @@ Introduced in ISSUE-007 Wave 1 (v0.65.0) and wired into the pre-tool-use hook by
|
|
|
416
416
|
|
|
417
417
|
Generate or refresh the snapshot with `npx phasegate baseline` (`--dry-run` to inspect, `--force` to overwrite, `--paths <glob,glob,...>` to scope, `--json` for CI-friendly output). See the [Baseline section in CLI Reference](cli-reference.md#baseline-retrofit-grandfather) for details.
|
|
418
418
|
|
|
419
|
+
#### `agentIntegration` (Stop hook strict mode)
|
|
420
|
+
|
|
421
|
+
Controls how phasegate's agent-side hooks integrate with Claude Code. Currently only `stopHook.enforce` is exposed.
|
|
422
|
+
|
|
423
|
+
```jsonc
|
|
424
|
+
{
|
|
425
|
+
"agentIntegration": {
|
|
426
|
+
"stopHook": {
|
|
427
|
+
"enforce": true
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
| Sub-field | Type | Default | Description |
|
|
434
|
+
|--------------------|-----------|---------|----------------------------------------------------------------------------------------------------------------------|
|
|
435
|
+
| `stopHook.enforce` | `boolean` | `false` | When `true`, a non-zero exit from `phasegate:complete-check` causes the Stop hook to emit `{"decision":"block","reason":"Complete Check failed (exitCode=N)"}` on stdout and exit with code 2, blocking Claude Code's turn. When `false` (default), the hook exits with the inner CLI's exit code, which Claude Code surfaces only as a transcript warning. |
|
|
436
|
+
|
|
437
|
+
Use `enforce: true` when your team treats Complete Check failures as hard gates (e.g., disallow ending a session with failing tests or lint). Leave it as default `false` for an opt-in / advisory experience.
|
|
438
|
+
|
|
439
|
+
Reentry-detection cases (`REENTRY_DETECTED`) always exit with code 0 regardless of this setting; strict mode applies only to actual Complete Check failures.
|
|
440
|
+
|
|
419
441
|
---
|
|
420
442
|
|
|
421
443
|
### Quick Mode
|
|
@@ -91,7 +91,8 @@ Use /quick-implementor skill for version changes in package.json.
|
|
|
91
91
|
|
|
92
92
|
### Stop (before session end)
|
|
93
93
|
- Runs `phasegate:complete-check` (L2-L4 full validation)
|
|
94
|
-
-
|
|
94
|
+
- By default, the hook exits with the inner CLI's exit code, which Claude Code shows as a transcript warning but does not turn-block on.
|
|
95
|
+
- Set `agentIntegration.stopHook.enforce: true` in `phasegate.config.json` to enable **strict mode**: on Complete Check failure, the hook emits `{"decision":"block","reason":"Complete Check failed (exitCode=N)"}` on stdout and exits with code 2, hard-blocking Claude Code's turn end. Reentry-detection still exits 0 regardless of this setting. See `docs/guide/configuration.md` `agentIntegration` section for details.
|
|
95
96
|
|
|
96
97
|
## Optional Shell Script Hooks
|
|
97
98
|
|
package/package.json
CHANGED
|
@@ -69,9 +69,13 @@ export class HandleStopUseCase {
|
|
|
69
69
|
|
|
70
70
|
try {
|
|
71
71
|
const cliResult = await this.cliExecutorPort.execute('phasegate:complete-check', []);
|
|
72
|
+
// WI-087 finding #4: enforce=true かつ Complete Check 失敗時のみ shouldEnforceFailure=true
|
|
73
|
+
const enforce = await this.configQueryPort.getStopHookEnforce();
|
|
74
|
+
const shouldEnforceFailure = enforce && cliResult.exitCode !== 0;
|
|
72
75
|
return {
|
|
73
76
|
executed: true,
|
|
74
77
|
cliResult,
|
|
78
|
+
shouldEnforceFailure,
|
|
75
79
|
};
|
|
76
80
|
} finally {
|
|
77
81
|
// deactivate は成否問わず必ず実行(finally保証)
|
|
@@ -17,4 +17,7 @@ export interface ConfigQueryPort {
|
|
|
17
17
|
getRelaxedGates(): Promise<readonly string[]>;
|
|
18
18
|
getProjectPaths(): ProjectPaths;
|
|
19
19
|
getBaselineConfig(): Promise<BaselineConfig>;
|
|
20
|
+
// WI-087 finding #4: Stop hook strict mode 設定。true なら Complete Check 失敗時に
|
|
21
|
+
// exit 2 + decision JSON を出して Claude Code セッションを block する。
|
|
22
|
+
getStopHookEnforce(): Promise<boolean>;
|
|
20
23
|
}
|
|
@@ -48,12 +48,21 @@ interface BaselineSection {
|
|
|
48
48
|
path?: string;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
interface AgentIntegrationStopHookSection {
|
|
52
|
+
enforce?: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface AgentIntegrationSection {
|
|
56
|
+
stopHook?: AgentIntegrationStopHookSection;
|
|
57
|
+
}
|
|
58
|
+
|
|
51
59
|
interface HarnessConfigDocument {
|
|
52
60
|
harnesses?: HarnessesSection;
|
|
53
61
|
project?: ProjectSection;
|
|
54
62
|
protectedFiles?: ProtectedFilesSection;
|
|
55
63
|
quickMode?: QuickModeSection;
|
|
56
64
|
baseline?: BaselineSection;
|
|
65
|
+
agentIntegration?: AgentIntegrationSection;
|
|
57
66
|
}
|
|
58
67
|
|
|
59
68
|
export class HarnessConfigConfigQueryAdapter implements ConfigQueryPort {
|
|
@@ -128,4 +137,11 @@ export class HarnessConfigConfigQueryAdapter implements ConfigQueryPort {
|
|
|
128
137
|
path: baseline.path ?? '.phasegate/baseline.json',
|
|
129
138
|
};
|
|
130
139
|
}
|
|
140
|
+
|
|
141
|
+
async getStopHookEnforce(): Promise<boolean> {
|
|
142
|
+
const config = this.loadConfig();
|
|
143
|
+
const enforce = config.agentIntegration?.stopHook?.enforce;
|
|
144
|
+
// type guard: boolean 以外(schema validator ですり抜ける null/undefined を含む)は false にフォールバック
|
|
145
|
+
return enforce === true;
|
|
146
|
+
}
|
|
131
147
|
}
|
|
@@ -88,6 +88,15 @@ async function main(): Promise<void> {
|
|
|
88
88
|
|
|
89
89
|
if (output.executed && output.cliResult) {
|
|
90
90
|
if (output.cliResult.exitCode !== 0) {
|
|
91
|
+
// WI-087 finding #4: enforce=true なら exit 2 + decision JSON で turn block
|
|
92
|
+
if (output.shouldEnforceFailure === true) {
|
|
93
|
+
const reason = `Complete Check failed (exitCode=${output.cliResult.exitCode})`;
|
|
94
|
+
process.stdout.write(`${JSON.stringify({ decision: 'block', reason })}\n`);
|
|
95
|
+
process.stderr.write(
|
|
96
|
+
`Complete Check失敗 (exitCode=${output.cliResult.exitCode}) — strict mode により turn を block します\n`,
|
|
97
|
+
);
|
|
98
|
+
process.exit(2);
|
|
99
|
+
}
|
|
91
100
|
process.stderr.write(`Complete Check失敗 (exitCode=${output.cliResult.exitCode})\n`);
|
|
92
101
|
}
|
|
93
102
|
process.exit(output.cliResult.exitCode);
|
package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json
CHANGED
|
@@ -482,6 +482,21 @@
|
|
|
482
482
|
}
|
|
483
483
|
}
|
|
484
484
|
},
|
|
485
|
+
"agentIntegration": {
|
|
486
|
+
"type": "object",
|
|
487
|
+
"additionalProperties": false,
|
|
488
|
+
"properties": {
|
|
489
|
+
"stopHook": {
|
|
490
|
+
"type": "object",
|
|
491
|
+
"additionalProperties": false,
|
|
492
|
+
"properties": {
|
|
493
|
+
"enforce": {
|
|
494
|
+
"type": "boolean"
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
},
|
|
485
500
|
"architecture": {
|
|
486
501
|
"type": "object",
|
|
487
502
|
"additionalProperties": false,
|