phasegate 0.167.0 → 0.172.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/docs/ADR/021-severity-contract.md +46 -0
  3. package/package.json +1 -1
  4. package/scripts/harness/attestation/application/dto/attestation-document.ts +2 -0
  5. package/scripts/harness/attestation/application/dto/verify-attestation-output.ts +2 -0
  6. package/scripts/harness/attestation/application/mappers/attestation-record-mapper.ts +13 -0
  7. package/scripts/harness/attestation/application/ports/ac-bound-allowlist-port.ts +12 -0
  8. package/scripts/harness/attestation/application/ports/matrix-source-port.ts +16 -0
  9. package/scripts/harness/attestation/application/usecases/produce-attestation-usecase.ts +39 -0
  10. package/scripts/harness/attestation/application/usecases/verify-attestation-usecase.ts +60 -1
  11. package/scripts/harness/attestation/composition-root.ts +14 -0
  12. package/scripts/harness/attestation/domain/entities/attestation-record.ts +13 -0
  13. package/scripts/harness/attestation/domain/services/ac-bound-scope-service.ts +80 -0
  14. package/scripts/harness/attestation/infrastructure/adapters/config-ac-bound-allowlist-adapter.ts +23 -0
  15. package/scripts/harness/attestation/infrastructure/adapters/file-system-matrix-source-adapter.ts +25 -0
  16. package/scripts/harness/attestation/presentation/handlers/verify-attestation-handler.ts +1 -0
  17. package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +3 -0
  18. package/scripts/harness/config-foundation/domain/harness-config.ts +1 -0
  19. package/scripts/harness/config-foundation/domain/value-objects/l3-config.ts +4 -0
  20. package/scripts/harness/config-foundation/domain/value-objects/layers-config.ts +1 -1
  21. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +7 -0
  22. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +7 -0
  23. package/scripts/harness/harness-error/domain/errors/severity-downgrade-violation-error.ts +1 -1
  24. package/scripts/harness/nyquist-validation/application/mappers/coverage-result-mapper.ts +13 -15
  25. package/scripts/harness/nyquist-validation/application/mappers/impact-analysis-result-mapper.ts +12 -12
  26. package/scripts/harness/nyquist-validation/application/usecases/analyze-impact-usecase.ts +11 -11
  27. package/scripts/harness/nyquist-validation/application/usecases/calculate-coverage-usecase.ts +11 -11
  28. package/scripts/harness/nyquist-validation/application/usecases/check-ac-coverage-gate-usecase.ts +9 -9
  29. package/scripts/harness/nyquist-validation/domain/aggregates/requirement-test-matrix.ts +1 -1
  30. package/scripts/harness/nyquist-validation/domain/entities/story-mapping.ts +1 -1
  31. package/scripts/harness/nyquist-validation/domain/services/ac-coverage-gate-policy.ts +1 -1
  32. package/scripts/harness/nyquist-validation/domain/services/coverage-calculation-service.ts +1 -1
  33. package/scripts/harness/nyquist-validation/domain/services/impact-analysis-service.ts +2 -2
  34. package/scripts/harness/nyquist-validation/domain/services/matrix-validation-service.ts +5 -5
  35. package/scripts/harness/nyquist-validation/domain/value-objects/impact-analysis-result.ts +1 -1
  36. package/scripts/harness/nyquist-validation/infrastructure/adapters/ajv-json-schema-validator-adapter.ts +3 -3
  37. package/scripts/harness/nyquist-validation/infrastructure/adapters/config-foundation-coverage-threshold-adapter.ts +3 -3
  38. package/scripts/harness/validator-system/application/dto/run-l3-validators-input.ts +2 -0
  39. package/scripts/harness/validator-system/application/use-cases/run-l1-validators-usecase.ts +1 -1
  40. package/scripts/harness/validator-system/application/use-cases/run-l2-validators-usecase.ts +3 -3
  41. package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +33 -3
  42. package/scripts/harness/validator-system/application/use-cases/run-l4-validators-usecase.ts +3 -3
  43. package/scripts/harness/validator-system/application/use-cases/run-quick-mode-usecase.ts +3 -3
  44. package/scripts/harness/validator-system/composition-root.ts +12 -0
  45. package/scripts/harness/validator-system/domain/ports/ac-bound-coverage-policy-port.ts +22 -0
  46. package/scripts/harness/validator-system/domain/services/validator-execution-service.ts +1 -1
  47. package/scripts/harness/validator-system/domain/services/validator-registry.ts +2 -2
  48. package/scripts/harness/validator-system/domain/value-objects/layer-config.ts +1 -1
  49. package/scripts/harness/validator-system/domain/value-objects/validation-result.ts +1 -1
  50. package/scripts/harness/validator-system/domain/value-objects/validator-definition.ts +2 -2
  51. package/scripts/harness/validator-system/domain/value-objects/validator-id.ts +2 -0
  52. package/scripts/harness/validator-system/infrastructure/adapters/json-coverage-report-adapter.ts +1 -1
  53. package/scripts/harness/validator-system/infrastructure/adapters/nyquist-ac-bound-coverage-policy-adapter.ts +118 -0
  54. 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
  * LayerConfig 値オブジェクト
6
6
  * HarnessConfigV2から注入されるL2/L3/L4の実行設定VO
7
7
  */
8
- import { ValidatorId } from './validator-id.js';
8
+ import type { ValidatorId } from './validator-id.js';
9
9
 
10
10
  export interface LayerConfigProps {
11
11
  readonly layer: 'L2' | 'L3' | 'L4';
@@ -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',
@@ -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['total'];
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
+ }
@@ -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['validatorId'] === 'string' &&
25
- typeof o['passed'] === 'boolean' &&
26
- Array.isArray(o['errors']) &&
27
- typeof o['durationMs'] === 'number'
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