phasegate 0.183.0 → 0.212.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 +52 -15
- package/README.md +39 -11
- package/docs/ADR/030-injection-threat-model-and-trust-root.md +145 -0
- package/docs/guide/hooks-integration.md +50 -1
- package/docs/guide/installation.md +1 -1
- package/docs/guide/quick-vs-full-mode.md +1 -1
- package/docs/guide/skills-overview.md +17 -17
- package/package.json +1 -1
- package/scripts/harness/agent-integration/presentation/phasegate-status-context.ts +131 -63
- package/scripts/harness/agent-integration/presentation/session-start-hook.ts +31 -4
- package/scripts/harness/agent-integration/presentation/spotlight.ts +65 -0
- package/scripts/harness/biome-ast-engine/infrastructure/parsers/comment-density-parser.ts +51 -10
- package/scripts/harness/ci-governance/application/dto/pin-integrity-input.ts +8 -0
- package/scripts/harness/ci-governance/application/dto/pin-integrity-output.ts +9 -0
- package/scripts/harness/ci-governance/application/dto/verify-integrity-input.ts +7 -0
- package/scripts/harness/ci-governance/application/dto/verify-integrity-output.ts +10 -0
- package/scripts/harness/ci-governance/application/usecases/pin-integrity-usecase.ts +60 -0
- package/scripts/harness/ci-governance/application/usecases/verify-integrity-usecase.ts +46 -0
- package/scripts/harness/ci-governance/composition-root.ts +68 -65
- package/scripts/harness/ci-governance/domain/ports/integrity-manifest-repository-port.ts +14 -0
- package/scripts/harness/ci-governance/domain/ports/sha256-hasher-port.ts +10 -0
- package/scripts/harness/ci-governance/domain/services/integrity-checker.ts +42 -0
- package/scripts/harness/ci-governance/domain/value-objects/integrity-drift.ts +16 -0
- package/scripts/harness/ci-governance/domain/value-objects/integrity-manifest.ts +49 -0
- package/scripts/harness/ci-governance/domain/value-objects/integrity-target.ts +40 -0
- package/scripts/harness/ci-governance/infrastructure/adapters/adr-foundation-existence-adapter.ts +26 -2
- package/scripts/harness/ci-governance/infrastructure/adapters/file-system-sha256-hasher-adapter.ts +17 -0
- package/scripts/harness/ci-governance/infrastructure/adapters/harness-api-command-existence-adapter.ts +8 -1
- package/scripts/harness/ci-governance/infrastructure/adapters/integrity-manifest-json-repository-adapter.ts +83 -0
- package/scripts/harness/ci-governance/presentation/handlers/integrity-handler.ts +76 -0
- package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +53 -28
- package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +33 -8
- package/scripts/harness/harness-api/domain/value-objects/known-harness-commands.ts +90 -0
- package/scripts/harness/installation/application/bundled-skill-selection.ts +2 -5
- package/scripts/harness/main.ts +257 -105
- package/scripts/harness/phase-dependency-model/domain/ports/story-reflection-file-system-port.ts +2 -0
- package/scripts/harness/phase-dependency-model/domain/services/story-reflection-checker.ts +8 -0
- package/scripts/harness/phase-dependency-model/infrastructure/filesystem/file-system-story-reflection-adapter.ts +137 -2
- package/scripts/harness/quick-mode/domain/services/quick-mode-judgment-engine.ts +44 -40
- package/scripts/harness/setup/skill-deployer.ts +8 -10
- package/scripts/harness/skill-quality/domain/services/skill-structure-validator.ts +13 -2
- package/scripts/harness/skill-quality/domain/types/skill-kind.ts +6 -0
- package/scripts/harness/skill-quality/domain/value-objects/skill-structure.ts +24 -8
- package/scripts/harness/validator-system/application/use-cases/run-l2-validators-usecase.ts +82 -49
- package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +87 -58
- package/scripts/harness/validator-system/composition-root.ts +141 -99
- package/scripts/harness/validator-system/domain/ports/coverage-attestation-gating-policy-port.ts +14 -0
- package/scripts/harness/validator-system/domain/ports/injection-scan-policy-port.ts +14 -0
- package/scripts/harness/validator-system/domain/services/coverage-attestation-gating-service.ts +56 -0
- package/scripts/harness/validator-system/domain/services/injection-pattern-scan-service.ts +118 -0
- package/scripts/harness/validator-system/domain/value-objects/coverage-gating-report.ts +67 -0
- package/scripts/harness/validator-system/domain/value-objects/injection-scan-report.ts +55 -0
- package/scripts/harness/validator-system/domain/value-objects/validator-id.ts +35 -31
- package/scripts/harness/validator-system/infrastructure/adapters/adr-foundation-reference-adapter.ts +13 -7
- package/scripts/harness/validator-system/infrastructure/adapters/file-system-coverage-attestation-gating-adapter.ts +87 -0
- package/scripts/harness/validator-system/infrastructure/adapters/file-system-injection-scan-adapter.ts +82 -0
- package/skills/README.md +1 -1
- package/skills/cascade-updater/SKILL.md +3 -3
- package/skills/codebase-mapper/SKILL.md +6 -6
- package/skills/codex-delegator/SKILL.md +4 -3
- package/skills/codex-delegator/references/prompt-patterns.md +3 -3
- package/skills/codex-delegator/references/review-dimensions.md +1 -1
- package/skills/consistency-checker/SKILL.md +1 -1
- package/skills/consistency-checker/references//343/203/201/343/202/247/343/203/203/343/202/257/343/203/252/343/202/271/343/203/210.md +1 -1
- package/skills/doc-health-checker/SKILL.md +148 -0
- package/skills/domain-designer/SKILL.md +4 -2
- package/skills/engineering-perspective/SKILL.md +1 -0
- package/skills/environment-designer/SKILL.md +8 -6
- package/skills/implementation-readiness-checker/SKILL.md +2 -1
- package/skills/it-test-designer/SKILL.md +10 -8
- package/skills/it-test-logic-designer/SKILL.md +11 -9
- package/skills/it-test-logic-designer/references/repository-test-patterns.md +8 -1
- package/skills/logical-designer/SKILL.md +5 -3
- package/skills/mock-designer/SKILL.md +10 -6
- package/skills/phasegate-config-doctor/SKILL.md +3 -2
- package/skills/phasegate-toolkit-guide/SKILL.md +1 -0
- package/skills/quick-implementor/SKILL.md +1 -1
- package/skills/release-publisher/SKILL.md +101 -0
- package/skills/scenario-test-designer/SKILL.md +27 -14
- package/skills/scenario-test-logic-designer/SKILL.md +10 -8
- package/skills/scenario-test-logic-designer/references/msw-patterns.md +3 -1
- package/skills/scenario-test-logic-designer/references/playwright-patterns.md +3 -1
- package/skills/skill-creator/SKILL.md +75 -332
- package/skills/story-implementor/SKILL.md +54 -0
- package/skills/story-mapper/SKILL.md +8 -4
- package/skills/story-writer/SKILL.md +14 -5
- package/skills/test-coverage-checker/SKILL.md +4 -6
- package/skills/uiux-designer/SKILL.md +4 -2
- package/skills/uiux-designer/references/uiux-design-template.md +4 -4
- package/skills/unit-designer/SKILL.md +11 -7
- package/skills/unit-test-designer/SKILL.md +25 -11
- package/skills/unit-test-logic-designer/SKILL.md +10 -8
- package/skills/unit-test-logic-designer/references/test-patterns.md +7 -1
- package/skills/doc-freshness-checker/SKILL.md +0 -140
- package/skills/implementation-planner/SKILL.md +0 -167
- package/skills/implementation-planner/references/document-structure.md +0 -116
- package/skills/implementation-planner/references/plan-template.md +0 -177
- package/skills/implementation-planner/references/workflow.md +0 -164
- package/skills/pointer-validator/SKILL.md +0 -104
|
@@ -5,24 +5,29 @@
|
|
|
5
5
|
* RunL2ValidatorsUseCase — H08-01: L2バリデータ実行
|
|
6
6
|
* @work-item-id WI-110 / WI-111 / WI-140 / WI-132 / WI-133 / WI-136 / WI-137 / WI-138
|
|
7
7
|
*/
|
|
8
|
-
import { readFile } from
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import type {
|
|
12
|
-
import
|
|
13
|
-
import type {
|
|
14
|
-
import type {
|
|
15
|
-
import type {
|
|
16
|
-
import type { ValidatorConfigPort } from
|
|
17
|
-
import type {
|
|
18
|
-
import
|
|
19
|
-
import
|
|
20
|
-
import
|
|
21
|
-
import
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
import {
|
|
8
|
+
import { readFile } from "node:fs/promises";
|
|
9
|
+
import type { CliCommandRegistryPort } from "../../domain/ports/cli-command-registry-port.js";
|
|
10
|
+
import type { ContractTraceabilityPolicyPort } from "../../domain/ports/contract-traceability-policy-port.js";
|
|
11
|
+
import type { CoverageAttestationGatingPolicyPort } from "../../domain/ports/coverage-attestation-gating-policy-port.js";
|
|
12
|
+
import type { E2eTestFileRegistryPort } from "../../domain/ports/e2e-test-file-registry-port.js";
|
|
13
|
+
import type { MetadataPolicyPort } from "../../domain/ports/metadata-policy-port.js";
|
|
14
|
+
import type { PhaseGatePolicyPort } from "../../domain/ports/phase-gate-policy-port.js";
|
|
15
|
+
import type { TestQualityAnalyzerPort } from "../../domain/ports/test-quality-analyzer-port.js";
|
|
16
|
+
import type { ValidatorConfigPort } from "../../domain/ports/validator-config-port.js";
|
|
17
|
+
import type { WorkItemStatusPolicyPort } from "../../domain/ports/work-item-status-policy-port.js";
|
|
18
|
+
import { CliE2eTestExistenceService } from "../../domain/services/cli-e2e-test-existence-service.js";
|
|
19
|
+
import { ContractTraceabilityCoverageService } from "../../domain/services/contract-traceability-coverage-service.js";
|
|
20
|
+
import { CoverageAttestationGatingService } from "../../domain/services/coverage-attestation-gating-service.js";
|
|
21
|
+
import {
|
|
22
|
+
ValidatorExecutionError,
|
|
23
|
+
type ValidatorExecutionService,
|
|
24
|
+
} from "../../domain/services/validator-execution-service.js";
|
|
25
|
+
import type { ValidatorRegistry } from "../../domain/services/validator-registry.js";
|
|
26
|
+
import { type HarnessErrorLike, ValidationResult } from "../../domain/value-objects/validation-result.js";
|
|
27
|
+
import { ValidatorId } from "../../domain/value-objects/validator-id.js";
|
|
28
|
+
import type { RunL2ValidatorsInput } from "../dto/run-l2-validators-input.js";
|
|
29
|
+
import type { ValidationResultContract } from "../dto/validation-result-contract.js";
|
|
30
|
+
import type { ValidationResultContractMapper } from "../mappers/validation-result-contract-mapper.js";
|
|
26
31
|
|
|
27
32
|
export interface RunL2ValidatorsUseCaseDeps {
|
|
28
33
|
validatorRegistry: ValidatorRegistry;
|
|
@@ -36,6 +41,7 @@ export interface RunL2ValidatorsUseCaseDeps {
|
|
|
36
41
|
e2eTestFileRegistryPort?: E2eTestFileRegistryPort;
|
|
37
42
|
workItemStatusPolicyPort?: WorkItemStatusPolicyPort;
|
|
38
43
|
contractTraceabilityPolicyPort?: ContractTraceabilityPolicyPort;
|
|
44
|
+
coverageAttestationGatingPolicyPort?: CoverageAttestationGatingPolicyPort;
|
|
39
45
|
}
|
|
40
46
|
|
|
41
47
|
export class RunL2ValidatorsUseCase {
|
|
@@ -50,8 +56,10 @@ export class RunL2ValidatorsUseCase {
|
|
|
50
56
|
private readonly e2eTestFileRegistryPort?: E2eTestFileRegistryPort;
|
|
51
57
|
private readonly workItemStatusPolicyPort?: WorkItemStatusPolicyPort;
|
|
52
58
|
private readonly contractTraceabilityPolicyPort?: ContractTraceabilityPolicyPort;
|
|
59
|
+
private readonly coverageAttestationGatingPolicyPort?: CoverageAttestationGatingPolicyPort;
|
|
53
60
|
private readonly cliE2eTestExistenceService = new CliE2eTestExistenceService();
|
|
54
61
|
private readonly contractTraceabilityCoverageService = new ContractTraceabilityCoverageService();
|
|
62
|
+
private readonly coverageAttestationGatingService = new CoverageAttestationGatingService();
|
|
55
63
|
|
|
56
64
|
constructor(deps: RunL2ValidatorsUseCaseDeps) {
|
|
57
65
|
this.registry = deps.validatorRegistry;
|
|
@@ -65,6 +73,7 @@ export class RunL2ValidatorsUseCase {
|
|
|
65
73
|
this.e2eTestFileRegistryPort = deps.e2eTestFileRegistryPort;
|
|
66
74
|
this.workItemStatusPolicyPort = deps.workItemStatusPolicyPort;
|
|
67
75
|
this.contractTraceabilityPolicyPort = deps.contractTraceabilityPolicyPort;
|
|
76
|
+
this.coverageAttestationGatingPolicyPort = deps.coverageAttestationGatingPolicyPort;
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
async execute(input: RunL2ValidatorsInput): Promise<readonly ValidationResultContract[]> {
|
|
@@ -73,24 +82,27 @@ export class RunL2ValidatorsUseCase {
|
|
|
73
82
|
if (input.validatorIds && input.validatorIds.length > 0) {
|
|
74
83
|
validatorIds = input.validatorIds.map((id) => ValidatorId.create(id));
|
|
75
84
|
} else {
|
|
76
|
-
validatorIds = this.registry.listByLayer(
|
|
85
|
+
validatorIds = this.registry.listByLayer("L2").map((d) => d.validatorId);
|
|
77
86
|
}
|
|
78
87
|
|
|
79
88
|
const definitions = this.registry.select(validatorIds);
|
|
80
89
|
|
|
81
90
|
// LayerConfig取得
|
|
82
|
-
let layerConfig
|
|
91
|
+
let layerConfig: Awaited<ReturnType<ValidatorConfigPort["getLayerConfig"]>>;
|
|
83
92
|
try {
|
|
84
|
-
layerConfig = await this.configPort.getLayerConfig(
|
|
93
|
+
layerConfig = await this.configPort.getLayerConfig("L2");
|
|
85
94
|
} catch (err) {
|
|
86
|
-
throw new ValidatorExecutionError(
|
|
95
|
+
throw new ValidatorExecutionError(
|
|
96
|
+
`Failed to get L2 LayerConfig: ${err instanceof Error ? err.message : String(err)}`,
|
|
97
|
+
err,
|
|
98
|
+
);
|
|
87
99
|
}
|
|
88
100
|
|
|
89
101
|
const results = this.executionService.execute(definitions, [layerConfig]);
|
|
90
102
|
const overrideMap = new Map<string, ValidationResult>(results.map((result) => [result.validatorId.value, result]));
|
|
91
103
|
|
|
92
104
|
if (this.phaseGatePolicyPort) {
|
|
93
|
-
const l2001Result = overrideMap.get(
|
|
105
|
+
const l2001Result = overrideMap.get("L2-001");
|
|
94
106
|
if (l2001Result && !l2001Result.skipped) {
|
|
95
107
|
const policyResult = await this.phaseGatePolicyPort.checkPrerequisites({
|
|
96
108
|
unitName: input.unitName,
|
|
@@ -98,24 +110,20 @@ export class RunL2ValidatorsUseCase {
|
|
|
98
110
|
});
|
|
99
111
|
if (!policyResult.satisfied) {
|
|
100
112
|
overrideMap.set(
|
|
101
|
-
|
|
102
|
-
ValidationResult.fail(
|
|
103
|
-
ValidatorId.create('L2-001'),
|
|
104
|
-
[...policyResult.violations],
|
|
105
|
-
0,
|
|
106
|
-
),
|
|
113
|
+
"L2-001",
|
|
114
|
+
ValidationResult.fail(ValidatorId.create("L2-001"), [...policyResult.violations], 0),
|
|
107
115
|
);
|
|
108
116
|
}
|
|
109
117
|
}
|
|
110
118
|
}
|
|
111
119
|
|
|
112
120
|
if (this.metadataPolicyPort) {
|
|
113
|
-
const l2002Result = overrideMap.get(
|
|
121
|
+
const l2002Result = overrideMap.get("L2-002");
|
|
114
122
|
if (l2002Result && !l2002Result.skipped) {
|
|
115
123
|
const allErrors: HarnessErrorLike[] = [];
|
|
116
124
|
for (const filePath of input.targetPaths) {
|
|
117
125
|
try {
|
|
118
|
-
const fileContent = await readFile(filePath,
|
|
126
|
+
const fileContent = await readFile(filePath, "utf-8");
|
|
119
127
|
const policyResult = await this.metadataPolicyPort.validateMetadata({ filePath, fileContent });
|
|
120
128
|
if (!policyResult.passed) {
|
|
121
129
|
allErrors.push(...policyResult.errors);
|
|
@@ -125,52 +133,52 @@ export class RunL2ValidatorsUseCase {
|
|
|
125
133
|
}
|
|
126
134
|
}
|
|
127
135
|
if (allErrors.length > 0) {
|
|
128
|
-
overrideMap.set(
|
|
136
|
+
overrideMap.set("L2-002", ValidationResult.fail(ValidatorId.create("L2-002"), allErrors, 0));
|
|
129
137
|
}
|
|
130
138
|
}
|
|
131
139
|
}
|
|
132
140
|
|
|
133
141
|
if (this.testQualityAnalyzerPort) {
|
|
134
|
-
const l2003Result = overrideMap.get(
|
|
142
|
+
const l2003Result = overrideMap.get("L2-003");
|
|
135
143
|
if (l2003Result && !l2003Result.skipped) {
|
|
136
144
|
const analysisResult = await this.testQualityAnalyzerPort.analyzeTestFiles(input.targetPaths);
|
|
137
145
|
const allViolations: HarnessErrorLike[] = analysisResult.results
|
|
138
146
|
.filter((r) => !r.passed)
|
|
139
147
|
.flatMap((r) => [...r.violations]);
|
|
140
148
|
if (allViolations.length > 0) {
|
|
141
|
-
overrideMap.set(
|
|
149
|
+
overrideMap.set("L2-003", ValidationResult.fail(ValidatorId.create("L2-003"), allViolations, 0));
|
|
142
150
|
}
|
|
143
151
|
}
|
|
144
152
|
}
|
|
145
153
|
|
|
146
154
|
if (this.cliCommandRegistryPort && this.e2eTestFileRegistryPort) {
|
|
147
|
-
const l2013Result = overrideMap.get(
|
|
155
|
+
const l2013Result = overrideMap.get("L2-013");
|
|
148
156
|
if (l2013Result && !l2013Result.skipped) {
|
|
149
157
|
const commands = await this.cliCommandRegistryPort.getRegisteredCommands();
|
|
150
158
|
const e2eFiles = await this.e2eTestFileRegistryPort.getE2eTestFiles();
|
|
151
159
|
const report = this.cliE2eTestExistenceService.check(commands, e2eFiles);
|
|
152
160
|
if (report.hasViolations()) {
|
|
153
161
|
const errors = report.toMessages().map((msg) => ({
|
|
154
|
-
code: { value:
|
|
155
|
-
severity: { value:
|
|
162
|
+
code: { value: "L2-013", toString: () => "L2-013" },
|
|
163
|
+
severity: { value: "error", toString: () => "error" },
|
|
156
164
|
message: msg,
|
|
157
|
-
suggestion:
|
|
165
|
+
suggestion: "Add an E2E test for each registered CLI command in cli-harness.test.ts.",
|
|
158
166
|
}));
|
|
159
|
-
overrideMap.set(
|
|
167
|
+
overrideMap.set("L2-013", ValidationResult.fail(ValidatorId.create("L2-013"), errors, 0));
|
|
160
168
|
} else {
|
|
161
|
-
overrideMap.set(
|
|
169
|
+
overrideMap.set("L2-013", ValidationResult.pass(ValidatorId.create("L2-013"), 0));
|
|
162
170
|
}
|
|
163
171
|
}
|
|
164
172
|
}
|
|
165
173
|
|
|
166
174
|
if (this.workItemStatusPolicyPort) {
|
|
167
|
-
const l2014Result = overrideMap.get(
|
|
175
|
+
const l2014Result = overrideMap.get("L2-014");
|
|
168
176
|
if (l2014Result && !l2014Result.skipped) {
|
|
169
177
|
const staleReports = await this.workItemStatusPolicyPort.findStaleReports(input.targetPaths);
|
|
170
178
|
if (staleReports.length > 0) {
|
|
171
179
|
const errors = staleReports.map((report) => ({
|
|
172
|
-
code: { value:
|
|
173
|
-
severity: { value:
|
|
180
|
+
code: { value: "L2-014", toString: () => "L2-014" },
|
|
181
|
+
severity: { value: "error", toString: () => "error" },
|
|
174
182
|
message: `${report.id} status is stale: current=${report.currentStatus}, derived=${report.derivedStatus}`,
|
|
175
183
|
suggestion: report.nextAction,
|
|
176
184
|
workItemId: report.id,
|
|
@@ -179,21 +187,21 @@ export class RunL2ValidatorsUseCase {
|
|
|
179
187
|
derivedStatus: report.derivedStatus,
|
|
180
188
|
evidence: report.evidence,
|
|
181
189
|
}));
|
|
182
|
-
overrideMap.set(
|
|
190
|
+
overrideMap.set("L2-014", ValidationResult.fail(ValidatorId.create("L2-014"), errors, 0));
|
|
183
191
|
} else {
|
|
184
|
-
overrideMap.set(
|
|
192
|
+
overrideMap.set("L2-014", ValidationResult.pass(ValidatorId.create("L2-014"), 0));
|
|
185
193
|
}
|
|
186
194
|
}
|
|
187
195
|
}
|
|
188
196
|
|
|
189
197
|
if (this.contractTraceabilityPolicyPort) {
|
|
190
|
-
const l2015Result = overrideMap.get(
|
|
198
|
+
const l2015Result = overrideMap.get("L2-015");
|
|
191
199
|
if (l2015Result && !l2015Result.skipped) {
|
|
192
200
|
const inputModel = await this.contractTraceabilityPolicyPort.collect(input.targetPaths);
|
|
193
201
|
const report = this.contractTraceabilityCoverageService.check(inputModel);
|
|
194
202
|
if (report.hasFindings()) {
|
|
195
203
|
const errors = report.findings.map((finding) => ({
|
|
196
|
-
code: { value:
|
|
204
|
+
code: { value: "L2-015", toString: () => "L2-015" },
|
|
197
205
|
severity: { value: finding.severity, toString: () => finding.severity },
|
|
198
206
|
message: finding.message,
|
|
199
207
|
suggestion: finding.suggestion,
|
|
@@ -201,9 +209,34 @@ export class RunL2ValidatorsUseCase {
|
|
|
201
209
|
subject: finding.subject,
|
|
202
210
|
sourcePath: finding.sourcePath,
|
|
203
211
|
}));
|
|
204
|
-
overrideMap.set(
|
|
212
|
+
overrideMap.set("L2-015", ValidationResult.fail(ValidatorId.create("L2-015"), errors, 0));
|
|
205
213
|
} else {
|
|
206
|
-
overrideMap.set(
|
|
214
|
+
overrideMap.set("L2-015", ValidationResult.pass(ValidatorId.create("L2-015"), 0));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// WI-258 / ADR-030 §Decision.3.②: L2-016 coverage-attestation-gating。
|
|
220
|
+
// bare ✅(attestation 参照なし)は error violation(fail-closed)。ungated-legacy マーカーは
|
|
221
|
+
// 免除だが warning(見える負債)で件数報告する。warning-only の fail は ADR-017 の集約規則で
|
|
222
|
+
// failOnWarning=false のとき overall PASS 扱いとなる。
|
|
223
|
+
if (this.coverageAttestationGatingPolicyPort) {
|
|
224
|
+
const l2016Result = overrideMap.get("L2-016");
|
|
225
|
+
if (l2016Result && !l2016Result.skipped) {
|
|
226
|
+
const models = await this.coverageAttestationGatingPolicyPort.collect();
|
|
227
|
+
const report = this.coverageAttestationGatingService.check(models);
|
|
228
|
+
const findings = [...report.violations, ...report.warnings];
|
|
229
|
+
if (findings.length > 0) {
|
|
230
|
+
const errors: HarnessErrorLike[] = findings.map((finding) => ({
|
|
231
|
+
code: { value: "L2-016", toString: () => "L2-016" },
|
|
232
|
+
severity: { value: finding.severity, toString: () => finding.severity },
|
|
233
|
+
message: finding.message,
|
|
234
|
+
suggestion: finding.suggestion,
|
|
235
|
+
sourcePath: finding.sourcePath,
|
|
236
|
+
}));
|
|
237
|
+
overrideMap.set("L2-016", ValidationResult.fail(ValidatorId.create("L2-016"), errors, 0));
|
|
238
|
+
} else {
|
|
239
|
+
overrideMap.set("L2-016", ValidationResult.pass(ValidatorId.create("L2-016"), 0));
|
|
207
240
|
}
|
|
208
241
|
}
|
|
209
242
|
}
|
|
@@ -5,24 +5,31 @@
|
|
|
5
5
|
*
|
|
6
6
|
* RunL3ValidatorsUseCase — H08-02: L3バリデータ実行
|
|
7
7
|
*/
|
|
8
|
-
|
|
9
|
-
import {
|
|
10
|
-
import type {
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
import type {
|
|
14
|
-
import type {
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
import
|
|
8
|
+
|
|
9
|
+
import type { AcBoundCoveragePolicyPort } from "../../domain/ports/ac-bound-coverage-policy-port.js";
|
|
10
|
+
import type { AcCoveragePolicyPort } from "../../domain/ports/ac-coverage-policy-port.js";
|
|
11
|
+
import type { InjectionScanPolicyPort } from "../../domain/ports/injection-scan-policy-port.js";
|
|
12
|
+
import type { PerformanceScannerPort } from "../../domain/ports/performance-scanner-port.js";
|
|
13
|
+
import type { SecurityPatternScannerPort } from "../../domain/ports/security-pattern-scanner-port.js";
|
|
14
|
+
import type { ValidatorConfigPort } from "../../domain/ports/validator-config-port.js";
|
|
15
|
+
import { InjectionPatternScanService } from "../../domain/services/injection-pattern-scan-service.js";
|
|
16
|
+
import {
|
|
17
|
+
ValidatorExecutionError,
|
|
18
|
+
type ValidatorExecutionService,
|
|
19
|
+
} from "../../domain/services/validator-execution-service.js";
|
|
20
|
+
import { ValidatorLanguageCapabilityService } from "../../domain/services/validator-language-capability-service.js";
|
|
21
|
+
import type { ValidatorRegistry } from "../../domain/services/validator-registry.js";
|
|
22
|
+
import type { HarnessErrorLike } from "../../domain/value-objects/validation-result.js";
|
|
23
|
+
import { ValidationResult } from "../../domain/value-objects/validation-result.js";
|
|
24
|
+
import { ValidatorId } from "../../domain/value-objects/validator-id.js";
|
|
25
|
+
import type { RunL3ValidatorsInput } from "../dto/run-l3-validators-input.js";
|
|
26
|
+
import type { ValidationResultContract } from "../dto/validation-result-contract.js";
|
|
27
|
+
import type { ValidationResultContractMapper } from "../mappers/validation-result-contract-mapper.js";
|
|
21
28
|
|
|
22
29
|
export class CoverageReportNotFoundError extends Error {
|
|
23
30
|
constructor(path: string) {
|
|
24
31
|
super(`Coverage report not found: ${path}`);
|
|
25
|
-
this.name =
|
|
32
|
+
this.name = "CoverageReportNotFoundError";
|
|
26
33
|
}
|
|
27
34
|
}
|
|
28
35
|
|
|
@@ -33,9 +40,16 @@ export interface RunL3ValidatorsUseCaseDeps {
|
|
|
33
40
|
contractMapper: ValidationResultContractMapper;
|
|
34
41
|
acCoveragePolicyPort?: AcCoveragePolicyPort;
|
|
35
42
|
acBoundCoveragePolicyPort?: AcBoundCoveragePolicyPort;
|
|
36
|
-
coverageReportPort?: {
|
|
43
|
+
coverageReportPort?: {
|
|
44
|
+
getCoverage(): Promise<{
|
|
45
|
+
overallCoverage: number;
|
|
46
|
+
perFileCoverage: readonly { filePath: string; coverage: number }[];
|
|
47
|
+
}>;
|
|
48
|
+
};
|
|
37
49
|
securityScannerPort?: SecurityPatternScannerPort;
|
|
38
50
|
performanceScannerPort?: PerformanceScannerPort;
|
|
51
|
+
/** WI-259 / ADR-030 §Decision.3.④: L3-006 (injection-scan, advisory) 用ポート。 */
|
|
52
|
+
injectionScanPolicyPort?: InjectionScanPolicyPort;
|
|
39
53
|
/** L3-005 のスコープ対象 story-id(config layers.L3.acBoundStories 由来。既定 [])。 */
|
|
40
54
|
acBoundStories?: readonly string[];
|
|
41
55
|
}
|
|
@@ -47,11 +61,13 @@ export class RunL3ValidatorsUseCase {
|
|
|
47
61
|
private readonly mapper: ValidationResultContractMapper;
|
|
48
62
|
private readonly acCoveragePolicyPort?: AcCoveragePolicyPort;
|
|
49
63
|
private readonly acBoundCoveragePolicyPort?: AcBoundCoveragePolicyPort;
|
|
50
|
-
private readonly coverageReportPort?: RunL3ValidatorsUseCaseDeps[
|
|
64
|
+
private readonly coverageReportPort?: RunL3ValidatorsUseCaseDeps["coverageReportPort"];
|
|
51
65
|
private readonly securityScannerPort?: SecurityPatternScannerPort;
|
|
52
66
|
private readonly performanceScannerPort?: PerformanceScannerPort;
|
|
67
|
+
private readonly injectionScanPolicyPort?: InjectionScanPolicyPort;
|
|
53
68
|
private readonly acBoundStories: readonly string[];
|
|
54
69
|
private readonly languageCapabilityService = new ValidatorLanguageCapabilityService();
|
|
70
|
+
private readonly injectionScanService = new InjectionPatternScanService();
|
|
55
71
|
|
|
56
72
|
constructor(deps: RunL3ValidatorsUseCaseDeps) {
|
|
57
73
|
this.registry = deps.validatorRegistry;
|
|
@@ -63,6 +79,7 @@ export class RunL3ValidatorsUseCase {
|
|
|
63
79
|
this.coverageReportPort = deps.coverageReportPort;
|
|
64
80
|
this.securityScannerPort = deps.securityScannerPort;
|
|
65
81
|
this.performanceScannerPort = deps.performanceScannerPort;
|
|
82
|
+
this.injectionScanPolicyPort = deps.injectionScanPolicyPort;
|
|
66
83
|
this.acBoundStories = deps.acBoundStories ?? [];
|
|
67
84
|
}
|
|
68
85
|
|
|
@@ -71,16 +88,19 @@ export class RunL3ValidatorsUseCase {
|
|
|
71
88
|
if (input.validatorIds && input.validatorIds.length > 0) {
|
|
72
89
|
validatorIds = input.validatorIds.map((id) => ValidatorId.create(id));
|
|
73
90
|
} else {
|
|
74
|
-
validatorIds = this.registry.listByLayer(
|
|
91
|
+
validatorIds = this.registry.listByLayer("L3").map((d) => d.validatorId);
|
|
75
92
|
}
|
|
76
93
|
|
|
77
94
|
const definitions = this.registry.select(validatorIds);
|
|
78
95
|
|
|
79
|
-
let layerConfig
|
|
96
|
+
let layerConfig: Awaited<ReturnType<ValidatorConfigPort["getLayerConfig"]>>;
|
|
80
97
|
try {
|
|
81
|
-
layerConfig = await this.configPort.getLayerConfig(
|
|
98
|
+
layerConfig = await this.configPort.getLayerConfig("L3");
|
|
82
99
|
} catch (err) {
|
|
83
|
-
throw new ValidatorExecutionError(
|
|
100
|
+
throw new ValidatorExecutionError(
|
|
101
|
+
`Failed to get L3 LayerConfig: ${err instanceof Error ? err.message : String(err)}`,
|
|
102
|
+
err,
|
|
103
|
+
);
|
|
84
104
|
}
|
|
85
105
|
|
|
86
106
|
// LayerConfig.enabled === false の場合は空を返す
|
|
@@ -105,18 +125,17 @@ export class RunL3ValidatorsUseCase {
|
|
|
105
125
|
// このブロックは例外を送出せず、L3-003 の per-validator 結果のみを差し替える。
|
|
106
126
|
// これにより兄弟バリデータ(L3-001/002/004 および L2/L4 バッチ)は常に通常実行される。
|
|
107
127
|
const l3003InScope =
|
|
108
|
-
!unsupportedValidatorIds.has(
|
|
109
|
-
definitions.some((d) => d.validatorId.value === 'L3-003');
|
|
128
|
+
!unsupportedValidatorIds.has("L3-003") && definitions.some((d) => d.validatorId.value === "L3-003");
|
|
110
129
|
if (this.coverageReportPort && l3003InScope) {
|
|
111
|
-
const l3003Id = ValidatorId.create(
|
|
112
|
-
const threshold = layerConfig.getThreshold(
|
|
130
|
+
const l3003Id = ValidatorId.create("L3-003");
|
|
131
|
+
const threshold = layerConfig.getThreshold("coverageThreshold");
|
|
113
132
|
|
|
114
133
|
if (threshold === null) {
|
|
115
134
|
overrideMap.set(
|
|
116
|
-
|
|
135
|
+
"L3-003",
|
|
117
136
|
ValidationResult.skipWithReason(
|
|
118
137
|
l3003Id,
|
|
119
|
-
|
|
138
|
+
"coverageThreshold が未設定のためカバレッジ判定をスキップ(カバレッジゲートはオプトイン)",
|
|
120
139
|
),
|
|
121
140
|
);
|
|
122
141
|
} else {
|
|
@@ -124,13 +143,13 @@ export class RunL3ValidatorsUseCase {
|
|
|
124
143
|
const coverageData = await this.coverageReportPort.getCoverage();
|
|
125
144
|
if (coverageData.overallCoverage < threshold) {
|
|
126
145
|
overrideMap.set(
|
|
127
|
-
|
|
146
|
+
"L3-003",
|
|
128
147
|
ValidationResult.fail(
|
|
129
148
|
l3003Id,
|
|
130
149
|
[
|
|
131
150
|
{
|
|
132
|
-
code:
|
|
133
|
-
severity:
|
|
151
|
+
code: "L3-003",
|
|
152
|
+
severity: "error",
|
|
134
153
|
message: `カバレッジ不足: 現在値 ${coverageData.overallCoverage}%、不足 ${threshold - coverageData.overallCoverage}%`,
|
|
135
154
|
suggestion: `テストカバレッジを ${threshold}% 以上に引き上げてください`,
|
|
136
155
|
},
|
|
@@ -139,20 +158,20 @@ export class RunL3ValidatorsUseCase {
|
|
|
139
158
|
),
|
|
140
159
|
);
|
|
141
160
|
} else {
|
|
142
|
-
overrideMap.set(
|
|
161
|
+
overrideMap.set("L3-003", ValidationResult.pass(l3003Id, 0));
|
|
143
162
|
}
|
|
144
163
|
} catch {
|
|
145
164
|
// レポート不在などで取得失敗 → FAIL-CLOSED(例外は握りつぶし per-validator FAIL に変換)
|
|
146
165
|
overrideMap.set(
|
|
147
|
-
|
|
166
|
+
"L3-003",
|
|
148
167
|
ValidationResult.fail(
|
|
149
168
|
l3003Id,
|
|
150
169
|
[
|
|
151
170
|
{
|
|
152
|
-
code:
|
|
153
|
-
severity:
|
|
171
|
+
code: "L3-003",
|
|
172
|
+
severity: "error",
|
|
154
173
|
message: `coverageThreshold=${threshold}% が設定されていますがカバレッジレポートが見つかりません(テストをカバレッジ付きで実行してください)`,
|
|
155
|
-
suggestion:
|
|
174
|
+
suggestion: "vitest --coverage 等でカバレッジレポートを生成してから再実行してください",
|
|
156
175
|
},
|
|
157
176
|
],
|
|
158
177
|
0,
|
|
@@ -163,42 +182,35 @@ export class RunL3ValidatorsUseCase {
|
|
|
163
182
|
}
|
|
164
183
|
|
|
165
184
|
if (this.securityScannerPort) {
|
|
166
|
-
const l3001Result = overrideMap.get(
|
|
185
|
+
const l3001Result = overrideMap.get("L3-001");
|
|
167
186
|
if (l3001Result && !l3001Result.skipped) {
|
|
168
187
|
const scanResult = await this.securityScannerPort.scan(input.targetPaths);
|
|
169
188
|
if (!scanResult.passed) {
|
|
170
|
-
overrideMap.set(
|
|
189
|
+
overrideMap.set("L3-001", ValidationResult.fail(ValidatorId.create("L3-001"), [...scanResult.findings], 0));
|
|
171
190
|
}
|
|
172
191
|
}
|
|
173
192
|
}
|
|
174
193
|
|
|
175
194
|
if (this.performanceScannerPort) {
|
|
176
|
-
const l3002Result = overrideMap.get(
|
|
195
|
+
const l3002Result = overrideMap.get("L3-002");
|
|
177
196
|
if (l3002Result && !l3002Result.skipped) {
|
|
178
|
-
const bundleSizeLimit = layerConfig.getThreshold(
|
|
197
|
+
const bundleSizeLimit = layerConfig.getThreshold("bundleSizeLimit");
|
|
179
198
|
const thresholds: Record<string, number> = bundleSizeLimit !== null ? { bundleSizeLimit } : {};
|
|
180
199
|
const scanResult = await this.performanceScannerPort.scan(input.targetPaths, thresholds);
|
|
181
200
|
if (!scanResult.passed) {
|
|
182
|
-
overrideMap.set(
|
|
201
|
+
overrideMap.set("L3-002", ValidationResult.fail(ValidatorId.create("L3-002"), [...scanResult.findings], 0));
|
|
183
202
|
}
|
|
184
203
|
}
|
|
185
204
|
}
|
|
186
205
|
|
|
187
206
|
if (this.acCoveragePolicyPort) {
|
|
188
|
-
const l3004Result = overrideMap.get(
|
|
207
|
+
const l3004Result = overrideMap.get("L3-004");
|
|
189
208
|
if (l3004Result && !l3004Result.skipped) {
|
|
190
209
|
const policyResult = await this.acCoveragePolicyPort.checkCoverage({
|
|
191
210
|
matrixFilePath: input.requirementMatrixPath,
|
|
192
211
|
});
|
|
193
212
|
if (!policyResult.passed) {
|
|
194
|
-
overrideMap.set(
|
|
195
|
-
'L3-004',
|
|
196
|
-
ValidationResult.fail(
|
|
197
|
-
ValidatorId.create('L3-004'),
|
|
198
|
-
[...policyResult.errors],
|
|
199
|
-
0,
|
|
200
|
-
),
|
|
201
|
-
);
|
|
213
|
+
overrideMap.set("L3-004", ValidationResult.fail(ValidatorId.create("L3-004"), [...policyResult.errors], 0));
|
|
202
214
|
}
|
|
203
215
|
}
|
|
204
216
|
}
|
|
@@ -206,21 +218,38 @@ export class RunL3ValidatorsUseCase {
|
|
|
206
218
|
// L3-005: AC-bound coverage(fail-closed, default-OFF)。
|
|
207
219
|
// override map に unskipped な L3-005 がある場合のみ policy を呼ぶ(L3-004 と同じ方式)。
|
|
208
220
|
if (this.acBoundCoveragePolicyPort) {
|
|
209
|
-
const l3005Result = overrideMap.get(
|
|
221
|
+
const l3005Result = overrideMap.get("L3-005");
|
|
210
222
|
if (l3005Result && !l3005Result.skipped) {
|
|
211
223
|
const policyResult = await this.acBoundCoveragePolicyPort.checkAcBoundCoverage({
|
|
212
224
|
matrixFilePath: input.requirementMatrixPath,
|
|
213
225
|
acBoundStories: input.acBoundStories ?? this.acBoundStories,
|
|
214
226
|
});
|
|
215
227
|
if (!policyResult.passed) {
|
|
216
|
-
overrideMap.set(
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
228
|
+
overrideMap.set("L3-005", ValidationResult.fail(ValidatorId.create("L3-005"), [...policyResult.errors], 0));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// WI-259 / ADR-030 §Decision.3.④: L3-006 advisory インジェクションスキャナ。
|
|
234
|
+
// 指示搭載ファイルの既知インジェクションパターンを検出し warning-only の finding として報告する。
|
|
235
|
+
// finding は必ず severity=warning のため ADR-017 集約規則(failOnWarning=false 既定)で overall PASS。
|
|
236
|
+
// blocking にしない(§4.(b): 「すり抜け=安全」の誤信頼を避ける)。default-OFF/skip 時は override しない。
|
|
237
|
+
if (this.injectionScanPolicyPort) {
|
|
238
|
+
const l3006Result = overrideMap.get("L3-006");
|
|
239
|
+
if (l3006Result && !l3006Result.skipped) {
|
|
240
|
+
const targets = await this.injectionScanPolicyPort.collect();
|
|
241
|
+
const report = this.injectionScanService.scan(targets);
|
|
242
|
+
if (report.hasFindings()) {
|
|
243
|
+
const errors: HarnessErrorLike[] = report.findings.map((finding) => ({
|
|
244
|
+
code: { value: "L3-006", toString: () => "L3-006" },
|
|
245
|
+
severity: { value: "warning", toString: () => "warning" },
|
|
246
|
+
message: `${finding.sourcePath}:${finding.lineNumber} [${finding.kind}] ${finding.message}`,
|
|
247
|
+
suggestion: finding.suggestion,
|
|
248
|
+
sourcePath: finding.sourcePath,
|
|
249
|
+
}));
|
|
250
|
+
overrideMap.set("L3-006", ValidationResult.fail(ValidatorId.create("L3-006"), errors, 0));
|
|
251
|
+
} else {
|
|
252
|
+
overrideMap.set("L3-006", ValidationResult.pass(ValidatorId.create("L3-006"), 0));
|
|
224
253
|
}
|
|
225
254
|
}
|
|
226
255
|
}
|
|
@@ -232,6 +261,6 @@ export class RunL3ValidatorsUseCase {
|
|
|
232
261
|
}
|
|
233
262
|
|
|
234
263
|
private async getProjectLanguages(): Promise<readonly string[]> {
|
|
235
|
-
return this.configPort.getProjectLanguages ? await this.configPort.getProjectLanguages() : [
|
|
264
|
+
return this.configPort.getProjectLanguages ? await this.configPort.getProjectLanguages() : ["typescript"];
|
|
236
265
|
}
|
|
237
266
|
}
|