phasegate 0.72.0 → 0.74.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 +22 -0
- package/docs/guide/retrofit-adoption.md +36 -0
- package/package.json +1 -1
- package/scripts/harness/agent-integration/application/usecases/handle-pre-tool-use-usecase.ts +34 -5
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +29 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.74.0] - 2026-04-22
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- ISSUE-007 Wave 9 — phase-gate ブロック時に出力される `scaffold:` 行に含まれる `<unit-id>` プレースホルダを、実 unit ID に置換するようにした。従来は L2-001 registry (`l2-error-definitions.ts`) の `defaultScaffoldCommand` が静的文字列 `npx phasegate scaffold-design --unit <unit-id> --phase logical` のまま出力されていたため、ユーザーが手で unit 名に書き換える必要があった。PHASE_GATE の場合は `metadata.unitId`、FULL_MODE_REQUIRED の場合は `targetFilePaths` から `WriteTargetScope.fromPath` で導出した unit ID を使って置換する。
|
|
15
|
+
- IT-AI-GUIDE-UID-001 / IT-AI-GUIDE-UID-002 を追加(PHASE_GATE と FULL_MODE_REQUIRED の両経路で `<unit-id>` が実 unit に置換されることを検証)。
|
|
16
|
+
|
|
17
|
+
## [0.73.0] - 2026-04-22
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- ISSUE-007 Wave 8 — `phasegate.config.json` の `project.paths` セクションを schema (`harness-config-v2.schema.json`) に追加。`project.paths.source` (array, minItems: 1) で phase-gate の監視ディレクトリを override できるようになった。`project.paths.docs.construction` / `project.paths.docs.inception` も optional で指定可能。
|
|
22
|
+
- IT-CF-PP-001a..d を追加(`project.paths.source` の valid/invalid パターン)。
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- ISSUE-007 Wave 8 dogfood で発覚した retrofit blocker を解消 — 従来は adapter (`harness-config-config-query-adapter.ts`) が `config.project?.paths?.source` を読む設計だったにも関わらず schema がそれを additionalProperties として reject していたため、`src/` 配下を使う一般 Node.js プロジェクトでは phase-gate が実質無効化されていた。
|
|
27
|
+
|
|
28
|
+
### Retrofit ガイド追補
|
|
29
|
+
|
|
30
|
+
- `docs/guide/retrofit-adoption.md` に「source path の指定」セクションを追加。`src/` 系プロジェクトでは `project.paths.source: ["src"]` の明示が必須である旨を記載。
|
|
31
|
+
|
|
10
32
|
## [0.72.0] - 2026-04-22
|
|
11
33
|
|
|
12
34
|
### Changed
|
|
@@ -63,6 +63,42 @@ grandfather をオフにしたい場合のみ `phasegate.config.json` に明示:
|
|
|
63
63
|
`path` も省略時は `.phasegate/baseline.json` が使われる。retrofit 用途では
|
|
64
64
|
この既定値のまま触らないのが最短。
|
|
65
65
|
|
|
66
|
+
### source path の指定(v0.73.0 以降、`src/` 系 retrofit に必須)
|
|
67
|
+
|
|
68
|
+
pre-tool-use hook の phase-gate は `project.paths.source` に記載されたディレクトリ
|
|
69
|
+
配下のファイルのみを監視対象にする。既定値は `["scripts/harness"]` で、これは
|
|
70
|
+
phasegate 本体のレイアウトに合わせたもの。**一般的な Node.js プロジェクト
|
|
71
|
+
(`src/` 配下) では必ず上書きすること**。
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"project": {
|
|
76
|
+
"name": "your-project",
|
|
77
|
+
"preset": "standard",
|
|
78
|
+
"paths": {
|
|
79
|
+
"source": ["src"]
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
monorepo など複数ディレクトリを持つ場合は配列で列挙:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"project": {
|
|
90
|
+
"paths": {
|
|
91
|
+
"source": ["apps/web/src", "apps/api/src", "packages/shared/src"]
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
この設定を忘れると、phase-gate は `scripts/harness/` 配下しか見ないため、
|
|
98
|
+
retrofit 対象の新規コード作成が block されず、結果として **phasegate が
|
|
99
|
+
無効化された運用** になってしまう(ISSUE-007 Wave 8 で schema 上 overridable
|
|
100
|
+
になるまで、この設定は config validator で拒否されていた)。
|
|
101
|
+
|
|
66
102
|
---
|
|
67
103
|
|
|
68
104
|
## Step 2: 既存コードを baseline に登録
|
package/package.json
CHANGED
package/scripts/harness/agent-integration/application/usecases/handle-pre-tool-use-usecase.ts
CHANGED
|
@@ -103,7 +103,14 @@ export class HandlePreToolUseUseCase {
|
|
|
103
103
|
// fallthrough: continue to full-mode / story-reflection checks (which may also grandfather)
|
|
104
104
|
} else {
|
|
105
105
|
const guidance = await this.resolveGuidance('L2-001');
|
|
106
|
-
|
|
106
|
+
const unitIdForGuidance =
|
|
107
|
+
metadata?.unitId ?? this.deriveUnitIdFromPaths(input.targetFilePaths);
|
|
108
|
+
return HandlePreToolUseUseCase.buildPhaseGateBlockOutput(
|
|
109
|
+
blockedFilePath,
|
|
110
|
+
metadata,
|
|
111
|
+
guidance,
|
|
112
|
+
unitIdForGuidance,
|
|
113
|
+
);
|
|
107
114
|
}
|
|
108
115
|
} else {
|
|
109
116
|
return {
|
|
@@ -125,10 +132,12 @@ export class HandlePreToolUseUseCase {
|
|
|
125
132
|
const fullModeResult = await this.fullModeRequirementQueryPort.check(input.targetFilePaths);
|
|
126
133
|
if (fullModeResult.requiresFullMode) {
|
|
127
134
|
const guidance = await this.resolveGuidance('L2-001');
|
|
135
|
+
const unitIdForGuidance = this.deriveUnitIdFromPaths(input.targetFilePaths);
|
|
128
136
|
return HandlePreToolUseUseCase.buildFullModeRequiredBlockOutput(
|
|
129
137
|
input.targetFilePaths[0],
|
|
130
138
|
fullModeResult,
|
|
131
139
|
guidance,
|
|
140
|
+
unitIdForGuidance,
|
|
132
141
|
);
|
|
133
142
|
}
|
|
134
143
|
}
|
|
@@ -198,6 +207,7 @@ export class HandlePreToolUseUseCase {
|
|
|
198
207
|
dominantCategory?: string;
|
|
199
208
|
},
|
|
200
209
|
guidance: ErrorGuidance | null,
|
|
210
|
+
unitId: string | undefined,
|
|
201
211
|
): HandlePreToolUseOutput {
|
|
202
212
|
const fp = blockedFilePath ?? '不明なファイル';
|
|
203
213
|
const lines: string[] = [
|
|
@@ -214,7 +224,7 @@ export class HandlePreToolUseUseCase {
|
|
|
214
224
|
}
|
|
215
225
|
const suggestedSkill = guidance?.suggestedSkill ?? '/story-implementor';
|
|
216
226
|
lines.push(`次のアクション: ${suggestedSkill} スキルを使用して設計フェーズから開始してください。`);
|
|
217
|
-
HandlePreToolUseUseCase.appendGuidanceLines(lines, guidance);
|
|
227
|
+
HandlePreToolUseUseCase.appendGuidanceLines(lines, guidance, unitId);
|
|
218
228
|
|
|
219
229
|
return {
|
|
220
230
|
shouldBlock: true,
|
|
@@ -227,16 +237,34 @@ export class HandlePreToolUseUseCase {
|
|
|
227
237
|
};
|
|
228
238
|
}
|
|
229
239
|
|
|
230
|
-
private static appendGuidanceLines(
|
|
240
|
+
private static appendGuidanceLines(
|
|
241
|
+
lines: string[],
|
|
242
|
+
guidance: ErrorGuidance | null,
|
|
243
|
+
unitId?: string,
|
|
244
|
+
): void {
|
|
231
245
|
if (guidance === null) return;
|
|
232
246
|
if (guidance.scaffoldCommand !== null) {
|
|
233
|
-
|
|
247
|
+
const command = unitId !== undefined && unitId !== ''
|
|
248
|
+
? guidance.scaffoldCommand.replaceAll('<unit-id>', unitId)
|
|
249
|
+
: guidance.scaffoldCommand;
|
|
250
|
+
lines.push(` scaffold: ${command}`);
|
|
234
251
|
}
|
|
235
252
|
if (guidance.templatePath !== null) {
|
|
236
253
|
lines.push(` テンプレ: ${guidance.templatePath}`);
|
|
237
254
|
}
|
|
238
255
|
}
|
|
239
256
|
|
|
257
|
+
private deriveUnitIdFromPaths(targetFilePaths: readonly string[]): string | undefined {
|
|
258
|
+
const projectPaths = this.configQueryPort.getProjectPaths();
|
|
259
|
+
for (const targetFilePath of targetFilePaths) {
|
|
260
|
+
const scope = WriteTargetScope.fromPath(targetFilePath, projectPaths);
|
|
261
|
+
if (scope?.unitId !== undefined) {
|
|
262
|
+
return scope.unitId;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return undefined;
|
|
266
|
+
}
|
|
267
|
+
|
|
240
268
|
private resolveStoryReflectionScope(input: HandlePreToolUseInput): WriteTargetScope | null {
|
|
241
269
|
if (!HandlePreToolUseUseCase.WRITE_TOOLS.has(input.toolName)) {
|
|
242
270
|
return null;
|
|
@@ -264,6 +292,7 @@ export class HandlePreToolUseUseCase {
|
|
|
264
292
|
blockedFilePath: string | undefined,
|
|
265
293
|
metadata: BlockMetadata,
|
|
266
294
|
guidance: ErrorGuidance | null,
|
|
295
|
+
unitId: string | undefined,
|
|
267
296
|
): HandlePreToolUseOutput {
|
|
268
297
|
const levelLabel = metadata.scopeLevel
|
|
269
298
|
? HandlePreToolUseUseCase.LEVEL_LABELS[metadata.scopeLevel] ?? `Level ${metadata.scopeLevel}`
|
|
@@ -286,7 +315,7 @@ export class HandlePreToolUseUseCase {
|
|
|
286
315
|
if (metadata.unitId) {
|
|
287
316
|
lines.push(` 実行例: ${suggestedSkill} --unit ${metadata.unitId}`);
|
|
288
317
|
}
|
|
289
|
-
HandlePreToolUseUseCase.appendGuidanceLines(lines, guidance);
|
|
318
|
+
HandlePreToolUseUseCase.appendGuidanceLines(lines, guidance, unitId);
|
|
290
319
|
|
|
291
320
|
return {
|
|
292
321
|
shouldBlock: true,
|
package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json
CHANGED
|
@@ -32,6 +32,35 @@
|
|
|
32
32
|
"standard",
|
|
33
33
|
"strict"
|
|
34
34
|
]
|
|
35
|
+
},
|
|
36
|
+
"paths": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"additionalProperties": false,
|
|
39
|
+
"properties": {
|
|
40
|
+
"source": {
|
|
41
|
+
"type": "array",
|
|
42
|
+
"items": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"minLength": 1
|
|
45
|
+
},
|
|
46
|
+
"minItems": 1,
|
|
47
|
+
"uniqueItems": true
|
|
48
|
+
},
|
|
49
|
+
"docs": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"additionalProperties": false,
|
|
52
|
+
"properties": {
|
|
53
|
+
"construction": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"minLength": 1
|
|
56
|
+
},
|
|
57
|
+
"inception": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"minLength": 1
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
35
64
|
}
|
|
36
65
|
}
|
|
37
66
|
},
|