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
@@ -15,6 +15,7 @@ export function toValidatorSystemConfig(resolvedConfig: HarnessConfigV2 | undefi
15
15
  performance: 'L3-002',
16
16
  coverage: 'L3-003',
17
17
  nyquist: 'L3-004',
18
+ 'ac-bound-coverage': 'L3-005',
18
19
  }, /^L3-\d{3}$/);
19
20
  const l4Validators = normalizeValidators(resolvedConfig.layers.L4.validators, {
20
21
  'drift-detect': 'L4-001',
@@ -49,6 +50,8 @@ export function toValidatorSystemConfig(resolvedConfig: HarnessConfigV2 | undefi
49
50
  validators: l3Validators.length > 0 ? l3Validators : ['L3-001', 'L3-002', 'L3-003', 'L3-004'],
50
51
  coverageThreshold: resolvedConfig.layers.L3.coverageThreshold,
51
52
  requirementMatrixPath: resolvedConfig.layers.L3.requirementMatrixPath,
53
+ // WI-227 / H16-03: L3-005 のスコープ(additive-safe。未設定は [])
54
+ acBoundStories: resolvedConfig.layers.L3.acBoundStories ?? [],
52
55
  },
53
56
  L4: {
54
57
  enabled: resolvedConfig.layers.L4.enabled,
@@ -83,6 +83,7 @@ export interface HarnessConfigResolvedDocument {
83
83
  validators: string[];
84
84
  coverageThreshold: number;
85
85
  requirementMatrixPath?: string;
86
+ acBoundStories?: readonly string[];
86
87
  };
87
88
  L4: {
88
89
  enabled: boolean;
@@ -11,6 +11,7 @@ interface L3ConfigProps {
11
11
  readonly validators: string[];
12
12
  readonly coverageThreshold: number;
13
13
  readonly requirementMatrixPath?: string;
14
+ readonly acBoundStories?: readonly string[];
14
15
  }
15
16
 
16
17
  export class L3Config {
@@ -18,6 +19,7 @@ export class L3Config {
18
19
  readonly validators: readonly string[];
19
20
  readonly coverageThreshold: number;
20
21
  readonly requirementMatrixPath?: string;
22
+ readonly acBoundStories?: readonly string[];
21
23
 
22
24
  constructor(props: L3ConfigProps) {
23
25
  if (props.coverageThreshold < 0) {
@@ -40,6 +42,7 @@ export class L3Config {
40
42
  this.validators = Object.freeze([...props.validators]);
41
43
  this.coverageThreshold = props.coverageThreshold;
42
44
  this.requirementMatrixPath = props.requirementMatrixPath;
45
+ this.acBoundStories = props.acBoundStories ? Object.freeze([...props.acBoundStories]) : undefined;
43
46
  Object.freeze(this);
44
47
  }
45
48
 
@@ -48,6 +51,7 @@ export class L3Config {
48
51
  validators: string[];
49
52
  coverageThreshold: number;
50
53
  requirementMatrixPath?: string;
54
+ acBoundStories?: readonly string[];
51
55
  }): L3Config {
52
56
  return new L3Config(raw);
53
57
  }
@@ -13,7 +13,7 @@ import { L4Config } from './l4-config.js';
13
13
  interface ResolvedLayersDocument {
14
14
  L1: { enabled: boolean; rules: Record<string, string> };
15
15
  L2: { enabled: boolean; validators: string[] };
16
- L3: { enabled: boolean; validators: string[]; coverageThreshold: number; requirementMatrixPath?: string };
16
+ L3: { enabled: boolean; validators: string[]; coverageThreshold: number; requirementMatrixPath?: string; acBoundStories?: readonly string[] };
17
17
  L4: { enabled: boolean; validators: string[]; schedule: string };
18
18
  }
19
19
 
@@ -130,6 +130,13 @@
130
130
  "requirementMatrixPath": {
131
131
  "type": "string",
132
132
  "minLength": 1
133
+ },
134
+ "acBoundStories": {
135
+ "type": "array",
136
+ "items": {
137
+ "type": "string"
138
+ },
139
+ "uniqueItems": true
133
140
  }
134
141
  }
135
142
  },
@@ -140,6 +140,13 @@
140
140
  "requirementMatrixPath": {
141
141
  "type": "string",
142
142
  "minLength": 1
143
+ },
144
+ "acBoundStories": {
145
+ "type": "array",
146
+ "items": {
147
+ "type": "string"
148
+ },
149
+ "uniqueItems": true
143
150
  }
144
151
  }
145
152
  },
@@ -6,7 +6,7 @@ import { HarnessErrorDomainError } from './harness-error-domain-error.js';
6
6
  export class SeverityDowngradeViolationError extends HarnessErrorDomainError {
7
7
  constructor(requested: string, defaultSeverity: string) {
8
8
  super(
9
- `severity の格下げは禁止されています: default="${defaultSeverity}" に対して "${requested}" が要求されました。`
9
+ `severity の格下げは禁止されています: default="${defaultSeverity}" に対して "${requested}" が要求されました。根拠: ADR-021`
10
10
  );
11
11
  this.name = 'SeverityDowngradeViolationError';
12
12
  }
@@ -7,19 +7,17 @@
7
7
  import type { CoverageResult } from '../../domain/value-objects/coverage-result.js';
8
8
  import type { CalculateCoverageOutput } from '../dto/calculate-coverage-output.js';
9
9
 
10
- export class CoverageResultMapper {
11
- static toOutput(
12
- result: CoverageResult,
13
- threshold: { active: number } | null
14
- ): CalculateCoverageOutput {
15
- return {
16
- coveredAcCount: result.coveredAcCount,
17
- totalAcCount: result.totalAcCount,
18
- ratePercent: result.toPercentage(),
19
- uncoveredAcIds: result.uncoveredAcIds,
20
- threshold: threshold?.active ?? null,
21
- meetsThreshold:
22
- threshold !== null ? result.meetsThreshold(threshold.active) : null,
23
- };
24
- }
10
+ export function toCalculateCoverageOutput(
11
+ result: CoverageResult,
12
+ threshold: { active: number } | null
13
+ ): CalculateCoverageOutput {
14
+ return {
15
+ coveredAcCount: result.coveredAcCount,
16
+ totalAcCount: result.totalAcCount,
17
+ ratePercent: result.toPercentage(),
18
+ uncoveredAcIds: result.uncoveredAcIds,
19
+ threshold: threshold?.active ?? null,
20
+ meetsThreshold:
21
+ threshold !== null ? result.meetsThreshold(threshold.active) : null,
22
+ };
25
23
  }
@@ -7,16 +7,16 @@
7
7
  import type { ImpactAnalysisResult } from '../../domain/value-objects/impact-analysis-result.js';
8
8
  import type { AnalyzeImpactOutput } from '../dto/analyze-impact-output.js';
9
9
 
10
- export class ImpactAnalysisResultMapper {
11
- static toOutput(result: ImpactAnalysisResult): AnalyzeImpactOutput {
12
- return {
13
- storyId: result.storyId,
14
- directTests: result.directTests.map((ref) => ({
15
- filePath: ref.filePath,
16
- testType: ref.testType,
17
- })),
18
- directMappingOnly: true,
19
- found: !result.isEmpty(),
20
- };
21
- }
10
+ export function toAnalyzeImpactOutput(
11
+ result: ImpactAnalysisResult
12
+ ): AnalyzeImpactOutput {
13
+ return {
14
+ storyId: result.storyId,
15
+ directTests: result.directTests.map((ref) => ({
16
+ filePath: ref.filePath,
17
+ testType: ref.testType,
18
+ })),
19
+ directMappingOnly: true,
20
+ found: !result.isEmpty(),
21
+ };
22
22
  }
@@ -12,7 +12,7 @@ import { RequirementTestMatrix } from '../../domain/aggregates/requirement-test-
12
12
  import { StoryMapping } from '../../domain/entities/story-mapping.js';
13
13
  import { AcMapping } from '../../domain/value-objects/ac-mapping.js';
14
14
  import { TestReference } from '../../domain/value-objects/test-reference.js';
15
- import { ImpactAnalysisResultMapper } from '../mappers/impact-analysis-result-mapper.js';
15
+ import { toAnalyzeImpactOutput } from '../mappers/impact-analysis-result-mapper.js';
16
16
  import type { AnalyzeImpactInput } from '../dto/analyze-impact-input.js';
17
17
  import type { AnalyzeImpactOutput } from '../dto/analyze-impact-output.js';
18
18
 
@@ -25,29 +25,29 @@ export interface AnalyzeImpactUseCaseDeps {
25
25
 
26
26
  function buildMatrix(data: unknown): RequirementTestMatrix {
27
27
  const obj = data as Record<string, unknown>;
28
- const rawStories = Array.isArray(obj['stories']) ? obj['stories'] : (Array.isArray(obj['storyMappings']) ? obj['storyMappings'] : []);
28
+ const rawStories = Array.isArray(obj.stories) ? obj.stories : (Array.isArray(obj.storyMappings) ? obj.storyMappings : []);
29
29
 
30
30
  const storyMappings = (rawStories as unknown[]).map((s) => {
31
31
  const story = s as Record<string, unknown>;
32
- const rawAcMappings = Array.isArray(story['storyMappings'])
33
- ? story['storyMappings']
34
- : (Array.isArray(story['acMappings']) ? story['acMappings'] : []);
32
+ const rawAcMappings = Array.isArray(story.storyMappings)
33
+ ? story.storyMappings
34
+ : (Array.isArray(story.acMappings) ? story.acMappings : []);
35
35
 
36
36
  const acMappings = (rawAcMappings as unknown[]).map((a) => {
37
37
  const acm = a as Record<string, unknown>;
38
- const rawRefs = Array.isArray(acm['testReferences']) ? acm['testReferences'] : [];
38
+ const rawRefs = Array.isArray(acm.testReferences) ? acm.testReferences : [];
39
39
  const testReferences = (rawRefs as unknown[]).map((r) => {
40
40
  const ref = r as Record<string, unknown>;
41
41
  return TestReference.create({
42
- filePath: String(ref['filePath'] ?? ''),
43
- testType: String(ref['testType'] ?? ''),
42
+ filePath: String(ref.filePath ?? ''),
43
+ testType: String(ref.testType ?? ''),
44
44
  });
45
45
  });
46
- return AcMapping.create({ acId: String(acm['acId'] ?? ''), testReferences });
46
+ return AcMapping.create({ acId: String(acm.acId ?? ''), testReferences });
47
47
  });
48
48
 
49
49
  return StoryMapping.create({
50
- storyId: String(story['storyId'] ?? ''),
50
+ storyId: String(story.storyId ?? ''),
51
51
  acMappings,
52
52
  });
53
53
  });
@@ -77,6 +77,6 @@ export class AnalyzeImpactUseCase {
77
77
  const matrix = buildMatrix(rawData);
78
78
  const result = this.impactAnalysisService.analyze(matrix, input.storyId);
79
79
 
80
- return ImpactAnalysisResultMapper.toOutput(result);
80
+ return toAnalyzeImpactOutput(result);
81
81
  }
82
82
  }
@@ -13,7 +13,7 @@ import { RequirementTestMatrix } from '../../domain/aggregates/requirement-test-
13
13
  import { StoryMapping } from '../../domain/entities/story-mapping.js';
14
14
  import { AcMapping } from '../../domain/value-objects/ac-mapping.js';
15
15
  import { TestReference } from '../../domain/value-objects/test-reference.js';
16
- import { CoverageResultMapper } from '../mappers/coverage-result-mapper.js';
16
+ import { toCalculateCoverageOutput } from '../mappers/coverage-result-mapper.js';
17
17
  import type { CalculateCoverageInput } from '../dto/calculate-coverage-input.js';
18
18
  import type { CalculateCoverageOutput } from '../dto/calculate-coverage-output.js';
19
19
 
@@ -27,29 +27,29 @@ export interface CalculateCoverageUseCaseDeps {
27
27
 
28
28
  function buildMatrix(data: unknown): RequirementTestMatrix {
29
29
  const obj = data as Record<string, unknown>;
30
- const rawStories = Array.isArray(obj['stories']) ? obj['stories'] : (Array.isArray(obj['storyMappings']) ? obj['storyMappings'] : []);
30
+ const rawStories = Array.isArray(obj.stories) ? obj.stories : (Array.isArray(obj.storyMappings) ? obj.storyMappings : []);
31
31
 
32
32
  const storyMappings = (rawStories as unknown[]).map((s) => {
33
33
  const story = s as Record<string, unknown>;
34
- const rawAcMappings = Array.isArray(story['storyMappings'])
35
- ? story['storyMappings']
36
- : (Array.isArray(story['acMappings']) ? story['acMappings'] : []);
34
+ const rawAcMappings = Array.isArray(story.storyMappings)
35
+ ? story.storyMappings
36
+ : (Array.isArray(story.acMappings) ? story.acMappings : []);
37
37
 
38
38
  const acMappings = (rawAcMappings as unknown[]).map((a) => {
39
39
  const acm = a as Record<string, unknown>;
40
- const rawRefs = Array.isArray(acm['testReferences']) ? acm['testReferences'] : [];
40
+ const rawRefs = Array.isArray(acm.testReferences) ? acm.testReferences : [];
41
41
  const testReferences = (rawRefs as unknown[]).map((r) => {
42
42
  const ref = r as Record<string, unknown>;
43
43
  return TestReference.create({
44
- filePath: String(ref['filePath'] ?? ''),
45
- testType: String(ref['testType'] ?? ''),
44
+ filePath: String(ref.filePath ?? ''),
45
+ testType: String(ref.testType ?? ''),
46
46
  });
47
47
  });
48
- return AcMapping.create({ acId: String(acm['acId'] ?? ''), testReferences });
48
+ return AcMapping.create({ acId: String(acm.acId ?? ''), testReferences });
49
49
  });
50
50
 
51
51
  return StoryMapping.create({
52
- storyId: String(story['storyId'] ?? ''),
52
+ storyId: String(story.storyId ?? ''),
53
53
  acMappings,
54
54
  });
55
55
  });
@@ -86,6 +86,6 @@ export class CalculateCoverageUseCase {
86
86
  threshold = await this.coverageThresholdPort.getThreshold();
87
87
  }
88
88
 
89
- return CoverageResultMapper.toOutput(coverageResult, threshold);
89
+ return toCalculateCoverageOutput(coverageResult, threshold);
90
90
  }
91
91
  }
@@ -24,29 +24,29 @@ export interface CheckAcCoverageGateUseCaseDeps {
24
24
 
25
25
  function buildMatrix(data: unknown): RequirementTestMatrix {
26
26
  const obj = data as Record<string, unknown>;
27
- const rawStories = Array.isArray(obj['stories']) ? obj['stories'] : (Array.isArray(obj['storyMappings']) ? obj['storyMappings'] : []);
27
+ const rawStories = Array.isArray(obj.stories) ? obj.stories : (Array.isArray(obj.storyMappings) ? obj.storyMappings : []);
28
28
 
29
29
  const storyMappings = (rawStories as unknown[]).map((s) => {
30
30
  const story = s as Record<string, unknown>;
31
- const rawAcMappings = Array.isArray(story['storyMappings'])
32
- ? story['storyMappings']
33
- : (Array.isArray(story['acMappings']) ? story['acMappings'] : []);
31
+ const rawAcMappings = Array.isArray(story.storyMappings)
32
+ ? story.storyMappings
33
+ : (Array.isArray(story.acMappings) ? story.acMappings : []);
34
34
 
35
35
  const acMappings = (rawAcMappings as unknown[]).map((a) => {
36
36
  const acm = a as Record<string, unknown>;
37
- const rawRefs = Array.isArray(acm['testReferences']) ? acm['testReferences'] : [];
37
+ const rawRefs = Array.isArray(acm.testReferences) ? acm.testReferences : [];
38
38
  const testReferences = (rawRefs as unknown[]).map((r) => {
39
39
  const ref = r as Record<string, unknown>;
40
40
  return TestReference.create({
41
- filePath: String(ref['filePath'] ?? ''),
42
- testType: String(ref['testType'] ?? ''),
41
+ filePath: String(ref.filePath ?? ''),
42
+ testType: String(ref.testType ?? ''),
43
43
  });
44
44
  });
45
- return AcMapping.create({ acId: String(acm['acId'] ?? ''), testReferences });
45
+ return AcMapping.create({ acId: String(acm.acId ?? ''), testReferences });
46
46
  });
47
47
 
48
48
  return StoryMapping.create({
49
- storyId: String(story['storyId'] ?? ''),
49
+ storyId: String(story.storyId ?? ''),
50
50
  acMappings,
51
51
  });
52
52
  });
@@ -5,7 +5,7 @@
5
5
  * requirement-test-matrix の集約ルート
6
6
  */
7
7
  import { DuplicateStoryMappingError } from '../errors/duplicate-story-mapping-error.js';
8
- import { StoryMapping } from '../entities/story-mapping.js';
8
+ import type { StoryMapping } from '../entities/story-mapping.js';
9
9
 
10
10
  export interface RequirementTestMatrixCreateProps {
11
11
  readonly storyMappings: readonly StoryMapping[];
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * ストーリー単位のAC→テスト参照マッピングエンティティ
6
6
  */
7
- import { AcMapping, type RawAcMapping } from '../value-objects/ac-mapping.js';
7
+ import type { AcMapping, RawAcMapping } from '../value-objects/ac-mapping.js';
8
8
 
9
9
  export interface StoryMappingCreateProps {
10
10
  readonly storyId: string;
@@ -5,7 +5,7 @@
5
5
  * AC網羅ゲート判定ポリシー
6
6
  * validator-system の L3-004 が実行主体として呼び出す公開ポリシー
7
7
  */
8
- import { RequirementTestMatrix } from '../aggregates/requirement-test-matrix.js';
8
+ import type { RequirementTestMatrix } from '../aggregates/requirement-test-matrix.js';
9
9
 
10
10
  export interface NyquistHarnessError {
11
11
  readonly code: string;
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * AC網羅率算出サービス(純粋な算出ロジック)
6
6
  */
7
- import { RequirementTestMatrix } from '../aggregates/requirement-test-matrix.js';
7
+ import type { RequirementTestMatrix } from '../aggregates/requirement-test-matrix.js';
8
8
  import { CoverageResult } from '../value-objects/coverage-result.js';
9
9
 
10
10
  export class CoverageCalculationService {
@@ -4,9 +4,9 @@
4
4
  *
5
5
  * テストケース逆引きサービス(v1: 直接マッピングのみ)
6
6
  */
7
- import { RequirementTestMatrix } from '../aggregates/requirement-test-matrix.js';
7
+ import type { RequirementTestMatrix } from '../aggregates/requirement-test-matrix.js';
8
8
  import { ImpactAnalysisResult } from '../value-objects/impact-analysis-result.js';
9
- import { TestReference } from '../value-objects/test-reference.js';
9
+ import type { TestReference } from '../value-objects/test-reference.js';
10
10
 
11
11
  export class ImpactAnalysisService {
12
12
  analyze(matrix: RequirementTestMatrix, storyId: string): ImpactAnalysisResult {
@@ -35,15 +35,15 @@ function extractStoryMappings(rawData: unknown): RawStoryMappingEntry[] {
35
35
  if (rawData === null || typeof rawData !== 'object') return [];
36
36
  const obj = rawData as Record<string, unknown>;
37
37
 
38
- const source = Array.isArray(obj['stories'])
39
- ? (obj['stories'] as unknown[])
40
- : Array.isArray(obj['storyMappings'])
41
- ? (obj['storyMappings'] as unknown[])
38
+ const source = Array.isArray(obj.stories)
39
+ ? (obj.stories as unknown[])
40
+ : Array.isArray(obj.storyMappings)
41
+ ? (obj.storyMappings as unknown[])
42
42
  : [];
43
43
 
44
44
  return source.map((item) => {
45
45
  const entry = item as Record<string, unknown>;
46
- return { storyId: String(entry['storyId'] ?? '') };
46
+ return { storyId: String(entry.storyId ?? '') };
47
47
  });
48
48
  }
49
49
 
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * テストケース逆引き結果を表す値オブジェクト
6
6
  */
7
- import { TestReference } from './test-reference.js';
7
+ import type { TestReference } from './test-reference.js';
8
8
 
9
9
  export interface ImpactAnalysisResultProps {
10
10
  readonly storyId: string;
@@ -15,13 +15,13 @@ function convertAjvError(err: ErrorObject): NyquistHarnessError {
15
15
 
16
16
  switch (err.keyword) {
17
17
  case 'required':
18
- message = `${err.instancePath || '(root)'} に必須フィールド '${String(err.params['missingProperty'] ?? '')}' がありません`;
18
+ message = `${err.instancePath || '(root)'} に必須フィールド '${String(err.params.missingProperty ?? '')}' がありません`;
19
19
  break;
20
20
  case 'type':
21
- message = `${err.instancePath} の型が不正です。期待: ${String(err.params['type'] ?? '')}`;
21
+ message = `${err.instancePath} の型が不正です。期待: ${String(err.params.type ?? '')}`;
22
22
  break;
23
23
  case 'pattern':
24
- message = `${err.instancePath} の値が形式 '${String(err.params['pattern'] ?? '')}' に一致しません`;
24
+ message = `${err.instancePath} の値が形式 '${String(err.params.pattern ?? '')}' に一致しません`;
25
25
  break;
26
26
  case 'enum':
27
27
  message = `${err.instancePath} の値は許容値のいずれでもありません`;
@@ -35,10 +35,10 @@ export class ConfigFoundationCoverageThresholdAdapter implements CoverageThresho
35
35
  preset = 'standard';
36
36
  }
37
37
 
38
- const active = THRESHOLDS[preset] ?? THRESHOLDS['standard'];
38
+ const active = THRESHOLDS[preset] ?? THRESHOLDS.standard;
39
39
  return {
40
- standard: THRESHOLDS['standard'] ?? 0.90,
41
- strict: THRESHOLDS['strict'] ?? 0.95,
40
+ standard: THRESHOLDS.standard ?? 0.90,
41
+ strict: THRESHOLDS.strict ?? 0.95,
42
42
  active: active ?? 0.90,
43
43
  };
44
44
  }
@@ -9,4 +9,6 @@ export interface RunL3ValidatorsInput {
9
9
  readonly targetPaths: readonly string[];
10
10
  readonly coverageReportPath?: string;
11
11
  readonly requirementMatrixPath?: string;
12
+ /** L3-005(AC-bound coverage)のスコープ対象 story-id 配列。省略時 []。 */
13
+ readonly acBoundStories?: readonly string[];
12
14
  }
@@ -10,7 +10,7 @@ import { ValidatorId } from '../../domain/value-objects/validator-id.js';
10
10
  import { ValidationResult } from '../../domain/value-objects/validation-result.js';
11
11
  import { ItTestMockDetectionService } from '../../domain/services/it-test-mock-detection-service.js';
12
12
  import { StubCommentDetectionService } from '../../domain/services/stub-comment-detection-service.js';
13
- import { ValidationResultContractMapper } from '../mappers/validation-result-contract-mapper.js';
13
+ import type { ValidationResultContractMapper } from '../mappers/validation-result-contract-mapper.js';
14
14
  import type { ValidationResultContract } from '../dto/validation-result-contract.js';
15
15
  import type { RunL1ValidatorsInput } from '../dto/run-l1-validators-input.js';
16
16
  import type { ItTestFileAnalyzerPort } from '../../domain/ports/it-test-file-analyzer-port.js';
@@ -8,9 +8,9 @@
8
8
  import { readFile } from 'node:fs/promises';
9
9
  import { ValidatorId, InvalidValidatorIdError } from '../../domain/value-objects/validator-id.js';
10
10
  import { ValidationResult, type HarnessErrorLike } from '../../domain/value-objects/validation-result.js';
11
- import { ValidatorRegistry } from '../../domain/services/validator-registry.js';
12
- import { ValidatorExecutionService, ValidatorExecutionError } from '../../domain/services/validator-execution-service.js';
13
- import { ValidationResultContractMapper } from '../mappers/validation-result-contract-mapper.js';
11
+ import type { ValidatorRegistry } from '../../domain/services/validator-registry.js';
12
+ import { type ValidatorExecutionService, ValidatorExecutionError } from '../../domain/services/validator-execution-service.js';
13
+ import type { ValidationResultContractMapper } from '../mappers/validation-result-contract-mapper.js';
14
14
  import type { ValidationResultContract } from '../dto/validation-result-contract.js';
15
15
  import type { RunL2ValidatorsInput } from '../dto/run-l2-validators-input.js';
16
16
  import type { ValidatorConfigPort } from '../../domain/ports/validator-config-port.js';
@@ -7,14 +7,15 @@
7
7
  */
8
8
  import { ValidatorId } from '../../domain/value-objects/validator-id.js';
9
9
  import { ValidationResult } from '../../domain/value-objects/validation-result.js';
10
- import { ValidatorRegistry } from '../../domain/services/validator-registry.js';
11
- import { ValidatorExecutionService, ValidatorExecutionError } from '../../domain/services/validator-execution-service.js';
10
+ import type { ValidatorRegistry } from '../../domain/services/validator-registry.js';
11
+ import { type ValidatorExecutionService, ValidatorExecutionError } from '../../domain/services/validator-execution-service.js';
12
12
  import { ValidatorLanguageCapabilityService } from '../../domain/services/validator-language-capability-service.js';
13
- import { ValidationResultContractMapper } from '../mappers/validation-result-contract-mapper.js';
13
+ import type { ValidationResultContractMapper } from '../mappers/validation-result-contract-mapper.js';
14
14
  import type { ValidationResultContract } from '../dto/validation-result-contract.js';
15
15
  import type { RunL3ValidatorsInput } from '../dto/run-l3-validators-input.js';
16
16
  import type { ValidatorConfigPort } from '../../domain/ports/validator-config-port.js';
17
17
  import type { AcCoveragePolicyPort } from '../../domain/ports/ac-coverage-policy-port.js';
18
+ import type { AcBoundCoveragePolicyPort } from '../../domain/ports/ac-bound-coverage-policy-port.js';
18
19
  import type { SecurityPatternScannerPort } from '../../domain/ports/security-pattern-scanner-port.js';
19
20
  import type { PerformanceScannerPort } from '../../domain/ports/performance-scanner-port.js';
20
21
 
@@ -31,9 +32,12 @@ export interface RunL3ValidatorsUseCaseDeps {
31
32
  validatorConfigPort: ValidatorConfigPort;
32
33
  contractMapper: ValidationResultContractMapper;
33
34
  acCoveragePolicyPort?: AcCoveragePolicyPort;
35
+ acBoundCoveragePolicyPort?: AcBoundCoveragePolicyPort;
34
36
  coverageReportPort?: { getCoverage(): Promise<{ overallCoverage: number; perFileCoverage: readonly { filePath: string; coverage: number }[] }> };
35
37
  securityScannerPort?: SecurityPatternScannerPort;
36
38
  performanceScannerPort?: PerformanceScannerPort;
39
+ /** L3-005 のスコープ対象 story-id(config layers.L3.acBoundStories 由来。既定 [])。 */
40
+ acBoundStories?: readonly string[];
37
41
  }
38
42
 
39
43
  export class RunL3ValidatorsUseCase {
@@ -42,9 +46,11 @@ export class RunL3ValidatorsUseCase {
42
46
  private readonly configPort: ValidatorConfigPort;
43
47
  private readonly mapper: ValidationResultContractMapper;
44
48
  private readonly acCoveragePolicyPort?: AcCoveragePolicyPort;
49
+ private readonly acBoundCoveragePolicyPort?: AcBoundCoveragePolicyPort;
45
50
  private readonly coverageReportPort?: RunL3ValidatorsUseCaseDeps['coverageReportPort'];
46
51
  private readonly securityScannerPort?: SecurityPatternScannerPort;
47
52
  private readonly performanceScannerPort?: PerformanceScannerPort;
53
+ private readonly acBoundStories: readonly string[];
48
54
  private readonly languageCapabilityService = new ValidatorLanguageCapabilityService();
49
55
 
50
56
  constructor(deps: RunL3ValidatorsUseCaseDeps) {
@@ -53,9 +59,11 @@ export class RunL3ValidatorsUseCase {
53
59
  this.configPort = deps.validatorConfigPort;
54
60
  this.mapper = deps.contractMapper;
55
61
  this.acCoveragePolicyPort = deps.acCoveragePolicyPort;
62
+ this.acBoundCoveragePolicyPort = deps.acBoundCoveragePolicyPort;
56
63
  this.coverageReportPort = deps.coverageReportPort;
57
64
  this.securityScannerPort = deps.securityScannerPort;
58
65
  this.performanceScannerPort = deps.performanceScannerPort;
66
+ this.acBoundStories = deps.acBoundStories ?? [];
59
67
  }
60
68
 
61
69
  async execute(input: RunL3ValidatorsInput): Promise<readonly ValidationResultContract[]> {
@@ -195,6 +203,28 @@ export class RunL3ValidatorsUseCase {
195
203
  }
196
204
  }
197
205
 
206
+ // L3-005: AC-bound coverage(fail-closed, default-OFF)。
207
+ // override map に unskipped な L3-005 がある場合のみ policy を呼ぶ(L3-004 と同じ方式)。
208
+ if (this.acBoundCoveragePolicyPort) {
209
+ const l3005Result = overrideMap.get('L3-005');
210
+ if (l3005Result && !l3005Result.skipped) {
211
+ const policyResult = await this.acBoundCoveragePolicyPort.checkAcBoundCoverage({
212
+ matrixFilePath: input.requirementMatrixPath,
213
+ acBoundStories: input.acBoundStories ?? this.acBoundStories,
214
+ });
215
+ if (!policyResult.passed) {
216
+ overrideMap.set(
217
+ 'L3-005',
218
+ ValidationResult.fail(
219
+ ValidatorId.create('L3-005'),
220
+ [...policyResult.errors],
221
+ 0,
222
+ ),
223
+ );
224
+ }
225
+ }
226
+ }
227
+
198
228
  const finalResults = definitions.map(
199
229
  (definition) => overrideMap.get(definition.validatorId.value) ?? ValidationResult.skip(definition.validatorId),
200
230
  );
@@ -10,10 +10,10 @@
10
10
  import { ValidatorId } from '../../domain/value-objects/validator-id.js';
11
11
  import { ValidationResult } from '../../domain/value-objects/validation-result.js';
12
12
  import { LayerConfig } from '../../domain/value-objects/layer-config.js';
13
- import { ValidatorRegistry } from '../../domain/services/validator-registry.js';
14
- import { ValidatorExecutionService, ValidatorExecutionError } from '../../domain/services/validator-execution-service.js';
13
+ import type { ValidatorRegistry } from '../../domain/services/validator-registry.js';
14
+ import { type ValidatorExecutionService, ValidatorExecutionError } from '../../domain/services/validator-execution-service.js';
15
15
  import { ValidatorLanguageCapabilityService } from '../../domain/services/validator-language-capability-service.js';
16
- import { ValidationResultContractMapper } from '../mappers/validation-result-contract-mapper.js';
16
+ import type { ValidationResultContractMapper } from '../mappers/validation-result-contract-mapper.js';
17
17
  import type { ValidationResultContract } from '../dto/validation-result-contract.js';
18
18
  import type { RunL4ValidatorsInput } from '../dto/run-l4-validators-input.js';
19
19
  import type { ValidatorConfigPort } from '../../domain/ports/validator-config-port.js';
@@ -5,9 +5,9 @@
5
5
  * RunQuickModeUseCase — H08-04: Quickモード緩和実行
6
6
  */
7
7
  import { ValidatorId } from '../../domain/value-objects/validator-id.js';
8
- import { ValidatorRegistry } from '../../domain/services/validator-registry.js';
9
- import { ValidatorExecutionService, ValidatorExecutionError } from '../../domain/services/validator-execution-service.js';
10
- import { ValidationResultContractMapper } from '../mappers/validation-result-contract-mapper.js';
8
+ import type { ValidatorRegistry } from '../../domain/services/validator-registry.js';
9
+ import { type ValidatorExecutionService, ValidatorExecutionError } from '../../domain/services/validator-execution-service.js';
10
+ import type { ValidationResultContractMapper } from '../mappers/validation-result-contract-mapper.js';
11
11
  import type { ValidationResultContract } from '../dto/validation-result-contract.js';
12
12
  import type { RunQuickModeInput } from '../dto/run-quick-mode-input.js';
13
13
  import type { ValidatorConfigPort } from '../../domain/ports/validator-config-port.js';