phasegate 0.160.21 → 0.162.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 +34 -0
- package/README.md +6 -1
- package/docs/contracts/requirement-test-matrix.schema.json +1 -1
- package/docs/guide/cli-reference.md +1 -1
- package/docs/guide/configuration.md +31 -1
- package/docs/guide/skills-overview.md +2 -0
- package/docs/principles/model-routing.md +1 -0
- package/docs/templates/personal/phasegate-local-config.json +4 -1
- package/docs/templates/project/phasegate.config.json +4 -1
- package/package.json +6 -3
- package/scripts/harness/agent-integration/domain/services/bash-write-target-extractor.ts +91 -23
- package/scripts/harness/agent-integration/domain/value-objects/protected-file-list.ts +4 -0
- package/scripts/harness/agent-integration/domain/value-objects/write-target-scope.ts +54 -5
- package/scripts/harness/agent-integration/infrastructure/adapters/ci-governance-baseline-grandfather-adapter.ts +25 -2
- package/scripts/harness/agent-integration/infrastructure/adapters/phase-gate-query-adapter.ts +17 -2
- package/scripts/harness/biome-ast-engine/domain/value-objects/import-graph.ts +28 -1
- package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +6 -1
- package/scripts/harness/config-foundation/domain/harness-config.ts +10 -0
- package/scripts/harness/config-foundation/domain/services/preset-resolution-service.ts +2 -0
- package/scripts/harness/config-foundation/domain/value-objects/l3-config.ts +4 -0
- package/scripts/harness/config-foundation/domain/value-objects/layers-config.ts +1 -1
- package/scripts/harness/config-foundation/domain/value-objects/project-config.ts +20 -5
- package/scripts/harness/config-foundation/infrastructure/presets/minimal.json +2 -1
- package/scripts/harness/config-foundation/infrastructure/presets/standard.json +2 -1
- package/scripts/harness/config-foundation/infrastructure/presets/strict.json +2 -1
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +39 -9
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +39 -9
- package/scripts/harness/harness-api/infrastructure/adapters/phase-dependency-model-query-adapter.ts +18 -4
- package/scripts/harness/harness-api/infrastructure/adapters/validator-system-execution-adapter.ts +27 -4
- package/scripts/harness/installation/application/ports/model-delegation-port.ts +10 -0
- package/scripts/harness/installation/application/usecases/run-install.ts +67 -5
- package/scripts/harness/installation/application/usecases/run-reconcile.ts +53 -5
- package/scripts/harness/installation/composition-root.ts +4 -2
- package/scripts/harness/installation/infrastructure/adapters/skill-deployer-model-delegation-adapter.ts +16 -0
- package/scripts/harness/installation/presentation/cli/install-handler.ts +2 -1
- package/scripts/harness/main.ts +99 -13
- package/scripts/harness/nyquist-validation/application/dto/generate-matrix-output.ts +9 -7
- package/scripts/harness/nyquist-validation/domain/services/matrix-validation-service.ts +27 -2
- package/scripts/harness/nyquist-validation/domain/services/requirement-intent-coverage-service.ts +4 -4
- package/scripts/harness/nyquist-validation/domain/value-objects/intent-coverage.ts +33 -0
- package/scripts/harness/nyquist-validation/infrastructure/adapters/markdown-requirement-source-adapter.ts +3 -1
- package/scripts/harness/quick-mode/application/usecases/classify-change-category-usecase.ts +8 -3
- package/scripts/harness/quick-mode/composition-root.ts +3 -0
- package/scripts/harness/quick-mode/domain/value-objects/quick-mode-config.ts +6 -1
- package/scripts/harness/quick-mode/infrastructure/adapters/validator-system-quick-mode-execution-adapter.ts +18 -0
- package/scripts/harness/quick-mode/presentation/handlers/ci-check-quick-mode-handler.ts +51 -5
- package/scripts/harness/setup/skill-deployer.ts +47 -0
- package/scripts/harness/skill-quality/domain/services/skill-structure-validator.ts +21 -0
- package/scripts/harness/skill-quality/domain/value-objects/skill-structure.ts +2 -0
- package/scripts/harness/skill-quality/infrastructure/adapters/l1-biome-validator-adapter.ts +12 -2
- package/scripts/harness/skill-quality/infrastructure/adapters/l2-validator-system-adapter.ts +12 -2
- package/scripts/harness/traceability-model/application/usecases/apply-work-item-status-usecase.ts +6 -1
- package/scripts/harness/traceability-model/infrastructure/parsers/story-catalog-parser.ts +28 -9
- package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +76 -25
- package/scripts/harness/validator-system/application/use-cases/run-l4-validators-usecase.ts +20 -8
- package/scripts/harness/validator-system/composition-root.ts +8 -1
- package/scripts/harness/validator-system/domain/ports/validator-config-port.ts +2 -0
- package/scripts/harness/validator-system/domain/services/validator-language-capability-service.ts +64 -0
- package/scripts/harness/validator-system/infrastructure/adapters/file-system-security-pattern-scanner-adapter.ts +16 -6
- package/scripts/harness/validator-system/infrastructure/adapters/file-system-work-item-reflection-adapter.ts +10 -1
- package/scripts/harness/validator-system/infrastructure/adapters/harness-config-validator-config-adapter.ts +7 -1
- package/scripts/harness/validator-system/infrastructure/adapters/import-graph-source-analysis-adapter.ts +35 -3
- package/scripts/harness/validator-system/infrastructure/adapters/nyquist-ac-coverage-policy-adapter.ts +86 -14
- package/scripts/harness/validator-system/infrastructure/adapters/phase-dependency-phase-gate-policy-adapter.ts +29 -3
- package/skills/cascade-updater/SKILL.md +1 -0
- package/skills/codebase-mapper/SKILL.md +1 -0
- package/skills/codex-delegator/SKILL.md +1 -0
- package/skills/consistency-checker/SKILL.md +1 -0
- package/skills/doc-freshness-checker/SKILL.md +1 -0
- package/skills/domain-designer/SKILL.md +1 -0
- package/skills/engineering-perspective/SKILL.md +1 -0
- package/skills/environment-designer/SKILL.md +1 -0
- package/skills/implementation-planner/SKILL.md +1 -0
- package/skills/implementation-readiness-checker/SKILL.md +1 -0
- package/skills/it-test-designer/SKILL.md +1 -0
- package/skills/it-test-logic-designer/SKILL.md +1 -0
- package/skills/logical-designer/SKILL.md +1 -0
- package/skills/mock-designer/SKILL.md +1 -0
- package/skills/phasegate-config-doctor/SKILL.md +1 -0
- package/skills/phasegate-toolkit-guide/SKILL.md +1 -0
- package/skills/pointer-validator/SKILL.md +1 -0
- package/skills/product-architect/SKILL.md +1 -0
- package/skills/quick-implementor/SKILL.md +1 -0
- package/skills/scenario-test-designer/SKILL.md +1 -0
- package/skills/scenario-test-logic-designer/SKILL.md +1 -0
- package/skills/skill-creator/SKILL.md +1 -0
- package/skills/story-implementor/SKILL.md +1 -0
- package/skills/story-mapper/SKILL.md +1 -0
- package/skills/story-writer/SKILL.md +1 -0
- package/skills/test-coverage-checker/SKILL.md +1 -0
- package/skills/uiux-designer/SKILL.md +1 -0
- package/skills/unit-designer/SKILL.md +1 -0
- package/skills/unit-test-designer/SKILL.md +1 -0
- package/skills/unit-test-logic-designer/SKILL.md +1 -0
- package/templates/.claude/scripts/deny-check.sh +73 -12
- package/templates/.claude/settings.json +4 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @layer application
|
|
3
3
|
* @unit validator-system
|
|
4
|
+
* @work-item-id WI-212
|
|
4
5
|
*
|
|
5
6
|
* RunL3ValidatorsUseCase — H08-02: L3バリデータ実行
|
|
6
7
|
*/
|
|
@@ -8,6 +9,7 @@ import { ValidatorId } from '../../domain/value-objects/validator-id.js';
|
|
|
8
9
|
import { ValidationResult } from '../../domain/value-objects/validation-result.js';
|
|
9
10
|
import { ValidatorRegistry } from '../../domain/services/validator-registry.js';
|
|
10
11
|
import { ValidatorExecutionService, ValidatorExecutionError } from '../../domain/services/validator-execution-service.js';
|
|
12
|
+
import { ValidatorLanguageCapabilityService } from '../../domain/services/validator-language-capability-service.js';
|
|
11
13
|
import { ValidationResultContractMapper } from '../mappers/validation-result-contract-mapper.js';
|
|
12
14
|
import type { ValidationResultContract } from '../dto/validation-result-contract.js';
|
|
13
15
|
import type { RunL3ValidatorsInput } from '../dto/run-l3-validators-input.js';
|
|
@@ -43,6 +45,7 @@ export class RunL3ValidatorsUseCase {
|
|
|
43
45
|
private readonly coverageReportPort?: RunL3ValidatorsUseCaseDeps['coverageReportPort'];
|
|
44
46
|
private readonly securityScannerPort?: SecurityPatternScannerPort;
|
|
45
47
|
private readonly performanceScannerPort?: PerformanceScannerPort;
|
|
48
|
+
private readonly languageCapabilityService = new ValidatorLanguageCapabilityService();
|
|
46
49
|
|
|
47
50
|
constructor(deps: RunL3ValidatorsUseCaseDeps) {
|
|
48
51
|
this.registry = deps.validatorRegistry;
|
|
@@ -77,36 +80,80 @@ export class RunL3ValidatorsUseCase {
|
|
|
77
80
|
return [];
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
const projectLanguages = await this.getProjectLanguages();
|
|
84
|
+
const { executableDefinitions, unsupportedResults, unsupportedValidatorIds } =
|
|
85
|
+
this.languageCapabilityService.splitDefinitions(definitions, projectLanguages);
|
|
86
|
+
|
|
87
|
+
const results = this.executionService.execute(executableDefinitions, [layerConfig]);
|
|
88
|
+
const overrideMap = new Map<string, ValidationResult>(
|
|
89
|
+
[...unsupportedResults, ...results].map((result) => [result.validatorId.value, result]),
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// L3-003: カバレッジ判定(カバレッジゲートはオプトイン)
|
|
93
|
+
// - coverageThreshold 未設定 → SKIP(透過的に判定をスキップ。getCoverage() は呼ばない)
|
|
94
|
+
// - coverageThreshold 設定あり → getCoverage() を try/catch で包み FAIL-CLOSED で判定する
|
|
95
|
+
// - 閾値未満 → FAIL / 閾値以上 → PASS
|
|
96
|
+
// - レポート不在などで getCoverage() が失敗 → FAIL(合格扱いにしない)
|
|
97
|
+
// このブロックは例外を送出せず、L3-003 の per-validator 結果のみを差し替える。
|
|
98
|
+
// これにより兄弟バリデータ(L3-001/002/004 および L2/L4 バッチ)は常に通常実行される。
|
|
99
|
+
const l3003InScope =
|
|
100
|
+
!unsupportedValidatorIds.has('L3-003') &&
|
|
101
|
+
definitions.some((d) => d.validatorId.value === 'L3-003');
|
|
102
|
+
if (this.coverageReportPort && l3003InScope) {
|
|
103
|
+
const l3003Id = ValidatorId.create('L3-003');
|
|
83
104
|
const threshold = layerConfig.getThreshold('coverageThreshold');
|
|
84
105
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
{
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
if (threshold === null) {
|
|
107
|
+
overrideMap.set(
|
|
108
|
+
'L3-003',
|
|
109
|
+
ValidationResult.skipWithReason(
|
|
110
|
+
l3003Id,
|
|
111
|
+
'coverageThreshold が未設定のためカバレッジ判定をスキップ(カバレッジゲートはオプトイン)',
|
|
112
|
+
),
|
|
113
|
+
);
|
|
114
|
+
} else {
|
|
115
|
+
try {
|
|
116
|
+
const coverageData = await this.coverageReportPort.getCoverage();
|
|
117
|
+
if (coverageData.overallCoverage < threshold) {
|
|
118
|
+
overrideMap.set(
|
|
119
|
+
'L3-003',
|
|
120
|
+
ValidationResult.fail(
|
|
121
|
+
l3003Id,
|
|
122
|
+
[
|
|
123
|
+
{
|
|
124
|
+
code: 'L3-003',
|
|
125
|
+
severity: 'error',
|
|
126
|
+
message: `カバレッジ不足: 現在値 ${coverageData.overallCoverage}%、不足 ${threshold - coverageData.overallCoverage}%`,
|
|
127
|
+
suggestion: `テストカバレッジを ${threshold}% 以上に引き上げてください`,
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
0,
|
|
131
|
+
),
|
|
132
|
+
);
|
|
133
|
+
} else {
|
|
134
|
+
overrideMap.set('L3-003', ValidationResult.pass(l3003Id, 0));
|
|
135
|
+
}
|
|
136
|
+
} catch {
|
|
137
|
+
// レポート不在などで取得失敗 → FAIL-CLOSED(例外は握りつぶし per-validator FAIL に変換)
|
|
138
|
+
overrideMap.set(
|
|
139
|
+
'L3-003',
|
|
140
|
+
ValidationResult.fail(
|
|
141
|
+
l3003Id,
|
|
142
|
+
[
|
|
143
|
+
{
|
|
144
|
+
code: 'L3-003',
|
|
145
|
+
severity: 'error',
|
|
146
|
+
message: `coverageThreshold=${threshold}% が設定されていますがカバレッジレポートが見つかりません(テストをカバレッジ付きで実行してください)`,
|
|
147
|
+
suggestion: 'vitest --coverage 等でカバレッジレポートを生成してから再実行してください',
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
0,
|
|
151
|
+
),
|
|
152
|
+
);
|
|
153
|
+
}
|
|
104
154
|
}
|
|
105
155
|
}
|
|
106
156
|
|
|
107
|
-
const results = this.executionService.execute(definitions, [layerConfig]);
|
|
108
|
-
const overrideMap = new Map<string, ValidationResult>(results.map((result) => [result.validatorId.value, result]));
|
|
109
|
-
|
|
110
157
|
if (this.securityScannerPort) {
|
|
111
158
|
const l3001Result = overrideMap.get('L3-001');
|
|
112
159
|
if (l3001Result && !l3001Result.skipped) {
|
|
@@ -153,4 +200,8 @@ export class RunL3ValidatorsUseCase {
|
|
|
153
200
|
);
|
|
154
201
|
return this.mapper.toContracts(finalResults);
|
|
155
202
|
}
|
|
203
|
+
|
|
204
|
+
private async getProjectLanguages(): Promise<readonly string[]> {
|
|
205
|
+
return this.configPort.getProjectLanguages ? await this.configPort.getProjectLanguages() : ['typescript'];
|
|
206
|
+
}
|
|
156
207
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* @unit validator-system
|
|
4
4
|
* @work-item-id WI-107 / WI-156
|
|
5
5
|
* @work-item-id WI-217
|
|
6
|
+
* @work-item-id WI-212
|
|
6
7
|
*
|
|
7
8
|
* RunL4ValidatorsUseCase — H08-03: L4バリデータ実行
|
|
8
9
|
*/
|
|
@@ -11,6 +12,7 @@ import { ValidationResult } from '../../domain/value-objects/validation-result.j
|
|
|
11
12
|
import { LayerConfig } from '../../domain/value-objects/layer-config.js';
|
|
12
13
|
import { ValidatorRegistry } from '../../domain/services/validator-registry.js';
|
|
13
14
|
import { ValidatorExecutionService, ValidatorExecutionError } from '../../domain/services/validator-execution-service.js';
|
|
15
|
+
import { ValidatorLanguageCapabilityService } from '../../domain/services/validator-language-capability-service.js';
|
|
14
16
|
import { ValidationResultContractMapper } from '../mappers/validation-result-contract-mapper.js';
|
|
15
17
|
import type { ValidationResultContract } from '../dto/validation-result-contract.js';
|
|
16
18
|
import type { RunL4ValidatorsInput } from '../dto/run-l4-validators-input.js';
|
|
@@ -93,6 +95,7 @@ export class RunL4ValidatorsUseCase {
|
|
|
93
95
|
private readonly pathRoots: { readonly inceptionRoot: string; readonly designRoot: string };
|
|
94
96
|
private readonly checkDocFreshnessUseCase?: CheckDocFreshnessUseCasePort;
|
|
95
97
|
private readonly validateDocPointersUseCase?: ValidateDocPointersUseCasePort;
|
|
98
|
+
private readonly languageCapabilityService = new ValidatorLanguageCapabilityService();
|
|
96
99
|
|
|
97
100
|
constructor(deps: RunL4ValidatorsUseCaseDeps) {
|
|
98
101
|
this.registry = deps.validatorRegistry;
|
|
@@ -141,11 +144,17 @@ export class RunL4ValidatorsUseCase {
|
|
|
141
144
|
thresholds: { ...layerConfig.thresholds },
|
|
142
145
|
strictOnly: layerConfig.strictOnly,
|
|
143
146
|
preset: layerConfig.preset,
|
|
144
|
-
|
|
147
|
+
})
|
|
145
148
|
: layerConfig;
|
|
146
149
|
|
|
147
|
-
const
|
|
148
|
-
const
|
|
150
|
+
const projectLanguages = await this.getProjectLanguages();
|
|
151
|
+
const { executableDefinitions, unsupportedResults } =
|
|
152
|
+
this.languageCapabilityService.splitDefinitions(definitions, projectLanguages);
|
|
153
|
+
|
|
154
|
+
const results = this.executionService.execute(executableDefinitions, [effectiveLayerConfig]);
|
|
155
|
+
const overrideMap = new Map<string, ValidationResult>(
|
|
156
|
+
[...unsupportedResults, ...results].map((result) => [result.validatorId.value, result]),
|
|
157
|
+
);
|
|
149
158
|
|
|
150
159
|
if (this.driftDetectionService) {
|
|
151
160
|
const l4001Result = overrideMap.get('L4-001');
|
|
@@ -168,9 +177,12 @@ export class RunL4ValidatorsUseCase {
|
|
|
168
177
|
const l4002Result = overrideMap.get('L4-002');
|
|
169
178
|
if (l4002Result && !l4002Result.skipped) {
|
|
170
179
|
const report = await this.consistencyCheckService.check(input.targetUnits ? [...input.targetUnits] : undefined);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
180
|
+
// WI-217: 標準レイアウト (docs/inception, docs/product/construction) でも
|
|
181
|
+
// reflection を必ず実行する。以前は usesConfiguredDocumentRoots() が false を
|
|
182
|
+
// 返すため標準構成では reflection が恒久的にスキップされ、L4-002 が常に PASS
|
|
183
|
+
// していた。description が存在しない場合はアダプタ側が skipReason を返すため、
|
|
184
|
+
// 常時実行しても未整備プロジェクトを誤って失敗させることはない。
|
|
185
|
+
const reflectionResult = await this.consistencyCheckService.checkWorkItemReflection(this.pathRoots);
|
|
174
186
|
const architectureSemanticErrors = this.architectureSemanticAnalysisService
|
|
175
187
|
? await this.architectureSemanticAnalysisService.analyze()
|
|
176
188
|
: [];
|
|
@@ -276,8 +288,8 @@ export class RunL4ValidatorsUseCase {
|
|
|
276
288
|
return [...executionErrors, ...freshnessFindings];
|
|
277
289
|
}
|
|
278
290
|
|
|
279
|
-
private
|
|
280
|
-
return this.
|
|
291
|
+
private async getProjectLanguages(): Promise<readonly string[]> {
|
|
292
|
+
return this.configPort.getProjectLanguages ? await this.configPort.getProjectLanguages() : ['typescript'];
|
|
281
293
|
}
|
|
282
294
|
|
|
283
295
|
private toPointerValidationHarnessErrors(output: ValidateDocPointersOutputContract): readonly ValidationResult['errors'][number][] {
|
|
@@ -29,6 +29,7 @@ import { FileSystemContractTraceabilityPolicyAdapter } from './infrastructure/ad
|
|
|
29
29
|
import { PhaseDependencyPhaseGatePolicyAdapter } from './infrastructure/adapters/phase-dependency-phase-gate-policy-adapter.js';
|
|
30
30
|
import { TraceabilityMetadataPolicyAdapter } from './infrastructure/adapters/traceability-metadata-policy-adapter.js';
|
|
31
31
|
import { NyquistAcCoveragePolicyAdapter } from './infrastructure/adapters/nyquist-ac-coverage-policy-adapter.js';
|
|
32
|
+
import { JsonCoverageReportAdapter } from './infrastructure/adapters/json-coverage-report-adapter.js';
|
|
32
33
|
import { BiomeAstTestQualityAnalyzerAdapter } from './infrastructure/adapters/biome-ast-test-quality-analyzer-adapter.js';
|
|
33
34
|
import { FileSystemSecurityPatternScannerAdapter } from './infrastructure/adapters/file-system-security-pattern-scanner-adapter.js';
|
|
34
35
|
import { AstPerformanceScannerAdapter } from './infrastructure/adapters/ast-performance-scanner-adapter.js';
|
|
@@ -54,7 +55,7 @@ const DEFAULT_CONFIG = {
|
|
|
54
55
|
preset: 'standard' as const,
|
|
55
56
|
layers: {
|
|
56
57
|
L2: { enabled: true, validators: ['L2-001', 'L2-002', 'L2-003', 'L2-013', 'L2-014', 'L2-015'] },
|
|
57
|
-
L3: { enabled: true, validators: ['L3-001', 'L3-002', 'L3-003', 'L3-004'], coverageThreshold: 90, bundleSizeLimit: 512000 },
|
|
58
|
+
L3: { enabled: true, validators: ['L3-001', 'L3-002', 'L3-003', 'L3-004'], coverageThreshold: 90, bundleSizeLimit: 512000, requirementMatrixPath: '.harness/requirement-test-matrix.json' },
|
|
58
59
|
L4: { enabled: true, validators: ['L4-001', 'L4-002', 'L4-003', 'L4-004', 'L4-005', 'L4-006'] },
|
|
59
60
|
},
|
|
60
61
|
paths: {
|
|
@@ -149,6 +150,11 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
|
|
|
149
150
|
const phaseGatePolicyPort = new PhaseDependencyPhaseGatePolicyAdapter();
|
|
150
151
|
const metadataPolicyPort = new TraceabilityMetadataPolicyAdapter();
|
|
151
152
|
const acCoveragePolicyPort = new NyquistAcCoveragePolicyAdapter();
|
|
153
|
+
// L3-003: テストカバレッジレポート(vitest --coverage の json-summary 出力)を読み取る。
|
|
154
|
+
// 未配線だと L3-003 は装飾的な pass になり 90% 閾値が形骸化するため必ず配線する。
|
|
155
|
+
const coverageReportPort = new JsonCoverageReportAdapter(
|
|
156
|
+
join(process.cwd(), 'coverage', 'coverage-summary.json'),
|
|
157
|
+
);
|
|
152
158
|
const testQualityAnalyzerPort = new BiomeAstTestQualityAnalyzerAdapter();
|
|
153
159
|
const securityScannerPort = new FileSystemSecurityPatternScannerAdapter();
|
|
154
160
|
const performanceScannerPort = new AstPerformanceScannerAdapter();
|
|
@@ -189,6 +195,7 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
|
|
|
189
195
|
validatorConfigPort: configPort,
|
|
190
196
|
contractMapper,
|
|
191
197
|
acCoveragePolicyPort,
|
|
198
|
+
coverageReportPort,
|
|
192
199
|
securityScannerPort,
|
|
193
200
|
performanceScannerPort,
|
|
194
201
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @layer domain
|
|
3
3
|
* @unit validator-system
|
|
4
|
+
* @work-item-id WI-212
|
|
4
5
|
*
|
|
5
6
|
* ValidatorConfigPort — HarnessConfigV2からLayerConfig取得
|
|
6
7
|
*/
|
|
@@ -8,4 +9,5 @@ import type { LayerConfig } from '../value-objects/layer-config.js';
|
|
|
8
9
|
|
|
9
10
|
export interface ValidatorConfigPort {
|
|
10
11
|
getLayerConfig(layer: 'L2' | 'L3' | 'L4'): Promise<LayerConfig>;
|
|
12
|
+
getProjectLanguages?(): Promise<readonly string[]>;
|
|
11
13
|
}
|
package/scripts/harness/validator-system/domain/services/validator-language-capability-service.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @layer domain
|
|
3
|
+
* @unit validator-system
|
|
4
|
+
* @work-item-id WI-212
|
|
5
|
+
*/
|
|
6
|
+
import type { ValidatorId } from '../value-objects/validator-id.js';
|
|
7
|
+
import { ValidationResult } from '../value-objects/validation-result.js';
|
|
8
|
+
import type { ValidatorDefinition } from '../value-objects/validator-definition.js';
|
|
9
|
+
|
|
10
|
+
const DEFAULT_SUPPORTED_LANGUAGES = ['typescript'] as const;
|
|
11
|
+
|
|
12
|
+
const VALIDATOR_LANGUAGE_CAPABILITIES: Readonly<Record<string, readonly string[]>> = {
|
|
13
|
+
'L3-002': DEFAULT_SUPPORTED_LANGUAGES,
|
|
14
|
+
'L3-003': DEFAULT_SUPPORTED_LANGUAGES,
|
|
15
|
+
'L4-003': DEFAULT_SUPPORTED_LANGUAGES,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export class ValidatorLanguageCapabilityService {
|
|
19
|
+
getUnsupportedValidatorIds(
|
|
20
|
+
validatorIds: readonly ValidatorId[],
|
|
21
|
+
projectLanguages: readonly string[],
|
|
22
|
+
): ReadonlySet<string> {
|
|
23
|
+
const normalizedProjectLanguages = new Set(projectLanguages.map((language) => language.toLowerCase()));
|
|
24
|
+
const unsupportedIds = validatorIds
|
|
25
|
+
.filter((validatorId) => {
|
|
26
|
+
const supportedLanguages = VALIDATOR_LANGUAGE_CAPABILITIES[validatorId.value];
|
|
27
|
+
if (!supportedLanguages) return false;
|
|
28
|
+
return !supportedLanguages.some((language) => normalizedProjectLanguages.has(language));
|
|
29
|
+
})
|
|
30
|
+
.map((validatorId) => validatorId.value);
|
|
31
|
+
|
|
32
|
+
return new Set(unsupportedIds);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
splitDefinitions(
|
|
36
|
+
definitions: readonly ValidatorDefinition[],
|
|
37
|
+
projectLanguages: readonly string[],
|
|
38
|
+
): {
|
|
39
|
+
readonly executableDefinitions: readonly ValidatorDefinition[];
|
|
40
|
+
readonly unsupportedResults: readonly ValidationResult[];
|
|
41
|
+
readonly unsupportedValidatorIds: ReadonlySet<string>;
|
|
42
|
+
} {
|
|
43
|
+
const unsupportedValidatorIds = this.getUnsupportedValidatorIds(
|
|
44
|
+
definitions.map((definition) => definition.validatorId),
|
|
45
|
+
projectLanguages,
|
|
46
|
+
);
|
|
47
|
+
const executableDefinitions = definitions.filter(
|
|
48
|
+
(definition) => !unsupportedValidatorIds.has(definition.validatorId.value),
|
|
49
|
+
);
|
|
50
|
+
const unsupportedResults = definitions
|
|
51
|
+
.filter((definition) => unsupportedValidatorIds.has(definition.validatorId.value))
|
|
52
|
+
.map((definition) => ValidationResult.skipWithReason(
|
|
53
|
+
definition.validatorId,
|
|
54
|
+
this.createSkipReason(definition.validatorId, projectLanguages),
|
|
55
|
+
));
|
|
56
|
+
|
|
57
|
+
return { executableDefinitions, unsupportedResults, unsupportedValidatorIds };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private createSkipReason(validatorId: ValidatorId, projectLanguages: readonly string[]): string {
|
|
61
|
+
const supportedLanguages = VALIDATOR_LANGUAGE_CAPABILITIES[validatorId.value] ?? DEFAULT_SUPPORTED_LANGUAGES;
|
|
62
|
+
return `unsupported-language: ${validatorId.value} currently supports ${supportedLanguages.join(', ')}; project languages are ${projectLanguages.join(', ')}`;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -40,12 +40,14 @@ export class FileSystemSecurityPatternScannerAdapter implements SecurityPatternS
|
|
|
40
40
|
for (const filePath of targetPaths) {
|
|
41
41
|
try {
|
|
42
42
|
const content = await readFile(filePath, 'utf-8');
|
|
43
|
-
if (content.includes(ALLOWLIST_MARKER)) {
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
43
|
const lines = content.split('\n');
|
|
47
44
|
lines.forEach((line, idx) => {
|
|
48
|
-
|
|
45
|
+
// WI-120: allowlist は行/領域スコープ。以前はファイル内のどこかに
|
|
46
|
+
// マーカーが 1 つでもあればファイル全体をスキップしていたため、同一
|
|
47
|
+
// ファイル内の本物の秘密情報も検出漏れしていた。マーカーは当該行、
|
|
48
|
+
// または直前行(次行抑止コメント)にある場合のみ有効とする。
|
|
49
|
+
const previousLine = idx > 0 ? lines[idx - 1] : undefined;
|
|
50
|
+
if (isAllowlisted(line, previousLine)) {
|
|
49
51
|
return;
|
|
50
52
|
}
|
|
51
53
|
for (const { pattern, description, ruleId } of SECURITY_PATTERNS) {
|
|
@@ -71,9 +73,17 @@ export class FileSystemSecurityPatternScannerAdapter implements SecurityPatternS
|
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
|
|
74
|
-
|
|
76
|
+
/**
|
|
77
|
+
* 当該行が allowlist されているか判定する(行/領域スコープ)。
|
|
78
|
+
*
|
|
79
|
+
* - 当該行にマーカーがある → 許可
|
|
80
|
+
* - 直前行にマーカーがある(次行抑止コメント) → 許可
|
|
81
|
+
*
|
|
82
|
+
* ファイル全体を対象にしないことで、同一ファイル内の本物の秘密情報を検出できる。
|
|
83
|
+
*/
|
|
84
|
+
function isAllowlisted(line: string, previousLine: string | undefined): boolean {
|
|
75
85
|
if (line.includes(ALLOWLIST_MARKER)) return true;
|
|
76
|
-
return
|
|
86
|
+
return previousLine?.includes(ALLOWLIST_MARKER) ?? false;
|
|
77
87
|
}
|
|
78
88
|
|
|
79
89
|
function redactSecret(secretValue: string): string {
|
|
@@ -18,6 +18,13 @@ function normalizePath(value: string): string {
|
|
|
18
18
|
return value.replace(/\\/g, '/');
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
// Separator-agnostic check for whether a path points at a work-item
|
|
22
|
+
// description file. Exported so the Windows (`\`) normalization is unit
|
|
23
|
+
// testable without depending on the host filesystem separator.
|
|
24
|
+
export function isDescriptionFilePath(value: string): boolean {
|
|
25
|
+
return normalizePath(value).endsWith(`/${DESCRIPTION_FILE}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
21
28
|
async function listFiles(root: string): Promise<string[]> {
|
|
22
29
|
try {
|
|
23
30
|
const entries = await readdir(root, { withFileTypes: true });
|
|
@@ -66,7 +73,9 @@ export class FileSystemWorkItemReflectionAdapter implements WorkItemReflectionPo
|
|
|
66
73
|
const inceptionRoot = join(this.projectRoot, input.inceptionRoot);
|
|
67
74
|
const designRoot = join(this.projectRoot, input.designRoot);
|
|
68
75
|
const descriptionFiles = (await listFiles(inceptionRoot))
|
|
69
|
-
|
|
76
|
+
// Normalize separators first so Windows paths (which use `\`) still match
|
|
77
|
+
// the `/description.md` suffix.
|
|
78
|
+
.filter(isDescriptionFilePath);
|
|
70
79
|
|
|
71
80
|
if (descriptionFiles.length === 0) {
|
|
72
81
|
return {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* @layer infrastructure
|
|
3
3
|
* @unit validator-system
|
|
4
4
|
* @work-item-id WI-156
|
|
5
|
+
* @work-item-id WI-212
|
|
5
6
|
*
|
|
6
7
|
* HarnessConfigValidatorConfigAdapter — ValidatorConfigPort実装
|
|
7
8
|
* HarnessConfigV2からLayerConfig VOを構築する
|
|
@@ -18,7 +19,7 @@ export interface HarnessConfigLayers {
|
|
|
18
19
|
|
|
19
20
|
export interface HarnessConfigV2Like {
|
|
20
21
|
preset?: 'minimal' | 'standard' | 'strict';
|
|
21
|
-
project?: { preset?: 'minimal' | 'standard' | 'strict' };
|
|
22
|
+
project?: { preset?: 'minimal' | 'standard' | 'strict'; languages?: readonly string[] };
|
|
22
23
|
layers?: HarnessConfigLayers;
|
|
23
24
|
harnesses?: {
|
|
24
25
|
bundleSizeLimit?: number;
|
|
@@ -71,6 +72,11 @@ export class HarnessConfigValidatorConfigAdapter implements ValidatorConfigPort
|
|
|
71
72
|
});
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
async getProjectLanguages(): Promise<readonly string[]> {
|
|
76
|
+
const languages = this.config.project?.languages;
|
|
77
|
+
return languages && languages.length > 0 ? [...languages] : ['typescript'];
|
|
78
|
+
}
|
|
79
|
+
|
|
74
80
|
private normalizeValidatorId(idOrName: string): string {
|
|
75
81
|
try {
|
|
76
82
|
return ValidatorId.create(idOrName).value;
|
|
@@ -137,9 +137,7 @@ function extractExports(content: string): string[] {
|
|
|
137
137
|
function resolveImportTarget(fromFile: string, specifier: string, fileSet: ReadonlySet<string>): string | null {
|
|
138
138
|
if (!specifier.startsWith('.')) return null;
|
|
139
139
|
const base = resolve(dirname(fromFile), specifier);
|
|
140
|
-
const candidates =
|
|
141
|
-
? [base]
|
|
142
|
-
: [`${base}.ts`, `${base}.tsx`, join(base, 'index.ts'), join(base, 'index.tsx')];
|
|
140
|
+
const candidates = buildResolutionCandidates(base);
|
|
143
141
|
for (const candidate of candidates) {
|
|
144
142
|
const normalized = normalize(candidate);
|
|
145
143
|
if (fileSet.has(normalized)) return normalized;
|
|
@@ -147,6 +145,40 @@ function resolveImportTarget(fromFile: string, specifier: string, fileSet: Reado
|
|
|
147
145
|
return null;
|
|
148
146
|
}
|
|
149
147
|
|
|
148
|
+
/**
|
|
149
|
+
* import 指定子から実ファイル候補を生成する。
|
|
150
|
+
*
|
|
151
|
+
* このプロジェクトは ESM 規約に従い `./foo.js` と書いて実体 `foo.ts` を参照する
|
|
152
|
+
* (TypeScript の `.js` 拡張子付き import)。素朴に extname を見ると `.js` 実体を
|
|
153
|
+
* 探しに行き解決に失敗するため、`.js`/`.jsx`/`.mjs`/`.cjs` 拡張子は対応する
|
|
154
|
+
* TypeScript 拡張子(およびディレクトリの index)へマッピングした候補も加える。
|
|
155
|
+
*/
|
|
156
|
+
function buildResolutionCandidates(base: string): string[] {
|
|
157
|
+
const ext = extname(base);
|
|
158
|
+
const JS_TO_TS: Record<string, readonly string[]> = {
|
|
159
|
+
'.js': ['.ts', '.tsx'],
|
|
160
|
+
'.jsx': ['.tsx', '.ts'],
|
|
161
|
+
'.mjs': ['.mts', '.ts'],
|
|
162
|
+
'.cjs': ['.cts', '.ts'],
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
if (ext) {
|
|
166
|
+
const mapped = JS_TO_TS[ext];
|
|
167
|
+
if (mapped) {
|
|
168
|
+
const withoutExt = base.slice(0, base.length - ext.length);
|
|
169
|
+
// 例: `./foo.js` → `foo.ts` / `foo.tsx`、`./dir.js`(ディレクトリ)→ `dir/index.ts`
|
|
170
|
+
return [
|
|
171
|
+
...mapped.map((tsExt) => `${withoutExt}${tsExt}`),
|
|
172
|
+
base,
|
|
173
|
+
...mapped.map((tsExt) => join(withoutExt, `index${tsExt}`)),
|
|
174
|
+
];
|
|
175
|
+
}
|
|
176
|
+
return [base];
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return [`${base}.ts`, `${base}.tsx`, join(base, 'index.ts'), join(base, 'index.tsx')];
|
|
180
|
+
}
|
|
181
|
+
|
|
150
182
|
function classifyDeadCodeExclusion(filePath: string): string | undefined {
|
|
151
183
|
const normalized = filePath.replaceAll('\\', '/');
|
|
152
184
|
if (/(^|\/)__tests__\//.test(normalized) || /\.test\.ts$/.test(normalized) || /\.it\.test\.ts$/.test(normalized)) {
|
|
@@ -3,39 +3,111 @@
|
|
|
3
3
|
* @unit validator-system
|
|
4
4
|
*
|
|
5
5
|
* NyquistAcCoveragePolicyAdapter — AcCoveragePolicyPort実装
|
|
6
|
+
*
|
|
7
|
+
* FAIL-CLOSED: nyquist-validation モジュールの実行時に例外が発生した場合は
|
|
8
|
+
* passed=false(不合格)として扱い、エラーを結果に含めて上位へ通知する。
|
|
9
|
+
* かつては catch 節で passed=true を返す fail-open だったが、AC 網羅ゲート
|
|
10
|
+
* (L3-004)を無効化する重大な品質防御の抜け穴だったため fail-closed へ是正した。
|
|
11
|
+
*
|
|
12
|
+
* REAL REGISTRY: storyId 整合性検査に必要な有効 storyId 一覧を
|
|
13
|
+
* traceability-model の StoryCatalog から取得して nyquist モジュールへ配線する。
|
|
14
|
+
* かつては `getStoryIds: async () => []`(空スタブ)だったため、正当かつ完全網羅の
|
|
15
|
+
* マトリクスでも全 storyId が「未登録」と判定され L3-004 が構造的に PASS 不能だった。
|
|
16
|
+
*
|
|
17
|
+
* MATRIX PATH: requirement-test-matrix.json のパスは config
|
|
18
|
+
* (layers.L3.requirementMatrixPath)から供給される。未指定時は既定値
|
|
19
|
+
* `.harness/requirement-test-matrix.json` を用いる。マトリクスが不在の場合は
|
|
20
|
+
* fail-closed で「設定されているが不在」という実行可能なメッセージを返す。
|
|
6
21
|
*/
|
|
22
|
+
import { access } from 'node:fs/promises';
|
|
23
|
+
import { isAbsolute, join } from 'node:path';
|
|
7
24
|
import type { AcCoveragePolicyPort } from '../../domain/ports/ac-coverage-policy-port.js';
|
|
8
25
|
import type { HarnessErrorLike } from '../../domain/value-objects/validation-result.js';
|
|
9
26
|
|
|
27
|
+
const DEFAULT_MATRIX_PATH = '.harness/requirement-test-matrix.json';
|
|
28
|
+
|
|
29
|
+
function toL3004Error(message: string, suggestion: string): HarnessErrorLike {
|
|
30
|
+
return {
|
|
31
|
+
code: { value: 'L3-004', toString: () => 'L3-004' },
|
|
32
|
+
severity: { value: 'error', toString: () => 'error' },
|
|
33
|
+
message,
|
|
34
|
+
suggestion,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
10
38
|
export class NyquistAcCoveragePolicyAdapter implements AcCoveragePolicyPort {
|
|
11
39
|
async checkCoverage(context: { matrixFilePath?: string }): Promise<{
|
|
12
40
|
passed: boolean;
|
|
13
41
|
errors: readonly HarnessErrorLike[];
|
|
14
42
|
}> {
|
|
43
|
+
const rootDir = process.cwd();
|
|
44
|
+
const relativeOrAbsolute =
|
|
45
|
+
context.matrixFilePath && context.matrixFilePath.length > 0
|
|
46
|
+
? context.matrixFilePath
|
|
47
|
+
: DEFAULT_MATRIX_PATH;
|
|
48
|
+
const matrixFilePath = isAbsolute(relativeOrAbsolute)
|
|
49
|
+
? relativeOrAbsolute
|
|
50
|
+
: join(rootDir, relativeOrAbsolute);
|
|
51
|
+
|
|
52
|
+
// マトリクス不在は threshold-configured-but-absent と同様に fail-closed で
|
|
53
|
+
// 明確なメッセージを返す(ENOENT の生メッセージで落とさない)。
|
|
54
|
+
try {
|
|
55
|
+
await access(matrixFilePath);
|
|
56
|
+
} catch {
|
|
57
|
+
return {
|
|
58
|
+
passed: false,
|
|
59
|
+
errors: [
|
|
60
|
+
toL3004Error(
|
|
61
|
+
`AC網羅マトリクスが見つかりません: ${relativeOrAbsolute}(L3-004 は fail-closed)`,
|
|
62
|
+
`${relativeOrAbsolute} を生成してください(phasegate:generate-matrix)。パスは config の layers.L3.requirementMatrixPath で変更できます`,
|
|
63
|
+
),
|
|
64
|
+
],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
15
68
|
try {
|
|
69
|
+
// 有効 storyId 一覧を traceability-model の StoryCatalog から取得する(REAL registry)
|
|
70
|
+
const storyIds = await this.loadValidStoryIds(rootDir);
|
|
71
|
+
|
|
16
72
|
const { createNyquistValidationModule } = await import('../../../nyquist-validation/composition-root.js');
|
|
17
73
|
const mod = createNyquistValidationModule({
|
|
18
|
-
|
|
19
|
-
getStoryIds: async () => [],
|
|
20
|
-
} as {
|
|
21
|
-
rootDir: string;
|
|
22
|
-
getStoryIds: () => Promise<readonly string[]>;
|
|
74
|
+
getStoryIds: async () => storyIds,
|
|
23
75
|
});
|
|
24
76
|
const output = await mod.checkAcCoverageGateUseCase.execute({
|
|
25
|
-
matrixFilePath
|
|
77
|
+
matrixFilePath,
|
|
26
78
|
});
|
|
27
79
|
|
|
28
80
|
return {
|
|
29
81
|
passed: output.passed,
|
|
30
|
-
errors: output.errors.map((err) =>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
82
|
+
errors: output.errors.map((err) =>
|
|
83
|
+
toL3004Error(err.message, ''),
|
|
84
|
+
),
|
|
85
|
+
};
|
|
86
|
+
} catch (error) {
|
|
87
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
88
|
+
// FAIL-CLOSED: 例外を握り潰して合格扱いにせず、不合格として通知する
|
|
89
|
+
return {
|
|
90
|
+
passed: false,
|
|
91
|
+
errors: [
|
|
92
|
+
toL3004Error(
|
|
93
|
+
`AC網羅ゲートの検査に失敗しました(fail-closed): ${message}`,
|
|
94
|
+
'requirement-test-matrix.json の存在・形式・スキーマ整合性を確認してください',
|
|
95
|
+
),
|
|
96
|
+
],
|
|
36
97
|
};
|
|
37
|
-
} catch {
|
|
38
|
-
return { passed: true, errors: [] };
|
|
39
98
|
}
|
|
40
99
|
}
|
|
100
|
+
|
|
101
|
+
private async loadValidStoryIds(rootDir: string): Promise<readonly string[]> {
|
|
102
|
+
const { createConfigFoundationModule } = await import('../../../config-foundation/composition-root.js');
|
|
103
|
+
const configModule = createConfigFoundationModule();
|
|
104
|
+
const resolvedConfig = await configModule.usecases.loadResolvedConfigUseCase.execute();
|
|
105
|
+
|
|
106
|
+
const { createTraceabilityModelModule } = await import('../../../traceability-model/composition-root.js');
|
|
107
|
+
const traceModule = createTraceabilityModelModule(rootDir, {
|
|
108
|
+
pathRoots: { designDocsRoot: resolvedConfig.config.paths.designDocs },
|
|
109
|
+
});
|
|
110
|
+
const storyIds = await traceModule.storyCatalog.getAllStoryIds();
|
|
111
|
+
return storyIds.map((s) => s.value);
|
|
112
|
+
}
|
|
41
113
|
}
|
|
@@ -51,9 +51,35 @@ export class PhaseDependencyPhaseGatePolicyAdapter implements PhaseGatePolicyPor
|
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return {
|
|
54
|
+
// Fail-closed: an unexpected exit code means prerequisites could not be confirmed.
|
|
55
|
+
console.error(`[validator-system] phase gate check returned unexpected exit code ${result.exitCode}`);
|
|
56
|
+
return {
|
|
57
|
+
satisfied: false,
|
|
58
|
+
violations: [
|
|
59
|
+
{
|
|
60
|
+
code: { value: 'L2-002', toString: () => 'L2-002' },
|
|
61
|
+
severity: { value: 'error', toString: () => 'error' },
|
|
62
|
+
message: `phase gate check returned unexpected exit code ${result.exitCode}; prerequisites cannot be confirmed`,
|
|
63
|
+
suggestion: 'phase gate prerequisites could not be evaluated; treating as NOT satisfied (fail-closed)',
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
};
|
|
67
|
+
} catch (err) {
|
|
68
|
+
// Fail-closed: if the phase-dependency-model could not be loaded/executed we cannot
|
|
69
|
+
// confirm prerequisites are met, so treat them as NOT satisfied rather than opening the gate.
|
|
70
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
71
|
+
console.error(`[validator-system] phase gate prerequisite check failed to run: ${message}`);
|
|
72
|
+
return {
|
|
73
|
+
satisfied: false,
|
|
74
|
+
violations: [
|
|
75
|
+
{
|
|
76
|
+
code: { value: 'L2-002', toString: () => 'L2-002' },
|
|
77
|
+
severity: { value: 'error', toString: () => 'error' },
|
|
78
|
+
message: `phase gate prerequisite check could not be evaluated: ${message}`,
|
|
79
|
+
suggestion: 'phase gate prerequisites could not be evaluated; treating as NOT satisfied (fail-closed)',
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
};
|
|
57
83
|
}
|
|
58
84
|
}
|
|
59
85
|
}
|
|
@@ -3,6 +3,7 @@ name: codebase-mapper
|
|
|
3
3
|
description: コードベースの構造マップを生成するスキル。全ソースファイルの `@unit`/`@layer` アノテーションを解析し、Unit・レイヤー分布・Unit間依存関係・循環依存を可視化したマップ文書を出力する。L4バリデータ(drift-detection/dead-code)の入力精度向上にも使用。使用タイミング: 「コードベースの構造を把握したい」「Unitマップを作って」「どのファイルがどのUnitに属するか調べて」「依存関係を可視化して」など。
|
|
4
4
|
model: sonnet
|
|
5
5
|
review: opus
|
|
6
|
+
languages: [typescript]
|
|
6
7
|
---
|
|
7
8
|
|
|
8
9
|
# Codebase Mapper
|