phasegate 0.133.0 → 0.135.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 +20 -0
- package/README.ja.md +1 -4
- package/README.md +1 -5
- package/bin/phasegate +2 -0
- package/docs/contracts/lesson-artifact.schema.json +62 -0
- package/docs/contracts/requirement-test-matrix.schema.json +62 -0
- package/docs/guide/configuration.md +0 -1
- package/docs/guide/layer-model.md +9 -9
- package/docs/principles/architecture-philosophy.md +26 -55
- package/docs/principles/model-routing.md +30 -165
- package/docs/principles/testing-rules.md +66 -648
- package/package.json +4 -3
- package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +2 -2
- 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/schemas/harness-config-v2.schema.json +0 -16
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +0 -16
- package/scripts/harness/harness-error/infrastructure/adapters/validator-registry-bridge-adapter.ts +2 -0
- package/scripts/harness/harness-error/infrastructure/registry/l4-error-definitions.ts +14 -0
- package/scripts/harness/main.ts +3 -3
- package/scripts/harness/phase2-extensions/composition-root.ts +7 -1
- package/scripts/harness/phase2-extensions/infrastructure/adapters/file-system-document-scanner-adapter.ts +16 -2
- package/scripts/harness/phase2-extensions/infrastructure/adapters/harness-config-freshness-adapter.ts +13 -2
- package/scripts/harness/phase2-extensions/infrastructure/adapters/regex-pointer-extractor-adapter.ts +35 -4
- package/scripts/harness/quick-mode/infrastructure/adapters/validator-system-validator-id-registry-adapter.ts +1 -1
- package/scripts/harness/skill-quality/infrastructure/adapters/validator-id-registry-bridge-adapter.ts +1 -1
- package/scripts/harness/validator-system/application/dto/run-l4-validators-input.ts +1 -0
- package/scripts/harness/validator-system/application/use-cases/run-full-validation-usecase.ts +1 -0
- package/scripts/harness/validator-system/application/use-cases/run-l4-validators-usecase.ts +133 -3
- package/scripts/harness/validator-system/composition-root.ts +9 -16
- package/scripts/harness/validator-system/domain/ports/validator-config-port.ts +1 -1
- package/scripts/harness/validator-system/domain/services/validator-execution-service.ts +2 -2
- package/scripts/harness/validator-system/domain/services/validator-registry.ts +2 -2
- package/scripts/harness/validator-system/domain/value-objects/layer-config.ts +2 -2
- package/scripts/harness/validator-system/domain/value-objects/validator-definition.ts +5 -6
- package/scripts/harness/validator-system/domain/value-objects/validator-id.ts +11 -6
- package/scripts/harness/validator-system/infrastructure/adapters/biome-ast-source-code-analyzer-adapter.ts +29 -10
- package/scripts/harness/validator-system/infrastructure/adapters/harness-config-validator-config-adapter.ts +12 -5
- package/scripts/harness/validator-system/presentation/handlers/run-validators-handler.ts +6 -35
- package/scripts/harness/validator-system/application/dto/run-l0-validators-input.ts +0 -9
- package/scripts/harness/validator-system/application/use-cases/run-l0-validators-usecase.ts +0 -60
|
@@ -10,7 +10,6 @@ import { ValidationRule } from './domain/value-objects/validation-rule.js';
|
|
|
10
10
|
import { ValidatorRegistry } from './domain/services/validator-registry.js';
|
|
11
11
|
import { ValidatorExecutionService } from './domain/services/validator-execution-service.js';
|
|
12
12
|
import { ValidationResultContractMapper } from './application/mappers/validation-result-contract-mapper.js';
|
|
13
|
-
import { RunL0ValidatorsUseCase } from './application/use-cases/run-l0-validators-usecase.js';
|
|
14
13
|
import { RunL2ValidatorsUseCase } from './application/use-cases/run-l2-validators-usecase.js';
|
|
15
14
|
import { RunL3ValidatorsUseCase } from './application/use-cases/run-l3-validators-usecase.js';
|
|
16
15
|
import { RunL4ValidatorsUseCase } from './application/use-cases/run-l4-validators-usecase.js';
|
|
@@ -36,6 +35,7 @@ import { ImportGraphSourceAnalysisAdapter } from './infrastructure/adapters/impo
|
|
|
36
35
|
import { DriftDetectionService } from './domain/services/l4/drift-detection-service.js';
|
|
37
36
|
import { ConsistencyCheckService } from './domain/services/l4/consistency-check-service.js';
|
|
38
37
|
import { DeadCodeDetectionService } from './domain/services/l4/dead-code-detection-service.js';
|
|
38
|
+
import { buildPhase2Extensions } from '../phase2-extensions/composition-root.js';
|
|
39
39
|
import { RunValidatorsHandler } from './presentation/handlers/run-validators-handler.js';
|
|
40
40
|
import { RunQuickModeHandler } from './presentation/handlers/run-quick-mode-handler.js';
|
|
41
41
|
import { ReportValidationResultsHandler } from './presentation/handlers/report-validation-results-handler.js';
|
|
@@ -45,15 +45,14 @@ import { join } from 'node:path';
|
|
|
45
45
|
const DEFAULT_CONFIG = {
|
|
46
46
|
preset: 'standard' as const,
|
|
47
47
|
layers: {
|
|
48
|
-
L0: { enabled: true, validators: ['L0-001', 'L0-002'] },
|
|
49
48
|
L2: { enabled: true, validators: ['L2-001', 'L2-002', 'L2-003'] },
|
|
50
49
|
L3: { enabled: true, validators: ['L3-001', 'L3-002', 'L3-003', 'L3-004'], coverageThreshold: 90, bundleSizeLimit: 512000 },
|
|
51
|
-
L4: { enabled: true, validators: ['L4-001', 'L4-002', 'L4-003'] },
|
|
50
|
+
L4: { enabled: true, validators: ['L4-001', 'L4-002', 'L4-003', 'L4-004', 'L4-005'] },
|
|
52
51
|
},
|
|
53
52
|
validate: { failOnWarning: false },
|
|
54
53
|
};
|
|
55
54
|
|
|
56
|
-
/**
|
|
55
|
+
/** バリデータ定義カタログ */
|
|
57
56
|
function buildDefaultRegistry(): ValidatorRegistry {
|
|
58
57
|
const defaultRule = ValidationRule.create({
|
|
59
58
|
ruleName: 'default-rule',
|
|
@@ -61,7 +60,7 @@ function buildDefaultRegistry(): ValidatorRegistry {
|
|
|
61
60
|
fixExample: null,
|
|
62
61
|
});
|
|
63
62
|
|
|
64
|
-
const createDef = (id: string, layer: '
|
|
63
|
+
const createDef = (id: string, layer: 'L2' | 'L3' | 'L4', enabledCondition: 'always' | 'strictOnly' = 'always', externalPolicyRef: string | null = null) =>
|
|
65
64
|
ValidatorDefinition.create({
|
|
66
65
|
validatorId: ValidatorId.create(id),
|
|
67
66
|
layer,
|
|
@@ -75,7 +74,6 @@ function buildDefaultRegistry(): ValidatorRegistry {
|
|
|
75
74
|
});
|
|
76
75
|
|
|
77
76
|
const definitions = [
|
|
78
|
-
createDef('L0-001', 'L0', 'always'),
|
|
79
77
|
createDef('L2-001', 'L2', 'always', 'PhaseGatePolicyPort'),
|
|
80
78
|
createDef('L2-002', 'L2', 'always', 'MetadataPolicyPort'),
|
|
81
79
|
createDef('L2-003', 'L2', 'always'),
|
|
@@ -86,6 +84,8 @@ function buildDefaultRegistry(): ValidatorRegistry {
|
|
|
86
84
|
createDef('L4-001', 'L4', 'always'),
|
|
87
85
|
createDef('L4-002', 'L4', 'always'),
|
|
88
86
|
createDef('L4-003', 'L4', 'strictOnly'),
|
|
87
|
+
createDef('L4-004', 'L4', 'always'),
|
|
88
|
+
createDef('L4-005', 'L4', 'always'),
|
|
89
89
|
];
|
|
90
90
|
|
|
91
91
|
return new ValidatorRegistry(definitions);
|
|
@@ -93,7 +93,6 @@ function buildDefaultRegistry(): ValidatorRegistry {
|
|
|
93
93
|
|
|
94
94
|
export interface ValidatorSystemModule {
|
|
95
95
|
registry: ValidatorRegistry;
|
|
96
|
-
runL0ValidatorsUseCase: RunL0ValidatorsUseCase;
|
|
97
96
|
runL1ValidatorsUseCase: RunL1ValidatorsUseCase;
|
|
98
97
|
runL2ValidatorsUseCase: RunL2ValidatorsUseCase;
|
|
99
98
|
runL3ValidatorsUseCase: RunL3ValidatorsUseCase;
|
|
@@ -124,13 +123,6 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
|
|
|
124
123
|
const securityScannerPort = new FileSystemSecurityPatternScannerAdapter();
|
|
125
124
|
const performanceScannerPort = new AstPerformanceScannerAdapter();
|
|
126
125
|
|
|
127
|
-
const runL0ValidatorsUseCase = new RunL0ValidatorsUseCase({
|
|
128
|
-
validatorRegistry: registry,
|
|
129
|
-
validatorExecutionService: executionService,
|
|
130
|
-
validatorConfigPort: configPort,
|
|
131
|
-
contractMapper,
|
|
132
|
-
});
|
|
133
|
-
|
|
134
126
|
const runL2ValidatorsUseCase = new RunL2ValidatorsUseCase({
|
|
135
127
|
validatorRegistry: registry,
|
|
136
128
|
validatorExecutionService: executionService,
|
|
@@ -168,6 +160,7 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
|
|
|
168
160
|
const deadCodeDetectionService = new DeadCodeDetectionService({
|
|
169
161
|
sourceAnalysisPort,
|
|
170
162
|
});
|
|
163
|
+
const phase2Extensions = buildPhase2Extensions(process.cwd(), configData as never);
|
|
171
164
|
|
|
172
165
|
const runL4ValidatorsUseCase = new RunL4ValidatorsUseCase({
|
|
173
166
|
validatorRegistry: registry,
|
|
@@ -177,6 +170,8 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
|
|
|
177
170
|
driftDetectionService,
|
|
178
171
|
consistencyCheckService,
|
|
179
172
|
deadCodeDetectionService,
|
|
173
|
+
checkDocFreshnessUseCase: phase2Extensions.checkDocFreshnessUseCase,
|
|
174
|
+
validateDocPointersUseCase: phase2Extensions.validateDocPointersUseCase,
|
|
180
175
|
});
|
|
181
176
|
|
|
182
177
|
const runQuickModeUseCase = new RunQuickModeUseCase({
|
|
@@ -220,7 +215,6 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
|
|
|
220
215
|
const handlers = {
|
|
221
216
|
runValidators: new RunValidatorsHandler({
|
|
222
217
|
runFullValidationUseCase,
|
|
223
|
-
runL0ValidatorsUseCase,
|
|
224
218
|
runL1ValidatorsUseCase,
|
|
225
219
|
defaultFailOnWarning,
|
|
226
220
|
}),
|
|
@@ -230,7 +224,6 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
|
|
|
230
224
|
|
|
231
225
|
return {
|
|
232
226
|
registry,
|
|
233
|
-
runL0ValidatorsUseCase,
|
|
234
227
|
runL1ValidatorsUseCase,
|
|
235
228
|
runL2ValidatorsUseCase,
|
|
236
229
|
runL3ValidatorsUseCase,
|
|
@@ -19,7 +19,7 @@ export class ValidatorExecutionError extends Error {
|
|
|
19
19
|
|
|
20
20
|
export interface ValidatorExecutionServiceDeps {
|
|
21
21
|
configPort?: {
|
|
22
|
-
getLayerConfig(layer: '
|
|
22
|
+
getLayerConfig(layer: 'L2' | 'L3' | 'L4'): LayerConfig | Promise<LayerConfig>;
|
|
23
23
|
};
|
|
24
24
|
policyPort?: PhaseGatePolicyPort | { check: (...args: unknown[]) => unknown };
|
|
25
25
|
[key: string]: unknown;
|
|
@@ -132,7 +132,7 @@ export class ValidatorExecutionService {
|
|
|
132
132
|
});
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
private _getLayerConfigSync(layer: '
|
|
135
|
+
private _getLayerConfigSync(layer: 'L2' | 'L3' | 'L4'): LayerConfig | undefined {
|
|
136
136
|
const port = this.deps.configPort;
|
|
137
137
|
if (!port) return undefined;
|
|
138
138
|
const result = port.getLayerConfig(layer);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @unit validator-system
|
|
4
4
|
*
|
|
5
5
|
* ValidatorRegistry ドメインサービス
|
|
6
|
-
*
|
|
6
|
+
* L2-L4バリデータ定義のカタログ管理と選択実行インターフェース
|
|
7
7
|
*/
|
|
8
8
|
import { ValidatorId } from '../value-objects/validator-id.js';
|
|
9
9
|
import { ValidatorDefinition } from '../value-objects/validator-definition.js';
|
|
@@ -48,7 +48,7 @@ export class ValidatorRegistry {
|
|
|
48
48
|
return this.definitionList;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
listByLayer(layer: '
|
|
51
|
+
listByLayer(layer: 'L2' | 'L3' | 'L4'): readonly ValidatorDefinition[] {
|
|
52
52
|
return this.definitionList.filter((def) => def.layer === layer);
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { ValidatorId } from './validator-id.js';
|
|
9
9
|
|
|
10
10
|
export interface LayerConfigProps {
|
|
11
|
-
readonly layer: '
|
|
11
|
+
readonly layer: 'L2' | 'L3' | 'L4';
|
|
12
12
|
readonly enabled: boolean;
|
|
13
13
|
readonly validatorIds: readonly string[];
|
|
14
14
|
readonly thresholds: Record<string, number>;
|
|
@@ -17,7 +17,7 @@ export interface LayerConfigProps {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export class LayerConfig {
|
|
20
|
-
readonly layer: '
|
|
20
|
+
readonly layer: 'L2' | 'L3' | 'L4';
|
|
21
21
|
readonly enabled: boolean;
|
|
22
22
|
readonly validatorIds: readonly string[];
|
|
23
23
|
readonly thresholds: Readonly<Record<string, number>>;
|
|
@@ -10,17 +10,16 @@ import { ValidationRule } from './validation-rule.js';
|
|
|
10
10
|
|
|
11
11
|
export interface ValidatorDefinitionProps {
|
|
12
12
|
readonly validatorId: ValidatorId;
|
|
13
|
-
readonly layer: '
|
|
13
|
+
readonly layer: 'L2' | 'L3' | 'L4';
|
|
14
14
|
readonly name?: string;
|
|
15
15
|
readonly description?: string;
|
|
16
16
|
readonly rules: readonly ValidationRule[];
|
|
17
17
|
readonly enabledCondition: 'always' | 'layerEnabled' | 'strictOnly';
|
|
18
18
|
readonly externalPolicyRef: string | null;
|
|
19
|
-
readonly executionPhase?: '
|
|
19
|
+
readonly executionPhase?: 'pre-commit' | 'ci' | 'scheduled';
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
const LAYER_TO_PHASE: Record<'
|
|
23
|
-
L0: 'fuse-hook',
|
|
22
|
+
const LAYER_TO_PHASE: Record<'L2' | 'L3' | 'L4', 'pre-commit' | 'ci' | 'scheduled'> = {
|
|
24
23
|
L2: 'pre-commit',
|
|
25
24
|
L3: 'ci',
|
|
26
25
|
L4: 'scheduled',
|
|
@@ -28,13 +27,13 @@ const LAYER_TO_PHASE: Record<'L0' | 'L2' | 'L3' | 'L4', 'fuse-hook' | 'fuse-hook
|
|
|
28
27
|
|
|
29
28
|
export class ValidatorDefinition {
|
|
30
29
|
readonly validatorId: ValidatorId;
|
|
31
|
-
readonly layer: '
|
|
30
|
+
readonly layer: 'L2' | 'L3' | 'L4';
|
|
32
31
|
readonly name: string;
|
|
33
32
|
readonly description: string;
|
|
34
33
|
readonly rules: readonly ValidationRule[];
|
|
35
34
|
readonly enabledCondition: 'always' | 'layerEnabled' | 'strictOnly';
|
|
36
35
|
readonly externalPolicyRef: string | null;
|
|
37
|
-
readonly executionPhase: '
|
|
36
|
+
readonly executionPhase: 'pre-commit' | 'ci' | 'scheduled';
|
|
38
37
|
|
|
39
38
|
private constructor(props: ValidatorDefinitionProps) {
|
|
40
39
|
this.validatorId = props.validatorId;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @unit validator-system
|
|
4
4
|
*
|
|
5
5
|
* ValidatorId 値オブジェクト
|
|
6
|
-
* L1-001〜L4-
|
|
6
|
+
* L1-001〜L4-005 のバリデータを識別する不変値オブジェクト
|
|
7
7
|
* Wave 2A で L1-017, L1-018, L2-013 を追加
|
|
8
8
|
*/
|
|
9
9
|
|
|
@@ -20,8 +20,6 @@ const VALIDATOR_ID_PATTERN = /^L[0-4]-\d{3}$/;
|
|
|
20
20
|
|
|
21
21
|
/** バリデータID -> バリデータ名のマップ */
|
|
22
22
|
const VALIDATOR_NAME_MAP: Record<string, string> = {
|
|
23
|
-
'L0-001': 'fuse-hook-config',
|
|
24
|
-
'L0-002': 'fuse-mount-status',
|
|
25
23
|
'L1-017': 'it-test-mock-detection',
|
|
26
24
|
'L1-018': 'stub-comment-detection',
|
|
27
25
|
'L2-001': 'phase-gate',
|
|
@@ -35,12 +33,19 @@ const VALIDATOR_NAME_MAP: Record<string, string> = {
|
|
|
35
33
|
'L4-001': 'drift-detect',
|
|
36
34
|
'L4-002': 'consistency-check',
|
|
37
35
|
'L4-003': 'dead-code',
|
|
36
|
+
'L4-004': 'doc-freshness',
|
|
37
|
+
'L4-005': 'pointer-validation',
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
/** バリデータ名 -> バリデータID の逆引きマップ */
|
|
41
|
-
const NAME_TO_ID_MAP: Record<string, string> =
|
|
42
|
-
Object.entries(VALIDATOR_NAME_MAP).map(([id, name]) => [name, id])
|
|
43
|
-
|
|
41
|
+
const NAME_TO_ID_MAP: Record<string, string> = {
|
|
42
|
+
...Object.fromEntries(Object.entries(VALIDATOR_NAME_MAP).map(([id, name]) => [name, id])),
|
|
43
|
+
'drift-detector': 'L4-001',
|
|
44
|
+
'consistency-checker': 'L4-002',
|
|
45
|
+
'dead-code-detector': 'L4-003',
|
|
46
|
+
'doc-freshness-checker': 'L4-004',
|
|
47
|
+
'pointer-validator': 'L4-005',
|
|
48
|
+
};
|
|
44
49
|
|
|
45
50
|
/** 有効なValidatorID集合 */
|
|
46
51
|
const VALID_IDS = new Set(Object.keys(VALIDATOR_NAME_MAP));
|
|
@@ -12,9 +12,23 @@ import { basename, join, relative, sep } from 'node:path';
|
|
|
12
12
|
|
|
13
13
|
const HARNESS_ROOT = join(process.cwd(), 'scripts', 'harness');
|
|
14
14
|
|
|
15
|
+
export interface BiomeAstSourceCodeAnalyzerAdapterOptions {
|
|
16
|
+
readonly sourceRoot?: string;
|
|
17
|
+
/** スキャン対象から除外するパスパターン(デフォルト: テストディレクトリを除外) */
|
|
18
|
+
readonly excludePattern?: RegExp;
|
|
19
|
+
}
|
|
20
|
+
|
|
15
21
|
export class BiomeAstSourceCodeAnalyzerAdapter implements SourceCodeAnalyzerPort {
|
|
22
|
+
private readonly sourceRoot: string;
|
|
23
|
+
private readonly excludePattern: RegExp;
|
|
24
|
+
|
|
25
|
+
constructor(options: BiomeAstSourceCodeAnalyzerAdapterOptions = {}) {
|
|
26
|
+
this.sourceRoot = options.sourceRoot ?? HARNESS_ROOT;
|
|
27
|
+
this.excludePattern = options.excludePattern ?? /__tests__\//;
|
|
28
|
+
}
|
|
29
|
+
|
|
16
30
|
async analyzeExports(targetUnits?: readonly string[]): Promise<readonly SourceAnalysisResult[]> {
|
|
17
|
-
const targetFiles = await collectTargetFiles(targetUnits);
|
|
31
|
+
const targetFiles = await collectTargetFiles(this.sourceRoot, this.excludePattern, targetUnits);
|
|
18
32
|
if (targetFiles.length === 0) return [];
|
|
19
33
|
|
|
20
34
|
const program = ts.createProgram(targetFiles, {
|
|
@@ -30,7 +44,7 @@ export class BiomeAstSourceCodeAnalyzerAdapter implements SourceCodeAnalyzerPort
|
|
|
30
44
|
const sourceFile = program.getSourceFile(filePath);
|
|
31
45
|
if (!sourceFile) continue;
|
|
32
46
|
results.push({
|
|
33
|
-
unitName: resolveUnitName(filePath),
|
|
47
|
+
unitName: resolveUnitName(this.sourceRoot, filePath),
|
|
34
48
|
filePath,
|
|
35
49
|
exports: extractExports(sourceFile),
|
|
36
50
|
imports: extractImports(sourceFile),
|
|
@@ -134,26 +148,31 @@ function extractImports(sourceFile: ts.SourceFile): SourceAnalysisResult['import
|
|
|
134
148
|
return imports;
|
|
135
149
|
}
|
|
136
150
|
|
|
137
|
-
async function collectTargetFiles(
|
|
151
|
+
async function collectTargetFiles(
|
|
152
|
+
sourceRoot: string,
|
|
153
|
+
excludePattern: RegExp,
|
|
154
|
+
targetUnits?: readonly string[],
|
|
155
|
+
): Promise<readonly string[]> {
|
|
138
156
|
const roots = targetUnits && targetUnits.length > 0
|
|
139
|
-
? targetUnits.map((unit) => join(
|
|
140
|
-
: [
|
|
157
|
+
? targetUnits.map((unit) => join(sourceRoot, unit))
|
|
158
|
+
: [sourceRoot];
|
|
141
159
|
|
|
142
160
|
const files: string[] = [];
|
|
143
161
|
for (const root of roots) {
|
|
144
|
-
files.push(...await walkTsFiles(root));
|
|
162
|
+
files.push(...await walkTsFiles(root, excludePattern));
|
|
145
163
|
}
|
|
146
164
|
|
|
147
165
|
return files;
|
|
148
166
|
}
|
|
149
167
|
|
|
150
|
-
async function walkTsFiles(root: string): Promise<string[]> {
|
|
168
|
+
async function walkTsFiles(root: string, excludePattern: RegExp): Promise<string[]> {
|
|
151
169
|
try {
|
|
152
170
|
const entries = await readdir(root, { withFileTypes: true });
|
|
153
171
|
const files = await Promise.all(entries.map(async (entry) => {
|
|
154
172
|
const fullPath = join(root, entry.name);
|
|
173
|
+
if (excludePattern.test(fullPath)) return [];
|
|
155
174
|
if (entry.isDirectory()) {
|
|
156
|
-
return walkTsFiles(fullPath);
|
|
175
|
+
return walkTsFiles(fullPath, excludePattern);
|
|
157
176
|
}
|
|
158
177
|
return fullPath.endsWith('.ts') ? [fullPath] : [];
|
|
159
178
|
}));
|
|
@@ -163,8 +182,8 @@ async function walkTsFiles(root: string): Promise<string[]> {
|
|
|
163
182
|
}
|
|
164
183
|
}
|
|
165
184
|
|
|
166
|
-
function resolveUnitName(filePath: string): string {
|
|
167
|
-
const relativePath = relative(
|
|
185
|
+
function resolveUnitName(sourceRoot: string, filePath: string): string {
|
|
186
|
+
const relativePath = relative(sourceRoot, filePath);
|
|
168
187
|
const [firstSegment] = relativePath.split(sep);
|
|
169
188
|
return firstSegment || basename(filePath);
|
|
170
189
|
}
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* HarnessConfigV2からLayerConfig VOを構築する
|
|
7
7
|
*/
|
|
8
8
|
import { LayerConfig } from '../../domain/value-objects/layer-config.js';
|
|
9
|
+
import { ValidatorId } from '../../domain/value-objects/validator-id.js';
|
|
9
10
|
import type { ValidatorConfigPort } from '../../domain/ports/validator-config-port.js';
|
|
10
11
|
|
|
11
12
|
export interface HarnessConfigLayers {
|
|
12
|
-
L0?: { enabled?: boolean; validators?: string[]; strictOnly?: boolean; preset?: string };
|
|
13
13
|
L2?: { enabled?: boolean; validators?: string[]; strictOnly?: boolean; preset?: string };
|
|
14
14
|
L3?: { enabled?: boolean; validators?: string[]; strictOnly?: boolean; preset?: string; coverageThreshold?: number; bundleSizeLimit?: number };
|
|
15
15
|
L4?: { enabled?: boolean; validators?: string[]; strictOnly?: boolean; preset?: string; deadCodeGC?: boolean };
|
|
@@ -32,16 +32,15 @@ export class HarnessConfigValidatorConfigAdapter implements ValidatorConfigPort
|
|
|
32
32
|
this.config = config;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
async getLayerConfig(layer: '
|
|
35
|
+
async getLayerConfig(layer: 'L2' | 'L3' | 'L4'): Promise<LayerConfig> {
|
|
36
36
|
const preset = (this.config.project?.preset ?? this.config.preset ?? 'standard') as 'minimal' | 'standard' | 'strict';
|
|
37
37
|
const strictOnly = preset === 'strict';
|
|
38
38
|
const layerData = this.config.layers?.[layer] ?? {};
|
|
39
39
|
|
|
40
40
|
const defaultValidators: Record<string, string[]> = {
|
|
41
|
-
L0: ['L0-001', 'L0-002'],
|
|
42
41
|
L2: ['L2-001', 'L2-002', 'L2-003'],
|
|
43
42
|
L3: ['L3-001', 'L3-002', 'L3-003', 'L3-004'],
|
|
44
|
-
L4: ['L4-001', 'L4-002', 'L4-003'],
|
|
43
|
+
L4: ['L4-001', 'L4-002', 'L4-003', 'L4-004', 'L4-005'],
|
|
45
44
|
};
|
|
46
45
|
|
|
47
46
|
const thresholds: Record<string, number> = {};
|
|
@@ -64,10 +63,18 @@ export class HarnessConfigValidatorConfigAdapter implements ValidatorConfigPort
|
|
|
64
63
|
return LayerConfig.create({
|
|
65
64
|
layer,
|
|
66
65
|
enabled: layerData.enabled !== false,
|
|
67
|
-
validatorIds: layerData.validators ?? defaultValidators[layer],
|
|
66
|
+
validatorIds: (layerData.validators ?? defaultValidators[layer]).map((idOrName) => this.normalizeValidatorId(idOrName)),
|
|
68
67
|
thresholds,
|
|
69
68
|
strictOnly: layerData.strictOnly ?? strictOnly,
|
|
70
69
|
preset,
|
|
71
70
|
});
|
|
72
71
|
}
|
|
72
|
+
|
|
73
|
+
private normalizeValidatorId(idOrName: string): string {
|
|
74
|
+
try {
|
|
75
|
+
return ValidatorId.create(idOrName).value;
|
|
76
|
+
} catch {
|
|
77
|
+
return ValidatorId.fromName(idOrName).value;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
73
80
|
}
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* RunValidatorsHandler — バリデータ実行 CLIハンドラー
|
|
6
6
|
*/
|
|
7
7
|
import type { RunFullValidationUseCase } from '../../application/use-cases/run-full-validation-usecase.js';
|
|
8
|
-
import type { RunL0ValidatorsUseCase } from '../../application/use-cases/run-l0-validators-usecase.js';
|
|
9
8
|
import type { RunL1ValidatorsUseCase } from '../../application/use-cases/run-l1-validators-usecase.js';
|
|
10
9
|
import type { AggregatedValidationReport } from '../../application/dto/aggregated-validation-report.js';
|
|
11
10
|
import { HumanValidationResultFormatter } from '../formatters/human-validation-result-formatter.js';
|
|
@@ -25,7 +24,6 @@ export interface RunValidatorsHandlerArgs {
|
|
|
25
24
|
|
|
26
25
|
export interface RunValidatorsHandlerDeps {
|
|
27
26
|
runFullValidationUseCase: RunFullValidationUseCase;
|
|
28
|
-
runL0ValidatorsUseCase?: RunL0ValidatorsUseCase;
|
|
29
27
|
runL1ValidatorsUseCase?: RunL1ValidatorsUseCase;
|
|
30
28
|
/** WI-094 / ADR-017: config 由来の failOnWarning デフォルト。args.failOnWarning が undefined の場合に適用 */
|
|
31
29
|
defaultFailOnWarning?: boolean;
|
|
@@ -33,13 +31,11 @@ export interface RunValidatorsHandlerDeps {
|
|
|
33
31
|
|
|
34
32
|
export class RunValidatorsHandler {
|
|
35
33
|
private readonly useCase: RunFullValidationUseCase;
|
|
36
|
-
private readonly l0UseCase: RunL0ValidatorsUseCase | undefined;
|
|
37
34
|
private readonly l1UseCase: RunL1ValidatorsUseCase | undefined;
|
|
38
35
|
private readonly defaultFailOnWarning: boolean;
|
|
39
36
|
|
|
40
37
|
constructor(deps: RunValidatorsHandlerDeps) {
|
|
41
38
|
this.useCase = deps.runFullValidationUseCase;
|
|
42
|
-
this.l0UseCase = deps.runL0ValidatorsUseCase;
|
|
43
39
|
this.l1UseCase = deps.runL1ValidatorsUseCase;
|
|
44
40
|
this.defaultFailOnWarning = deps.defaultFailOnWarning ?? false;
|
|
45
41
|
}
|
|
@@ -47,37 +43,12 @@ export class RunValidatorsHandler {
|
|
|
47
43
|
async execute(args: RunValidatorsHandlerArgs): Promise<{ output: string; exitCode: number }> {
|
|
48
44
|
try {
|
|
49
45
|
if (args.layer === 'L0') {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
});
|
|
57
|
-
const l0Report: AggregatedValidationReport = {
|
|
58
|
-
overallPassed: l0Results.every((r) => r.passed || r.skipped),
|
|
59
|
-
totalValidators: l0Results.length,
|
|
60
|
-
passedValidators: l0Results.filter((r) => r.passed).length,
|
|
61
|
-
failedValidators: l0Results.filter((r) => !r.passed && !r.skipped).length,
|
|
62
|
-
skippedValidators: l0Results.filter((r) => r.skipped).length,
|
|
63
|
-
allErrors: l0Results.flatMap((r) => r.errors),
|
|
64
|
-
summary: {
|
|
65
|
-
totalErrors: l0Results.flatMap((r) => r.errors).filter((e) => e.severity === 'error').length,
|
|
66
|
-
totalWarnings: l0Results.flatMap((r) => r.errors).filter((e) => e.severity === 'warning').length,
|
|
67
|
-
errorsByLayer: { L2: 0, L3: 0, L4: 0 },
|
|
68
|
-
},
|
|
69
|
-
results: l0Results,
|
|
70
|
-
};
|
|
71
|
-
const format = args.format ?? 'human';
|
|
72
|
-
let output: string;
|
|
73
|
-
if (format === 'agent') {
|
|
74
|
-
output = new AgentValidationResultFormatter().format(l0Report);
|
|
75
|
-
} else if (format === 'ci') {
|
|
76
|
-
output = new CiValidationResultFormatter().format(l0Report);
|
|
77
|
-
} else {
|
|
78
|
-
output = new HumanValidationResultFormatter().format(l0Report);
|
|
79
|
-
}
|
|
80
|
-
return { output, exitCode: l0Report.overallPassed ? 0 : 1 };
|
|
46
|
+
const output = [
|
|
47
|
+
'L0 validator execution has been retired.',
|
|
48
|
+
'Runtime L0 enforcement is provided by agent-integration hooks and Husky git hooks.',
|
|
49
|
+
'Use the configured PreToolUse/PostToolUse/Stop/SessionStart/UserPromptSubmit hooks and .husky/pre-commit / .husky/commit-msg instead of validate --layer L0.',
|
|
50
|
+
].join('\n');
|
|
51
|
+
return { output, exitCode: 0 };
|
|
81
52
|
}
|
|
82
53
|
|
|
83
54
|
if (args.layer === 'L1') {
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @layer application
|
|
3
|
-
* @unit validator-system
|
|
4
|
-
*
|
|
5
|
-
* RunL0ValidatorsUseCase — L0 バリデータ実行
|
|
6
|
-
*/
|
|
7
|
-
import { ValidatorId } from '../../domain/value-objects/validator-id.js';
|
|
8
|
-
import { ValidationResult } from '../../domain/value-objects/validation-result.js';
|
|
9
|
-
import { ValidatorRegistry } from '../../domain/services/validator-registry.js';
|
|
10
|
-
import { ValidatorExecutionService } from '../../domain/services/validator-execution-service.js';
|
|
11
|
-
import { ValidationResultContractMapper } from '../mappers/validation-result-contract-mapper.js';
|
|
12
|
-
import type { ValidationResultContract } from '../dto/validation-result-contract.js';
|
|
13
|
-
import type { RunL0ValidatorsInput } from '../dto/run-l0-validators-input.js';
|
|
14
|
-
import type { ValidatorConfigPort } from '../../domain/ports/validator-config-port.js';
|
|
15
|
-
|
|
16
|
-
export interface RunL0ValidatorsUseCaseDeps {
|
|
17
|
-
validatorRegistry: ValidatorRegistry;
|
|
18
|
-
validatorExecutionService: ValidatorExecutionService;
|
|
19
|
-
validatorConfigPort: ValidatorConfigPort;
|
|
20
|
-
contractMapper: ValidationResultContractMapper;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export class RunL0ValidatorsUseCase {
|
|
24
|
-
private readonly registry: ValidatorRegistry;
|
|
25
|
-
private readonly executionService: ValidatorExecutionService;
|
|
26
|
-
private readonly configPort: ValidatorConfigPort;
|
|
27
|
-
private readonly mapper: ValidationResultContractMapper;
|
|
28
|
-
|
|
29
|
-
constructor(deps: RunL0ValidatorsUseCaseDeps) {
|
|
30
|
-
this.registry = deps.validatorRegistry;
|
|
31
|
-
this.executionService = deps.validatorExecutionService;
|
|
32
|
-
this.configPort = deps.validatorConfigPort;
|
|
33
|
-
this.mapper = deps.contractMapper;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async execute(input: RunL0ValidatorsInput): Promise<readonly ValidationResultContract[]> {
|
|
37
|
-
let validatorIds: readonly ValidatorId[];
|
|
38
|
-
if (input.validatorIds && input.validatorIds.length > 0) {
|
|
39
|
-
validatorIds = input.validatorIds.map((id) => ValidatorId.create(id));
|
|
40
|
-
} else {
|
|
41
|
-
validatorIds = this.registry.listByLayer('L0').map((d) => d.validatorId);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const definitions = this.registry.select(validatorIds);
|
|
45
|
-
|
|
46
|
-
const layerConfig = await this.configPort.getLayerConfig('L0');
|
|
47
|
-
if (!layerConfig.enabled) {
|
|
48
|
-
return [];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const results = this.executionService.execute(definitions, [layerConfig]);
|
|
52
|
-
const finalResults = definitions.map(
|
|
53
|
-
(definition) => {
|
|
54
|
-
const result = results.find((r) => r.validatorId.value === definition.validatorId.value);
|
|
55
|
-
return result ?? ValidationResult.skip(definition.validatorId);
|
|
56
|
-
},
|
|
57
|
-
);
|
|
58
|
-
return this.mapper.toContracts(finalResults);
|
|
59
|
-
}
|
|
60
|
-
}
|