phasegate 0.167.0 → 0.171.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 +44 -0
- package/docs/ADR/021-severity-contract.md +46 -0
- package/package.json +1 -1
- package/scripts/harness/attestation/application/dto/attestation-document.ts +2 -0
- package/scripts/harness/attestation/application/dto/verify-attestation-output.ts +2 -0
- package/scripts/harness/attestation/application/mappers/attestation-record-mapper.ts +13 -0
- package/scripts/harness/attestation/application/ports/ac-bound-allowlist-port.ts +12 -0
- package/scripts/harness/attestation/application/ports/matrix-source-port.ts +16 -0
- package/scripts/harness/attestation/application/usecases/produce-attestation-usecase.ts +39 -0
- package/scripts/harness/attestation/application/usecases/verify-attestation-usecase.ts +60 -1
- package/scripts/harness/attestation/composition-root.ts +14 -0
- package/scripts/harness/attestation/domain/entities/attestation-record.ts +13 -0
- package/scripts/harness/attestation/domain/services/ac-bound-scope-service.ts +80 -0
- package/scripts/harness/attestation/infrastructure/adapters/config-ac-bound-allowlist-adapter.ts +23 -0
- package/scripts/harness/attestation/infrastructure/adapters/file-system-matrix-source-adapter.ts +25 -0
- package/scripts/harness/attestation/presentation/handlers/verify-attestation-handler.ts +1 -0
- package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +3 -0
- package/scripts/harness/config-foundation/domain/harness-config.ts +1 -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/infrastructure/schemas/harness-config-v2.schema.json +7 -0
- package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +7 -0
- package/scripts/harness/harness-error/domain/errors/severity-downgrade-violation-error.ts +1 -1
- package/scripts/harness/nyquist-validation/application/mappers/coverage-result-mapper.ts +13 -15
- package/scripts/harness/nyquist-validation/application/mappers/impact-analysis-result-mapper.ts +12 -12
- package/scripts/harness/nyquist-validation/application/usecases/analyze-impact-usecase.ts +11 -11
- package/scripts/harness/nyquist-validation/application/usecases/calculate-coverage-usecase.ts +11 -11
- package/scripts/harness/nyquist-validation/application/usecases/check-ac-coverage-gate-usecase.ts +9 -9
- package/scripts/harness/nyquist-validation/domain/aggregates/requirement-test-matrix.ts +1 -1
- package/scripts/harness/nyquist-validation/domain/entities/story-mapping.ts +1 -1
- package/scripts/harness/nyquist-validation/domain/services/ac-coverage-gate-policy.ts +1 -1
- package/scripts/harness/nyquist-validation/domain/services/coverage-calculation-service.ts +1 -1
- package/scripts/harness/nyquist-validation/domain/services/impact-analysis-service.ts +2 -2
- package/scripts/harness/nyquist-validation/domain/services/matrix-validation-service.ts +5 -5
- package/scripts/harness/nyquist-validation/domain/value-objects/impact-analysis-result.ts +1 -1
- package/scripts/harness/nyquist-validation/infrastructure/adapters/ajv-json-schema-validator-adapter.ts +3 -3
- package/scripts/harness/nyquist-validation/infrastructure/adapters/config-foundation-coverage-threshold-adapter.ts +3 -3
- package/scripts/harness/validator-system/application/dto/run-l3-validators-input.ts +2 -0
- package/scripts/harness/validator-system/application/use-cases/run-l1-validators-usecase.ts +1 -1
- package/scripts/harness/validator-system/application/use-cases/run-l2-validators-usecase.ts +3 -3
- package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +33 -3
- package/scripts/harness/validator-system/application/use-cases/run-l4-validators-usecase.ts +3 -3
- package/scripts/harness/validator-system/application/use-cases/run-quick-mode-usecase.ts +3 -3
- package/scripts/harness/validator-system/composition-root.ts +12 -0
- package/scripts/harness/validator-system/domain/ports/ac-bound-coverage-policy-port.ts +22 -0
- package/scripts/harness/validator-system/domain/services/validator-execution-service.ts +1 -1
- package/scripts/harness/validator-system/domain/services/validator-registry.ts +2 -2
- package/scripts/harness/validator-system/domain/value-objects/layer-config.ts +1 -1
- package/scripts/harness/validator-system/domain/value-objects/validation-result.ts +1 -1
- package/scripts/harness/validator-system/domain/value-objects/validator-definition.ts +2 -2
- package/scripts/harness/validator-system/domain/value-objects/validator-id.ts +2 -0
- package/scripts/harness/validator-system/infrastructure/adapters/json-coverage-report-adapter.ts +1 -1
- package/scripts/harness/validator-system/infrastructure/adapters/nyquist-ac-bound-coverage-policy-adapter.ts +118 -0
- package/scripts/harness/validator-system/presentation/handlers/report-validation-results-handler.ts +4 -4
|
@@ -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 { NyquistAcBoundCoveragePolicyAdapter } from './infrastructure/adapters/nyquist-ac-bound-coverage-policy-adapter.js';
|
|
32
33
|
import { JsonCoverageReportAdapter } from './infrastructure/adapters/json-coverage-report-adapter.js';
|
|
33
34
|
import { BiomeAstTestQualityAnalyzerAdapter } from './infrastructure/adapters/biome-ast-test-quality-analyzer-adapter.js';
|
|
34
35
|
import { FileSystemSecurityPatternScannerAdapter } from './infrastructure/adapters/file-system-security-pattern-scanner-adapter.js';
|
|
@@ -112,6 +113,9 @@ export function buildDefaultRegistry(): ValidatorRegistry {
|
|
|
112
113
|
createDef('L3-002', 'L3', 'strictOnly'),
|
|
113
114
|
createDef('L3-003', 'L3', 'always'),
|
|
114
115
|
createDef('L3-004', 'L3', 'always', 'AcCoveragePolicyPort'),
|
|
116
|
+
// WI-227 / H16-03: L3-005 (ac-bound-coverage) は registry には登録するが
|
|
117
|
+
// default-OFF(DEFAULT_CONFIG.layers.L3.validators に含めない)。fail-closed / opt-in。
|
|
118
|
+
createDef('L3-005', 'L3', 'always', 'AcBoundCoveragePolicyPort'),
|
|
115
119
|
createDef('L4-001', 'L4', 'always'),
|
|
116
120
|
createDef('L4-002', 'L4', 'always'),
|
|
117
121
|
createDef('L4-003', 'L4', 'strictOnly'),
|
|
@@ -154,6 +158,8 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
|
|
|
154
158
|
const phaseGatePolicyPort = new PhaseDependencyPhaseGatePolicyAdapter();
|
|
155
159
|
const metadataPolicyPort = new TraceabilityMetadataPolicyAdapter();
|
|
156
160
|
const acCoveragePolicyPort = new NyquistAcCoveragePolicyAdapter();
|
|
161
|
+
// WI-227 / H16-03: L3-005 (ac-bound-coverage, fail-closed, default-OFF) 用の adapter。
|
|
162
|
+
const acBoundCoveragePolicyPort = new NyquistAcBoundCoveragePolicyAdapter();
|
|
157
163
|
// L3-003: テストカバレッジレポート(vitest --coverage の json-summary 出力)を読み取る。
|
|
158
164
|
// 未配線だと L3-003 は装飾的な pass になり 90% 閾値が形骸化するため必ず配線する。
|
|
159
165
|
const coverageReportPort = new JsonCoverageReportAdapter(
|
|
@@ -193,15 +199,21 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
|
|
|
193
199
|
contractTraceabilityPolicyPort,
|
|
194
200
|
});
|
|
195
201
|
|
|
202
|
+
// WI-227 / H16-03: L3-005 のスコープ対象 story-id を config から取得(既定 [])。
|
|
203
|
+
const acBoundStories =
|
|
204
|
+
(configData.layers?.L3 as { acBoundStories?: readonly string[] } | undefined)?.acBoundStories ?? [];
|
|
205
|
+
|
|
196
206
|
const runL3ValidatorsUseCase = new RunL3ValidatorsUseCase({
|
|
197
207
|
validatorRegistry: registry,
|
|
198
208
|
validatorExecutionService: executionService,
|
|
199
209
|
validatorConfigPort: configPort,
|
|
200
210
|
contractMapper,
|
|
201
211
|
acCoveragePolicyPort,
|
|
212
|
+
acBoundCoveragePolicyPort,
|
|
202
213
|
coverageReportPort,
|
|
203
214
|
securityScannerPort,
|
|
204
215
|
performanceScannerPort,
|
|
216
|
+
acBoundStories,
|
|
205
217
|
});
|
|
206
218
|
|
|
207
219
|
const markdownDesignDocumentPort = new MarkdownDesignDocumentAdapter(docsRoot);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @layer domain
|
|
3
|
+
* @unit validator-system
|
|
4
|
+
* @work-item-id WI-227
|
|
5
|
+
*
|
|
6
|
+
* AcBoundCoveragePolicyPort — L3-005(AC-bound coverage, fail-closed, default-OFF)
|
|
7
|
+
*
|
|
8
|
+
* L3-004(story-level の AC 網羅ゲート)とは独立した per-AC binding ゲート。
|
|
9
|
+
* `acBoundStories`(スコープ)内の各 story について、全 linked AC が
|
|
10
|
+
* ≥1 の `binding:"ac"` テスト参照(fileFallbackOnly===0)を持つことを検査する。
|
|
11
|
+
*/
|
|
12
|
+
import type { HarnessErrorLike } from '../value-objects/validation-result.js';
|
|
13
|
+
|
|
14
|
+
export interface AcBoundCoveragePolicyPort {
|
|
15
|
+
checkAcBoundCoverage(context: {
|
|
16
|
+
matrixFilePath?: string;
|
|
17
|
+
acBoundStories: readonly string[];
|
|
18
|
+
}): Promise<{
|
|
19
|
+
passed: boolean;
|
|
20
|
+
errors: readonly HarnessErrorLike[];
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* ValidatorExecutionService ドメインサービス
|
|
6
6
|
* 指定されたValidatorDefinition[]を順次実行し、ValidationResult[]を集約
|
|
7
7
|
*/
|
|
8
|
-
import { ValidatorDefinition } from '../value-objects/validator-definition.js';
|
|
8
|
+
import type { ValidatorDefinition } from '../value-objects/validator-definition.js';
|
|
9
9
|
import { ValidationResult, type HarnessErrorLike } from '../value-objects/validation-result.js';
|
|
10
10
|
import { LayerConfig } from '../value-objects/layer-config.js';
|
|
11
11
|
import type { PhaseGatePolicyPort } from '../ports/phase-gate-policy-port.js';
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* ValidatorRegistry ドメインサービス
|
|
6
6
|
* L2-L4バリデータ定義のカタログ管理と選択実行インターフェース
|
|
7
7
|
*/
|
|
8
|
-
import { ValidatorId } from '../value-objects/validator-id.js';
|
|
9
|
-
import { ValidatorDefinition } from '../value-objects/validator-definition.js';
|
|
8
|
+
import type { ValidatorId } from '../value-objects/validator-id.js';
|
|
9
|
+
import type { ValidatorDefinition } from '../value-objects/validator-definition.js';
|
|
10
10
|
|
|
11
11
|
export class UnknownValidatorError extends Error {
|
|
12
12
|
readonly validatorId: string;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* ValidationResult 値オブジェクト
|
|
6
6
|
* バリデータ実行結果のスナップショット(不変)
|
|
7
7
|
*/
|
|
8
|
-
import { ValidatorId } from './validator-id.js';
|
|
8
|
+
import type { ValidatorId } from './validator-id.js';
|
|
9
9
|
|
|
10
10
|
/** HarnessError の最小互換型(harness-error Unit の HarnessError との疎結合) */
|
|
11
11
|
export interface HarnessErrorLike {
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* ValidatorDefinition 値オブジェクト
|
|
6
6
|
* 個々のバリデータの不変定義を保持する
|
|
7
7
|
*/
|
|
8
|
-
import { ValidatorId } from './validator-id.js';
|
|
9
|
-
import { ValidationRule } from './validation-rule.js';
|
|
8
|
+
import type { ValidatorId } from './validator-id.js';
|
|
9
|
+
import type { ValidationRule } from './validation-rule.js';
|
|
10
10
|
|
|
11
11
|
export interface ValidatorDefinitionProps {
|
|
12
12
|
readonly validatorId: ValidatorId;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* WI-132/WI-133/WI-136/WI-137/WI-138 で L2-015 を追加
|
|
11
11
|
* WI-156 で L4-006 を追加
|
|
12
12
|
* WI-222 (HF2-05) で L4-007(ac-level-traceability, default-OFF advisory)を追加
|
|
13
|
+
* WI-227 (H16-03) で L3-005(ac-bound-coverage, default-OFF fail-closed)を追加
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
export class InvalidValidatorIdError extends Error {
|
|
@@ -37,6 +38,7 @@ const VALIDATOR_NAME_MAP: Record<string, string> = {
|
|
|
37
38
|
'L3-002': 'performance',
|
|
38
39
|
'L3-003': 'coverage',
|
|
39
40
|
'L3-004': 'nyquist',
|
|
41
|
+
'L3-005': 'ac-bound-coverage',
|
|
40
42
|
'L4-001': 'drift-detect',
|
|
41
43
|
'L4-002': 'consistency-check',
|
|
42
44
|
'L4-003': 'dead-code',
|
package/scripts/harness/validator-system/infrastructure/adapters/json-coverage-report-adapter.ts
CHANGED
|
@@ -33,7 +33,7 @@ export class JsonCoverageReportAdapter implements CoverageReportPort {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
const data = JSON.parse(raw) as Record<string, { lines?: { pct?: number } }>;
|
|
36
|
-
const total = data
|
|
36
|
+
const total = data.total;
|
|
37
37
|
const overallCoverage = total?.lines?.pct ?? 0;
|
|
38
38
|
|
|
39
39
|
const perFileCoverage: { filePath: string; coverage: number }[] = [];
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @layer infrastructure
|
|
3
|
+
* @unit validator-system
|
|
4
|
+
* @work-item-id WI-227
|
|
5
|
+
*
|
|
6
|
+
* NyquistAcBoundCoveragePolicyAdapter — AcBoundCoveragePolicyPort 実装(L3-005)
|
|
7
|
+
*
|
|
8
|
+
* FAIL-CLOSED: matrix の不在・parse 不能・例外はいずれも passed=false(不合格)として扱う。
|
|
9
|
+
* L4-007(advisory / fail-open)とは対照的に、L3-005 は CI を落とす blocking tier である。
|
|
10
|
+
*
|
|
11
|
+
* 判定: `acBoundStories`(スコープ)内の各 storyId について、matrix 上の全 linked AC を走査し、
|
|
12
|
+
* testReferences に `binding:"ac"` を 1 件も持たない AC(fileFallbackOnly)が 1 つでもあれば FAIL。
|
|
13
|
+
* スコープ外 story は完全に無視する。スコープが空なら検査対象が無いため PASS。
|
|
14
|
+
*
|
|
15
|
+
* matrix path は config(layers.L3.requirementMatrixPath)から供給される。CI では
|
|
16
|
+
* phasegate:generate-matrix で事前生成された最新マトリクスを READ する(L3-004 と同じ規約)。
|
|
17
|
+
*/
|
|
18
|
+
import { readFile } from 'node:fs/promises';
|
|
19
|
+
import { isAbsolute, join } from 'node:path';
|
|
20
|
+
import type { AcBoundCoveragePolicyPort } from '../../domain/ports/ac-bound-coverage-policy-port.js';
|
|
21
|
+
import type { HarnessErrorLike } from '../../domain/value-objects/validation-result.js';
|
|
22
|
+
|
|
23
|
+
const DEFAULT_MATRIX_PATH = '.harness/requirement-test-matrix.json';
|
|
24
|
+
|
|
25
|
+
interface MatrixTestReference {
|
|
26
|
+
readonly binding?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface MatrixAcMapping {
|
|
30
|
+
readonly acId?: string;
|
|
31
|
+
readonly testReferences?: readonly MatrixTestReference[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface MatrixStory {
|
|
35
|
+
readonly storyId?: string;
|
|
36
|
+
readonly storyMappings?: readonly MatrixAcMapping[];
|
|
37
|
+
readonly acMappings?: readonly MatrixAcMapping[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function toL3005Error(message: string, suggestion: string): HarnessErrorLike {
|
|
41
|
+
return {
|
|
42
|
+
code: { value: 'L3-005', toString: () => 'L3-005' },
|
|
43
|
+
severity: { value: 'error', toString: () => 'error' },
|
|
44
|
+
message,
|
|
45
|
+
suggestion,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export class NyquistAcBoundCoveragePolicyAdapter implements AcBoundCoveragePolicyPort {
|
|
50
|
+
async checkAcBoundCoverage(context: {
|
|
51
|
+
matrixFilePath?: string;
|
|
52
|
+
acBoundStories: readonly string[];
|
|
53
|
+
}): Promise<{ passed: boolean; errors: readonly HarnessErrorLike[] }> {
|
|
54
|
+
const scope = new Set(context.acBoundStories);
|
|
55
|
+
// スコープが空 = 検査すべき in-scope AC が存在しない → PASS
|
|
56
|
+
if (scope.size === 0) {
|
|
57
|
+
return { passed: true, errors: [] };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const rootDir = process.cwd();
|
|
61
|
+
const relativeOrAbsolute =
|
|
62
|
+
context.matrixFilePath && context.matrixFilePath.length > 0 ? context.matrixFilePath : DEFAULT_MATRIX_PATH;
|
|
63
|
+
const matrixFilePath = isAbsolute(relativeOrAbsolute) ? relativeOrAbsolute : join(rootDir, relativeOrAbsolute);
|
|
64
|
+
|
|
65
|
+
let parsed: unknown;
|
|
66
|
+
try {
|
|
67
|
+
const raw = await readFile(matrixFilePath, 'utf8');
|
|
68
|
+
parsed = JSON.parse(raw);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71
|
+
// FAIL-CLOSED: 不在・parse 不能はいずれも不合格
|
|
72
|
+
return {
|
|
73
|
+
passed: false,
|
|
74
|
+
errors: [
|
|
75
|
+
toL3005Error(
|
|
76
|
+
`AC-bound マトリクスを読み込めません(L3-005 は fail-closed): ${relativeOrAbsolute}: ${message}`,
|
|
77
|
+
`${relativeOrAbsolute} を生成してください(phasegate:generate-matrix)。パスは config の layers.L3.requirementMatrixPath で変更できます`,
|
|
78
|
+
),
|
|
79
|
+
],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const stories = this.extractStories(parsed);
|
|
84
|
+
const errors: HarnessErrorLike[] = [];
|
|
85
|
+
|
|
86
|
+
for (const story of stories) {
|
|
87
|
+
const storyId = story.storyId ?? '';
|
|
88
|
+
if (!scope.has(storyId)) continue; // スコープ外は無視
|
|
89
|
+
|
|
90
|
+
const acMappings = story.storyMappings ?? story.acMappings ?? [];
|
|
91
|
+
for (const ac of acMappings) {
|
|
92
|
+
const refs = ac.testReferences ?? [];
|
|
93
|
+
if (refs.length === 0) continue; // 未リンク AC は L3-004 の責務。L3-005 は ac-binding 有無のみ判定
|
|
94
|
+
const hasAcBound = refs.some((ref) => ref.binding === 'ac');
|
|
95
|
+
if (!hasAcBound) {
|
|
96
|
+
errors.push(
|
|
97
|
+
toL3005Error(
|
|
98
|
+
`AC-bound coverage 不足: ${storyId}.${ac.acId ?? '?'} は binding:"ac" のテスト参照を持ちません(fileFallbackOnly)`,
|
|
99
|
+
`${storyId}.${ac.acId ?? '?'} を検証するテストに @ac タグ(絶対 ${storyId}-N / 相対 AC-N)を付与してください`,
|
|
100
|
+
),
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (errors.length === 0) {
|
|
107
|
+
return { passed: true, errors: [] };
|
|
108
|
+
}
|
|
109
|
+
return { passed: false, errors };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private extractStories(parsed: unknown): readonly MatrixStory[] {
|
|
113
|
+
if (typeof parsed !== 'object' || parsed === null) return [];
|
|
114
|
+
const obj = parsed as { stories?: unknown; storyMappings?: unknown };
|
|
115
|
+
const raw = Array.isArray(obj.stories) ? obj.stories : Array.isArray(obj.storyMappings) ? obj.storyMappings : [];
|
|
116
|
+
return raw as readonly MatrixStory[];
|
|
117
|
+
}
|
|
118
|
+
}
|
package/scripts/harness/validator-system/presentation/handlers/report-validation-results-handler.ts
CHANGED
|
@@ -21,10 +21,10 @@ export function isValidationResultContract(obj: unknown): obj is ValidationResul
|
|
|
21
21
|
if (typeof obj !== 'object' || obj === null) return false;
|
|
22
22
|
const o = obj as Record<string, unknown>;
|
|
23
23
|
return (
|
|
24
|
-
typeof o
|
|
25
|
-
typeof o
|
|
26
|
-
Array.isArray(o
|
|
27
|
-
typeof o
|
|
24
|
+
typeof o.validatorId === 'string' &&
|
|
25
|
+
typeof o.passed === 'boolean' &&
|
|
26
|
+
Array.isArray(o.errors) &&
|
|
27
|
+
typeof o.durationMs === 'number'
|
|
28
28
|
);
|
|
29
29
|
}
|
|
30
30
|
|