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
|
@@ -5,11 +5,15 @@
|
|
|
5
5
|
* @work-item-id WI-212
|
|
6
6
|
* @work-item-id WI-301
|
|
7
7
|
* @work-item-id WI-302
|
|
8
|
+
* @work-item-id WI-319
|
|
9
|
+
* @work-item-id WI-328
|
|
8
10
|
*
|
|
9
11
|
* HarnessConfigValidatorConfigAdapter — ValidatorConfigPort実装
|
|
10
12
|
* HarnessConfigV2からLayerConfig VOを構築する
|
|
11
13
|
*/
|
|
12
14
|
|
|
15
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
16
|
+
import { join } from "node:path";
|
|
13
17
|
import type { ValidatorConfigPort } from "../../domain/ports/validator-config-port.js";
|
|
14
18
|
import { LayerConfig } from "../../domain/value-objects/layer-config.js";
|
|
15
19
|
import { ValidatorId } from "../../domain/value-objects/validator-id.js";
|
|
@@ -38,11 +42,92 @@ export interface HarnessConfigV2Like {
|
|
|
38
42
|
world?: { enabled?: boolean };
|
|
39
43
|
}
|
|
40
44
|
|
|
45
|
+
/**
|
|
46
|
+
* WI-319 (github#39): project.languages 未宣言時にファイルシステムから言語を検出するためのマーカー定義。
|
|
47
|
+
* typescript は package.json の存在自体を根拠にできない(phasegate 導入時に phasegate 用
|
|
48
|
+
* package.json が置かれるため)ので、この表には含めず hasTypescriptMarker() で個別判定する。
|
|
49
|
+
*/
|
|
50
|
+
const LANGUAGE_MARKER_FILES: ReadonlyArray<{ readonly language: string; readonly markers: readonly string[] }> = [
|
|
51
|
+
{ language: "python", markers: ["pyproject.toml", "setup.py", "setup.cfg", "requirements.txt"] },
|
|
52
|
+
{ language: "go", markers: ["go.mod"] },
|
|
53
|
+
{ language: "rust", markers: ["Cargo.toml"] },
|
|
54
|
+
{ language: "java", markers: ["pom.xml", "build.gradle", "build.gradle.kts"] },
|
|
55
|
+
{ language: "ruby", markers: ["Gemfile"] },
|
|
56
|
+
{ language: "php", markers: ["composer.json"] },
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
/** WI-328: 実効言語リストの出所。status 表示等で「なぜこの言語判定になったか」を示す。 */
|
|
60
|
+
export type ProjectLanguageSource = "declared" | "detected" | "fallback";
|
|
61
|
+
|
|
62
|
+
export interface ResolvedProjectLanguages {
|
|
63
|
+
readonly languages: readonly string[];
|
|
64
|
+
readonly source: ProjectLanguageSource;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* WI-328 (github#39 残課題): 実効言語リストとその出所を解決する唯一の実装。
|
|
69
|
+
* getProjectLanguages()(WI-319 の検出ロジック)と phasegate:status の言語表示が
|
|
70
|
+
* 同じテーブル・同じ優先順位を共有するために export する。
|
|
71
|
+
*
|
|
72
|
+
* 優先順位:
|
|
73
|
+
* 1. declared — config の project.languages 宣言(従来どおり最優先)
|
|
74
|
+
* 2. detected — project root のマーカーファイルから検出(WI-319 / github#39)
|
|
75
|
+
* 3. fallback — 検出ゼロなら typescript フォールバック(純 JS リポジトリ等の挙動維持)
|
|
76
|
+
*/
|
|
77
|
+
export function resolveProjectLanguages(
|
|
78
|
+
declaredLanguages: readonly string[] | undefined,
|
|
79
|
+
rootDir: string,
|
|
80
|
+
): ResolvedProjectLanguages {
|
|
81
|
+
if (declaredLanguages && declaredLanguages.length > 0) {
|
|
82
|
+
return { languages: [...declaredLanguages], source: "declared" };
|
|
83
|
+
}
|
|
84
|
+
const detected = detectLanguagesFromFilesystem(rootDir);
|
|
85
|
+
if (detected.length > 0) {
|
|
86
|
+
return { languages: detected, source: "detected" };
|
|
87
|
+
}
|
|
88
|
+
return { languages: ["typescript"], source: "fallback" };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function detectLanguagesFromFilesystem(rootDir: string): string[] {
|
|
92
|
+
const detected: string[] = [];
|
|
93
|
+
if (hasTypescriptMarker(rootDir)) detected.push("typescript");
|
|
94
|
+
for (const { language, markers } of LANGUAGE_MARKER_FILES) {
|
|
95
|
+
if (markers.some((marker) => existsSync(join(rootDir, marker)))) {
|
|
96
|
+
detected.push(language);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return detected;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* typescript の根拠は tsconfig.json の存在、または package.json の
|
|
104
|
+
* dependencies / devDependencies に typescript があることのみ。
|
|
105
|
+
* package.json の存在自体は根拠にしない(phasegate 導入時に phasegate 用
|
|
106
|
+
* package.json が置かれるため、誤検出の原因になる)。
|
|
107
|
+
*/
|
|
108
|
+
function hasTypescriptMarker(rootDir: string): boolean {
|
|
109
|
+
if (existsSync(join(rootDir, "tsconfig.json"))) return true;
|
|
110
|
+
const packageJsonPath = join(rootDir, "package.json");
|
|
111
|
+
if (!existsSync(packageJsonPath)) return false;
|
|
112
|
+
try {
|
|
113
|
+
const parsed = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as {
|
|
114
|
+
dependencies?: Record<string, string>;
|
|
115
|
+
devDependencies?: Record<string, string>;
|
|
116
|
+
};
|
|
117
|
+
return parsed.dependencies?.typescript !== undefined || parsed.devDependencies?.typescript !== undefined;
|
|
118
|
+
} catch {
|
|
119
|
+
// parse 失敗は typescript 根拠なしとして扱う(throw しない)
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
41
124
|
export class HarnessConfigValidatorConfigAdapter implements ValidatorConfigPort {
|
|
42
125
|
private readonly config: HarnessConfigV2Like;
|
|
126
|
+
private readonly rootDir: string;
|
|
43
127
|
|
|
44
|
-
constructor(config: HarnessConfigV2Like) {
|
|
128
|
+
constructor(config: HarnessConfigV2Like, rootDir: string = process.cwd()) {
|
|
45
129
|
this.config = config;
|
|
130
|
+
this.rootDir = rootDir;
|
|
46
131
|
}
|
|
47
132
|
|
|
48
133
|
async getLayerConfig(layer: "L2" | "L3" | "L4"): Promise<LayerConfig> {
|
|
@@ -95,8 +180,9 @@ export class HarnessConfigValidatorConfigAdapter implements ValidatorConfigPort
|
|
|
95
180
|
}
|
|
96
181
|
|
|
97
182
|
async getProjectLanguages(): Promise<readonly string[]> {
|
|
98
|
-
|
|
99
|
-
|
|
183
|
+
// WI-319 の宣言優先 → FS 検出 → typescript フォールバックの解決は
|
|
184
|
+
// resolveProjectLanguages() に一本化(WI-328 で status 表示と共有)。
|
|
185
|
+
return [...resolveProjectLanguages(this.config.project?.languages, this.rootDir).languages];
|
|
100
186
|
}
|
|
101
187
|
|
|
102
188
|
private normalizeValidatorId(idOrName: string): string {
|
|
@@ -18,18 +18,25 @@
|
|
|
18
18
|
* (layers.L3.requirementMatrixPath)から供給される。未指定時は既定値
|
|
19
19
|
* `.harness/requirement-test-matrix.json` を用いる。マトリクスが不在の場合は
|
|
20
20
|
* fail-closed で「設定されているが不在」という実行可能なメッセージを返す。
|
|
21
|
+
*
|
|
22
|
+
* FRESH PROJECT SKIP (WI-324): 例外として「matrix 不在 かつ StoryCatalog の
|
|
23
|
+
* story がゼロ」の場合のみ skipped=true を返す。phasegate 導入直後(story 未作成・
|
|
24
|
+
* matrix 未生成)のオンボーディングを fail-closed で阻害しないため。story が
|
|
25
|
+
* 1件でも存在するのに matrix が不在なら「あるべき matrix が消えた事故」として
|
|
26
|
+
* 従来どおり fail-closed。story 数の取得自体に失敗した場合も判定不能として
|
|
27
|
+
* 保守的に fail-closed 側へ倒す。
|
|
21
28
|
*/
|
|
22
|
-
import { access } from
|
|
23
|
-
import { isAbsolute, join } from
|
|
24
|
-
import type { AcCoveragePolicyPort } from
|
|
25
|
-
import type { HarnessErrorLike } from
|
|
29
|
+
import { access } from "node:fs/promises";
|
|
30
|
+
import { isAbsolute, join } from "node:path";
|
|
31
|
+
import type { AcCoveragePolicyPort } from "../../domain/ports/ac-coverage-policy-port.js";
|
|
32
|
+
import type { HarnessErrorLike } from "../../domain/value-objects/validation-result.js";
|
|
26
33
|
|
|
27
|
-
const DEFAULT_MATRIX_PATH =
|
|
34
|
+
const DEFAULT_MATRIX_PATH = ".harness/requirement-test-matrix.json";
|
|
28
35
|
|
|
29
36
|
function toL3004Error(message: string, suggestion: string): HarnessErrorLike {
|
|
30
37
|
return {
|
|
31
|
-
code: { value:
|
|
32
|
-
severity: { value:
|
|
38
|
+
code: { value: "L3-004", toString: () => "L3-004" },
|
|
39
|
+
severity: { value: "error", toString: () => "error" },
|
|
33
40
|
message,
|
|
34
41
|
suggestion,
|
|
35
42
|
};
|
|
@@ -39,21 +46,29 @@ export class NyquistAcCoveragePolicyAdapter implements AcCoveragePolicyPort {
|
|
|
39
46
|
async checkCoverage(context: { matrixFilePath?: string }): Promise<{
|
|
40
47
|
passed: boolean;
|
|
41
48
|
errors: readonly HarnessErrorLike[];
|
|
49
|
+
skipped?: boolean;
|
|
50
|
+
skipReason?: string;
|
|
42
51
|
}> {
|
|
43
52
|
const rootDir = process.cwd();
|
|
44
53
|
const relativeOrAbsolute =
|
|
45
|
-
context.matrixFilePath && context.matrixFilePath.length > 0
|
|
46
|
-
|
|
47
|
-
: DEFAULT_MATRIX_PATH;
|
|
48
|
-
const matrixFilePath = isAbsolute(relativeOrAbsolute)
|
|
49
|
-
? relativeOrAbsolute
|
|
50
|
-
: join(rootDir, relativeOrAbsolute);
|
|
54
|
+
context.matrixFilePath && context.matrixFilePath.length > 0 ? context.matrixFilePath : DEFAULT_MATRIX_PATH;
|
|
55
|
+
const matrixFilePath = isAbsolute(relativeOrAbsolute) ? relativeOrAbsolute : join(rootDir, relativeOrAbsolute);
|
|
51
56
|
|
|
52
57
|
// マトリクス不在は threshold-configured-but-absent と同様に fail-closed で
|
|
53
58
|
// 明確なメッセージを返す(ENOENT の生メッセージで落とさない)。
|
|
59
|
+
// 例外(WI-324): story がゼロのフレッシュプロジェクトのみ SKIP を返す。
|
|
54
60
|
try {
|
|
55
61
|
await access(matrixFilePath);
|
|
56
62
|
} catch {
|
|
63
|
+
if (await this.hasZeroStories(rootDir)) {
|
|
64
|
+
return {
|
|
65
|
+
passed: true,
|
|
66
|
+
errors: [],
|
|
67
|
+
skipped: true,
|
|
68
|
+
skipReason:
|
|
69
|
+
"story 未作成のため L3-004 をスキップ(story 作成後に requirement-test-matrix を生成すると有効化されます)",
|
|
70
|
+
};
|
|
71
|
+
}
|
|
57
72
|
return {
|
|
58
73
|
passed: false,
|
|
59
74
|
errors: [
|
|
@@ -69,7 +84,7 @@ export class NyquistAcCoveragePolicyAdapter implements AcCoveragePolicyPort {
|
|
|
69
84
|
// 有効 storyId 一覧を traceability-model の StoryCatalog から取得する(REAL registry)
|
|
70
85
|
const storyIds = await this.loadValidStoryIds(rootDir);
|
|
71
86
|
|
|
72
|
-
const { createNyquistValidationModule } = await import(
|
|
87
|
+
const { createNyquistValidationModule } = await import("../../../nyquist-validation/composition-root.js");
|
|
73
88
|
const mod = createNyquistValidationModule({
|
|
74
89
|
getStoryIds: async () => storyIds,
|
|
75
90
|
});
|
|
@@ -79,9 +94,7 @@ export class NyquistAcCoveragePolicyAdapter implements AcCoveragePolicyPort {
|
|
|
79
94
|
|
|
80
95
|
return {
|
|
81
96
|
passed: output.passed,
|
|
82
|
-
errors: output.errors.map((err) =>
|
|
83
|
-
toL3004Error(err.message, ''),
|
|
84
|
-
),
|
|
97
|
+
errors: output.errors.map((err) => toL3004Error(err.message, "")),
|
|
85
98
|
};
|
|
86
99
|
} catch (error) {
|
|
87
100
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -91,19 +104,35 @@ export class NyquistAcCoveragePolicyAdapter implements AcCoveragePolicyPort {
|
|
|
91
104
|
errors: [
|
|
92
105
|
toL3004Error(
|
|
93
106
|
`AC網羅ゲートの検査に失敗しました(fail-closed): ${message}`,
|
|
94
|
-
|
|
107
|
+
"requirement-test-matrix.json の存在・形式・スキーマ整合性を確認してください",
|
|
95
108
|
),
|
|
96
109
|
],
|
|
97
110
|
};
|
|
98
111
|
}
|
|
99
112
|
}
|
|
100
113
|
|
|
114
|
+
/**
|
|
115
|
+
* WI-324: 「story がゼロ」判定。StoryCatalog(traceability-model)の登録 story 数を
|
|
116
|
+
* シグナルとする。StoryCatalog は requirement-test-matrix の生成元と同じ
|
|
117
|
+
* user_stories.md を読むため、「matrix が本来存在しうるか」と一貫した判定になる。
|
|
118
|
+
* catalog 読み込み自体が失敗した場合(config 不正等)は判定不能として false を返し、
|
|
119
|
+
* 呼び出し側を保守的に fail-closed へ倒す。
|
|
120
|
+
*/
|
|
121
|
+
private async hasZeroStories(rootDir: string): Promise<boolean> {
|
|
122
|
+
try {
|
|
123
|
+
const storyIds = await this.loadValidStoryIds(rootDir);
|
|
124
|
+
return storyIds.length === 0;
|
|
125
|
+
} catch {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
101
130
|
private async loadValidStoryIds(rootDir: string): Promise<readonly string[]> {
|
|
102
|
-
const { createConfigFoundationModule } = await import(
|
|
131
|
+
const { createConfigFoundationModule } = await import("../../../config-foundation/composition-root.js");
|
|
103
132
|
const configModule = createConfigFoundationModule();
|
|
104
133
|
const resolvedConfig = await configModule.usecases.loadResolvedConfigUseCase.execute();
|
|
105
134
|
|
|
106
|
-
const { createTraceabilityModelModule } = await import(
|
|
135
|
+
const { createTraceabilityModelModule } = await import("../../../traceability-model/composition-root.js");
|
|
107
136
|
const traceModule = createTraceabilityModelModule(rootDir, {
|
|
108
137
|
pathRoots: { designDocsRoot: resolvedConfig.config.paths.designDocs },
|
|
109
138
|
});
|
|
@@ -4,8 +4,27 @@
|
|
|
4
4
|
*
|
|
5
5
|
* PhaseDependencyPhaseGatePolicyAdapter — PhaseGatePolicyPort実装
|
|
6
6
|
*/
|
|
7
|
-
import type { PhaseGatePolicyPort } from
|
|
8
|
-
import type { HarnessErrorLike } from
|
|
7
|
+
import type { PhaseGatePolicyPort } from "../../domain/ports/phase-gate-policy-port.js";
|
|
8
|
+
import type { HarnessErrorLike } from "../../domain/value-objects/validation-result.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* WI-357 (issue #29): `validate --layer L2` の失敗出力には従来
|
|
12
|
+
* 'phase gate prerequisites are not met' という事実の再掲しか載らず、
|
|
13
|
+
* 「何を作れば通るのか」がどの経路からも得られなかった。
|
|
14
|
+
* hook の block メッセージ側にしか復旧手順がないため、CLI から検証した
|
|
15
|
+
* エージェントは不足文書を推測で書くしかなくなる。
|
|
16
|
+
*
|
|
17
|
+
* 実行可能な復旧手順(scaffold コマンド)と、文書のセクション構成の在り処
|
|
18
|
+
* (skills/<skill>/SKILL.md、未配置なら `phasegate skills info`)を suggestion に載せる。
|
|
19
|
+
*/
|
|
20
|
+
export function buildPhaseGateRecoverySuggestion(unitName: string): string {
|
|
21
|
+
const unitArg = unitName.trim().length > 0 ? unitName.trim() : "<unit-id>";
|
|
22
|
+
return [
|
|
23
|
+
`不足している設計文書を scaffold で作成してください: npx phasegate scaffold-design --unit ${unitArg} --phase <logical|domain|uiux|unit-test|it-test> --apply`,
|
|
24
|
+
"各文書のセクション構成は導入先 repo の skills/<skill>/SKILL.md に記載されています(logical_design.md → skills/logical-designer/SKILL.md、domain_model.md → skills/domain-designer/SKILL.md、unit plan → skills/unit-designer/SKILL.md)。",
|
|
25
|
+
"skills/ が repo に無い場合は `npx phasegate skills info <skill-name>` で同じ内容を stdout から読めます。",
|
|
26
|
+
].join("\n");
|
|
27
|
+
}
|
|
9
28
|
|
|
10
29
|
export class PhaseDependencyPhaseGatePolicyAdapter implements PhaseGatePolicyPort {
|
|
11
30
|
async checkPrerequisites(context: { unitName: string; currentPhase: string }): Promise<{
|
|
@@ -18,11 +37,13 @@ export class PhaseDependencyPhaseGatePolicyAdapter implements PhaseGatePolicyPor
|
|
|
18
37
|
|
|
19
38
|
try {
|
|
20
39
|
// WI-085: paths config を phase-dependency-model に流入させる
|
|
21
|
-
const { createConfigFoundationModule } = await import(
|
|
22
|
-
const { toPhaseConfigSection } = await import(
|
|
40
|
+
const { createConfigFoundationModule } = await import("../../../config-foundation/composition-root.js");
|
|
41
|
+
const { toPhaseConfigSection } = await import(
|
|
42
|
+
"../../../config-foundation/application/mappers/phase-config-section-mapper.js"
|
|
43
|
+
);
|
|
23
44
|
const configModule = createConfigFoundationModule();
|
|
24
45
|
const resolvedConfig = await configModule.usecases.loadResolvedConfigUseCase.execute();
|
|
25
|
-
const { createPhaseDependencyModelModule } = await import(
|
|
46
|
+
const { createPhaseDependencyModelModule } = await import("../../../phase-dependency-model/composition-root.js");
|
|
26
47
|
const mod = createPhaseDependencyModelModule({
|
|
27
48
|
rootDir: process.cwd(),
|
|
28
49
|
phaseConfig: toPhaseConfigSection(resolvedConfig.config),
|
|
@@ -42,10 +63,10 @@ export class PhaseDependencyPhaseGatePolicyAdapter implements PhaseGatePolicyPor
|
|
|
42
63
|
satisfied: false,
|
|
43
64
|
violations: [
|
|
44
65
|
{
|
|
45
|
-
code: { value:
|
|
46
|
-
severity: { value:
|
|
66
|
+
code: { value: "L2-001", toString: () => "L2-001" },
|
|
67
|
+
severity: { value: "error", toString: () => "error" },
|
|
47
68
|
message: result.text,
|
|
48
|
-
suggestion:
|
|
69
|
+
suggestion: buildPhaseGateRecoverySuggestion(context.unitName),
|
|
49
70
|
},
|
|
50
71
|
],
|
|
51
72
|
};
|
|
@@ -57,10 +78,10 @@ export class PhaseDependencyPhaseGatePolicyAdapter implements PhaseGatePolicyPor
|
|
|
57
78
|
satisfied: false,
|
|
58
79
|
violations: [
|
|
59
80
|
{
|
|
60
|
-
code: { value:
|
|
61
|
-
severity: { value:
|
|
81
|
+
code: { value: "L2-002", toString: () => "L2-002" },
|
|
82
|
+
severity: { value: "error", toString: () => "error" },
|
|
62
83
|
message: `phase gate check returned unexpected exit code ${result.exitCode}; prerequisites cannot be confirmed`,
|
|
63
|
-
suggestion:
|
|
84
|
+
suggestion: "phase gate prerequisites could not be evaluated; treating as NOT satisfied (fail-closed)",
|
|
64
85
|
},
|
|
65
86
|
],
|
|
66
87
|
};
|
|
@@ -73,10 +94,10 @@ export class PhaseDependencyPhaseGatePolicyAdapter implements PhaseGatePolicyPor
|
|
|
73
94
|
satisfied: false,
|
|
74
95
|
violations: [
|
|
75
96
|
{
|
|
76
|
-
code: { value:
|
|
77
|
-
severity: { value:
|
|
97
|
+
code: { value: "L2-002", toString: () => "L2-002" },
|
|
98
|
+
severity: { value: "error", toString: () => "error" },
|
|
78
99
|
message: `phase gate prerequisite check could not be evaluated: ${message}`,
|
|
79
|
-
suggestion:
|
|
100
|
+
suggestion: "phase gate prerequisites could not be evaluated; treating as NOT satisfied (fail-closed)",
|
|
80
101
|
},
|
|
81
102
|
],
|
|
82
103
|
};
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* TraceabilityMetadataPolicyAdapter — MetadataPolicyPort実装
|
|
6
6
|
*/
|
|
7
|
-
import type { MetadataPolicyPort } from
|
|
8
|
-
import type { HarnessErrorLike } from
|
|
7
|
+
import type { MetadataPolicyPort } from "../../domain/ports/metadata-policy-port.js";
|
|
8
|
+
import type { HarnessErrorLike } from "../../domain/value-objects/validation-result.js";
|
|
9
9
|
|
|
10
10
|
export class TraceabilityMetadataPolicyAdapter implements MetadataPolicyPort {
|
|
11
11
|
async validateMetadata(context: { filePath: string; fileContent: string }): Promise<{
|
|
@@ -19,21 +19,26 @@ export class TraceabilityMetadataPolicyAdapter implements MetadataPolicyPort {
|
|
|
19
19
|
const hasLayer = /@layer\s+\S+/.test(fileContent);
|
|
20
20
|
const hasStoryId = /@story-id\s+H\d{2}-\d{2}/.test(fileContent) || /@story\s+H\d{2}-\d{2}/.test(fileContent);
|
|
21
21
|
|
|
22
|
+
// WI-335: この suggestion は「<directive> <値> を先頭コメントに追加」という機械適用可能な
|
|
23
|
+
// 文言契約を持つ(remediationType: 'mechanical')。文言をこの形から変える場合は
|
|
24
|
+
// remediation-round-trip テスト(機械適用器が同じ文言を解析する)が fail する。
|
|
22
25
|
if (!hasUnit) {
|
|
23
26
|
errors.push({
|
|
24
|
-
code: { value:
|
|
25
|
-
severity: { value:
|
|
27
|
+
code: { value: "L2-002", toString: () => "L2-002" },
|
|
28
|
+
severity: { value: "error", toString: () => "error" },
|
|
26
29
|
message: `メタデータ不足: @unit アノテーションがありません (${filePath})`,
|
|
27
|
-
suggestion:
|
|
30
|
+
suggestion: "// @unit <unit-name> を先頭コメントに追加してください",
|
|
31
|
+
remediationType: "mechanical",
|
|
28
32
|
});
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
if (!hasLayer) {
|
|
32
36
|
errors.push({
|
|
33
|
-
code: { value:
|
|
34
|
-
severity: { value:
|
|
37
|
+
code: { value: "L2-002", toString: () => "L2-002" },
|
|
38
|
+
severity: { value: "error", toString: () => "error" },
|
|
35
39
|
message: `メタデータ不足: @layer アノテーションがありません (${filePath})`,
|
|
36
|
-
suggestion:
|
|
40
|
+
suggestion: "// @layer domain|application|infrastructure|presentation を先頭コメントに追加してください",
|
|
41
|
+
remediationType: "mechanical",
|
|
37
42
|
});
|
|
38
43
|
}
|
|
39
44
|
|
|
@@ -4,34 +4,43 @@
|
|
|
4
4
|
*
|
|
5
5
|
* AgentValidationResultFormatter — AIエージェント向け詳細テキスト
|
|
6
6
|
*/
|
|
7
|
-
import type { AggregatedValidationReport } from
|
|
7
|
+
import type { AggregatedValidationReport } from "../../application/dto/aggregated-validation-report.js";
|
|
8
8
|
|
|
9
9
|
export class AgentValidationResultFormatter {
|
|
10
10
|
format(report: AggregatedValidationReport): string {
|
|
11
11
|
const lines: string[] = [];
|
|
12
|
-
lines.push(
|
|
13
|
-
lines.push(`OVERALL: ${report.overallPassed ?
|
|
14
|
-
lines.push(
|
|
15
|
-
|
|
12
|
+
lines.push("VALIDATION_REPORT");
|
|
13
|
+
lines.push(`OVERALL: ${report.overallPassed ? "PASS" : "FAIL"}`);
|
|
14
|
+
lines.push(
|
|
15
|
+
`TOTAL: ${report.totalValidators} PASSED: ${report.passedValidators} FAILED: ${report.failedValidators} SKIPPED: ${report.skippedValidators}`,
|
|
16
|
+
);
|
|
17
|
+
lines.push("");
|
|
16
18
|
|
|
17
19
|
for (const result of report.results) {
|
|
18
20
|
lines.push(`VALIDATOR: ${result.validatorId}`);
|
|
19
|
-
lines.push(`STATUS: ${result.skipped ?
|
|
21
|
+
lines.push(`STATUS: ${result.skipped ? "SKIPPED" : result.passed ? "PASSED" : "FAILED"}`);
|
|
20
22
|
lines.push(`DURATION: ${result.durationMs}ms`);
|
|
21
23
|
if (result.skipped && result.skipReason) {
|
|
22
24
|
lines.push(`SKIP_REASON: ${result.skipReason}`);
|
|
23
25
|
}
|
|
24
26
|
if (result.errors.length > 0) {
|
|
25
|
-
lines.push(
|
|
27
|
+
lines.push("ERRORS:");
|
|
26
28
|
for (const error of result.errors) {
|
|
27
29
|
lines.push(` - CODE: ${error.code}`);
|
|
28
30
|
lines.push(` MESSAGE: ${error.message}`);
|
|
29
|
-
|
|
31
|
+
// WI-357: 復旧手順を載せた suggestion は複数行になりうる。
|
|
32
|
+
// 素朴に埋め込むと 2 行目以降がインデントを失い、ERRORS ブロックの
|
|
33
|
+
// 構造が壊れて機械読みできなくなるため継続行を明示的に字下げする。
|
|
34
|
+
const suggestionLines = String(error.suggestion).split("\n");
|
|
35
|
+
lines.push(` SUGGESTION: ${suggestionLines[0]}`);
|
|
36
|
+
for (const continuation of suggestionLines.slice(1)) {
|
|
37
|
+
lines.push(` ${continuation}`);
|
|
38
|
+
}
|
|
30
39
|
}
|
|
31
40
|
}
|
|
32
|
-
lines.push(
|
|
41
|
+
lines.push("");
|
|
33
42
|
}
|
|
34
43
|
|
|
35
|
-
return lines.join(
|
|
44
|
+
return lines.join("\n");
|
|
36
45
|
}
|
|
37
46
|
}
|
|
@@ -21,6 +21,25 @@ Quick Mode下での軽微変更実装スキル。story-implementorの緩和版
|
|
|
21
21
|
| `test` | テスト追加・修正(新機能のテストではない) |
|
|
22
22
|
| `config` | 設定変更、依存バージョン更新 |
|
|
23
23
|
|
|
24
|
+
> **カテゴリ判定はファイルパスベース(意図ベースではない)**
|
|
25
|
+
> pre-tool-use hook は変更の「意図」ではなく **書き込み対象パス** からカテゴリを機械判定する。
|
|
26
|
+
> 特に `config` は allowlist で、以下だけが `config` に落ちる:
|
|
27
|
+
> `*.config.json` / `*.config.ts` / `phasegate.config.json` / `.github/workflows/*.y(a)ml` /
|
|
28
|
+
> リポジトリ直下の `.gitignore` `.gitattributes` `.editorconfig` `.npmrc` `.nvmrc` `tsconfig.json` `tsconfig.*.json` / `.husky/` 配下。
|
|
29
|
+
> allowlist 外のパスは新規作成(CREATE)なら `feature` に落ちて **必ずブロックされる**(fail-closed)。
|
|
30
|
+
>
|
|
31
|
+
> ブロックされたら、まず分類を確認する:
|
|
32
|
+
> ```bash
|
|
33
|
+
> npx phasegate check-change-category --paths <file1>,<file2>
|
|
34
|
+
> ```
|
|
35
|
+
> 判定が妥当で、かつ許可カテゴリの一時拡張が必要なら CLI 経由で計画・適用する
|
|
36
|
+
> (`phasegate.config.json` の手編集は保護ファイルとしてブロックされる):
|
|
37
|
+
> ```bash
|
|
38
|
+
> npx phasegate config:plan --intent quick-mode-relax --dry-run --json # 差分確認
|
|
39
|
+
> npx phasegate config:plan --intent quick-mode-relax --apply --json # 承認後に適用
|
|
40
|
+
> ```
|
|
41
|
+
> 判定が `feature` / `domain` / `api` なら、それは Quick Mode のスコープ外。`story-implementor` に切り替える。
|
|
42
|
+
|
|
24
43
|
### WI-aware trivial path(ISSUE-026 Phase D)
|
|
25
44
|
|
|
26
45
|
作業対象に `WI-XXX` が明示されている場合、最初に `docs/inception/{unit}/WI-XXX/description.md` または `docs/inception/_cross/WI-XXX/description.md` のfrontmatterを確認する。
|