phasegate 0.165.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.
Files changed (64) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/docs/ADR/021-severity-contract.md +46 -0
  3. package/docs/contracts/requirement-test-matrix.schema.json +5 -0
  4. package/package.json +1 -1
  5. package/scripts/harness/attestation/application/dto/attestation-document.ts +2 -0
  6. package/scripts/harness/attestation/application/dto/verify-attestation-output.ts +2 -0
  7. package/scripts/harness/attestation/application/mappers/attestation-record-mapper.ts +13 -0
  8. package/scripts/harness/attestation/application/ports/ac-bound-allowlist-port.ts +12 -0
  9. package/scripts/harness/attestation/application/ports/matrix-source-port.ts +16 -0
  10. package/scripts/harness/attestation/application/usecases/produce-attestation-usecase.ts +39 -0
  11. package/scripts/harness/attestation/application/usecases/verify-attestation-usecase.ts +60 -1
  12. package/scripts/harness/attestation/composition-root.ts +14 -0
  13. package/scripts/harness/attestation/domain/entities/attestation-record.ts +13 -0
  14. package/scripts/harness/attestation/domain/services/ac-bound-scope-service.ts +80 -0
  15. package/scripts/harness/attestation/infrastructure/adapters/config-ac-bound-allowlist-adapter.ts +23 -0
  16. package/scripts/harness/attestation/infrastructure/adapters/file-system-matrix-source-adapter.ts +25 -0
  17. package/scripts/harness/attestation/presentation/handlers/verify-attestation-handler.ts +1 -0
  18. package/scripts/harness/ci-governance/infrastructure/adapters/validator-id-registry-adapter.ts +8 -0
  19. package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +3 -0
  20. package/scripts/harness/config-foundation/domain/harness-config.ts +1 -0
  21. package/scripts/harness/config-foundation/domain/value-objects/l3-config.ts +4 -0
  22. package/scripts/harness/config-foundation/domain/value-objects/layers-config.ts +1 -1
  23. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +7 -0
  24. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +7 -0
  25. package/scripts/harness/harness-error/domain/errors/severity-downgrade-violation-error.ts +1 -1
  26. package/scripts/harness/main.ts +2 -2
  27. package/scripts/harness/nyquist-validation/application/dto/generate-matrix-output.ts +42 -0
  28. package/scripts/harness/nyquist-validation/application/mappers/coverage-result-mapper.ts +13 -15
  29. package/scripts/harness/nyquist-validation/application/mappers/impact-analysis-result-mapper.ts +12 -12
  30. package/scripts/harness/nyquist-validation/application/usecases/analyze-impact-usecase.ts +11 -11
  31. package/scripts/harness/nyquist-validation/application/usecases/calculate-coverage-usecase.ts +11 -11
  32. package/scripts/harness/nyquist-validation/application/usecases/check-ac-coverage-gate-usecase.ts +9 -9
  33. package/scripts/harness/nyquist-validation/application/usecases/generate-requirement-test-matrix-usecase.ts +62 -7
  34. package/scripts/harness/nyquist-validation/domain/aggregates/requirement-test-matrix.ts +1 -1
  35. package/scripts/harness/nyquist-validation/domain/entities/story-mapping.ts +1 -1
  36. package/scripts/harness/nyquist-validation/domain/services/ac-coverage-gate-policy.ts +1 -1
  37. package/scripts/harness/nyquist-validation/domain/services/coverage-calculation-service.ts +1 -1
  38. package/scripts/harness/nyquist-validation/domain/services/impact-analysis-service.ts +2 -2
  39. package/scripts/harness/nyquist-validation/domain/services/matrix-validation-service.ts +5 -5
  40. package/scripts/harness/nyquist-validation/domain/value-objects/impact-analysis-result.ts +1 -1
  41. package/scripts/harness/nyquist-validation/infrastructure/adapters/ajv-json-schema-validator-adapter.ts +3 -3
  42. package/scripts/harness/nyquist-validation/infrastructure/adapters/config-foundation-coverage-threshold-adapter.ts +3 -3
  43. package/scripts/harness/nyquist-validation/infrastructure/adapters/type-script-test-reference-source-adapter.ts +110 -8
  44. package/scripts/harness/nyquist-validation/infrastructure/schema/matrix-schema-loader.ts +5 -0
  45. package/scripts/harness/validator-system/application/dto/run-l3-validators-input.ts +2 -0
  46. package/scripts/harness/validator-system/application/use-cases/run-l1-validators-usecase.ts +1 -1
  47. package/scripts/harness/validator-system/application/use-cases/run-l2-validators-usecase.ts +3 -3
  48. package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +33 -3
  49. package/scripts/harness/validator-system/application/use-cases/run-l4-validators-usecase.ts +26 -3
  50. package/scripts/harness/validator-system/application/use-cases/run-quick-mode-usecase.ts +3 -3
  51. package/scripts/harness/validator-system/composition-root.ts +22 -0
  52. package/scripts/harness/validator-system/domain/ports/ac-bound-coverage-policy-port.ts +22 -0
  53. package/scripts/harness/validator-system/domain/ports/ac-level-traceability-port.ts +14 -0
  54. package/scripts/harness/validator-system/domain/services/l4/ac-level-traceability-service.ts +102 -0
  55. package/scripts/harness/validator-system/domain/services/validator-execution-service.ts +1 -1
  56. package/scripts/harness/validator-system/domain/services/validator-registry.ts +2 -2
  57. package/scripts/harness/validator-system/domain/value-objects/layer-config.ts +1 -1
  58. package/scripts/harness/validator-system/domain/value-objects/validation-result.ts +1 -1
  59. package/scripts/harness/validator-system/domain/value-objects/validator-definition.ts +2 -2
  60. package/scripts/harness/validator-system/domain/value-objects/validator-id.ts +5 -0
  61. package/scripts/harness/validator-system/infrastructure/adapters/json-coverage-report-adapter.ts +1 -1
  62. package/scripts/harness/validator-system/infrastructure/adapters/nyquist-ac-bound-coverage-policy-adapter.ts +118 -0
  63. package/scripts/harness/validator-system/infrastructure/adapters/nyquist-ac-level-traceability-adapter.ts +104 -0
  64. package/scripts/harness/validator-system/presentation/handlers/report-validation-results-handler.ts +4 -4
@@ -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
  }
@@ -1,17 +1,31 @@
1
1
  // @layer infrastructure
2
2
  // @unit nyquist-validation
3
3
  // @work-item-id WI-125
4
+ // @work-item-id WI-222
4
5
 
5
6
  import { readdir, readFile, stat } from 'node:fs/promises';
6
7
  import path from 'node:path';
7
8
  import type { TestReferenceSourcePort } from '../../application/usecases/generate-requirement-test-matrix-usecase.js';
8
- import type { TestReferenceSourceDto } from '../../application/dto/generate-matrix-output.js';
9
+ import type { OrphanAcTagDto, TestReferenceSourceDto } from '../../application/dto/generate-matrix-output.js';
9
10
 
10
11
  // StoryId は HXX-XX 形式に加え Phase 2 拡張 Epic の HF\d+-XX 形式も許容する
11
12
  // (markdown-requirement-source-adapter の STORY_HEADING / traceability-model の StoryId 正規表現と整合)。
12
13
  // 旧 /H\d{2}-\d{2}/ は HF2-01 等を取りこぼし、正しく注釈されたテストが「テストなし」と誤判定されていた。
13
14
  const STORY_TAG = /@story(?:-id)?\s+(H(?:F\d+|\d{2})-\d{2})/;
14
- const TEST_NAME = /\b(?:it|test)(?:\.each\([^)]*\))?\s*\(\s*['"`]([^'"`]+)['"`]/g;
15
+ const STORY_TAG_GLOBAL = /@story(?:-id)?\s+(H(?:F\d+|\d{2})-\d{2})/g;
16
+ // HF2-05: @ac は 1 行に複数 AC を許容する。行末までの全トークンを個別に解釈する。
17
+ // 各トークンは絶対形式 `HXX-YY-N`(story-relative でない)または相対形式 `AC-N`。
18
+ const AC_TAG_LINE = /@ac\s+(.+?)\s*$/;
19
+ const AC_ABSOLUTE_TOKEN = /^(H(?:F\d+|\d{2})-\d{2})-([1-9][0-9]*)$/;
20
+ const AC_RELATIVE_TOKEN = /^AC-([1-9][0-9]*)$/;
21
+ // テストケース開始行の検出(it / test、.each やスペースを許容)。
22
+ const TEST_START = /\b(?:it|test)(?:\.each\([^)]*\))?\s*\(\s*['"`]([^'"`]+)['"`]/;
23
+
24
+ interface PendingAcToken {
25
+ readonly rawTag: string;
26
+ readonly resolvedAcId: string | null;
27
+ readonly reason: OrphanAcTagDto['reason'] | null;
28
+ }
15
29
 
16
30
  async function collectTestFiles(root: string): Promise<readonly string[]> {
17
31
  const rootStat = await stat(root).catch(() => null);
@@ -43,15 +57,103 @@ export class TypeScriptTestReferenceSourceAdapter implements TestReferenceSource
43
57
  const storyId = storyMatch[1];
44
58
  const relativePath = path.relative(process.cwd(), filePath).replaceAll(path.sep, '/');
45
59
  const testType = classifyTestType(relativePath);
46
- const names = [...content.matchAll(TEST_NAME)].map((match) => match[1]);
47
- if (names.length === 0) {
48
- references.push({ storyId, filePath: relativePath, testType });
60
+ // HF2-05: 相対 @ac (AC-N) はファイルに @story がちょうど 1 件のときのみ解決する。
61
+ const storyCount = [...content.matchAll(STORY_TAG_GLOBAL)].length;
62
+ references.push(...this.scanFile({ content, storyId, relativePath, testType, storyCount }));
63
+ }
64
+ return Object.freeze(references);
65
+ }
66
+
67
+ /**
68
+ * HF2-05: ファイル内容を行単位で走査し、直前の it()/test() 以降に現れた @ac を
69
+ * 「最近接の @ac」として次のテストケースへ紐づける(positional scan)。
70
+ * @ac が無いテストは従来どおり acIds 無しの参照を生成する。
71
+ */
72
+ private scanFile(args: {
73
+ content: string;
74
+ storyId: string;
75
+ relativePath: string;
76
+ testType: 'unit' | 'it' | 'scenario';
77
+ storyCount: number;
78
+ }): TestReferenceSourceDto[] {
79
+ const { content, storyId, relativePath, testType, storyCount } = args;
80
+ const lines = content.split('\n');
81
+ const result: TestReferenceSourceDto[] = [];
82
+ let pending: PendingAcToken[] = [];
83
+
84
+ for (const line of lines) {
85
+ const acMatch = line.match(AC_TAG_LINE);
86
+ if (acMatch) {
87
+ for (const token of acMatch[1].trim().split(/\s+/)) {
88
+ if (token.length > 0) pending.push(this.resolveAcToken(token, storyId, storyCount));
89
+ }
49
90
  continue;
50
91
  }
51
- for (const testName of names) {
52
- references.push({ storyId, filePath: relativePath, testType, testName });
92
+ const testMatch = line.match(TEST_START);
93
+ if (testMatch) {
94
+ result.push(this.buildReference({
95
+ storyId,
96
+ relativePath,
97
+ testType,
98
+ testName: testMatch[1],
99
+ pending,
100
+ }));
101
+ pending = [];
53
102
  }
54
103
  }
55
- return Object.freeze(references);
104
+
105
+ // it()/test() が 1 件も無い注釈済みファイルは、file-level 参照 1 件を維持する(従来挙動)。
106
+ if (result.length === 0) {
107
+ result.push({ storyId, filePath: relativePath, testType });
108
+ }
109
+ return result;
110
+ }
111
+
112
+ private buildReference(args: {
113
+ storyId: string;
114
+ relativePath: string;
115
+ testType: 'unit' | 'it' | 'scenario';
116
+ testName: string;
117
+ pending: readonly PendingAcToken[];
118
+ }): TestReferenceSourceDto {
119
+ const { storyId, relativePath, testType, testName, pending } = args;
120
+ const base: TestReferenceSourceDto = { storyId, filePath: relativePath, testType, testName };
121
+ if (pending.length === 0) return base;
122
+
123
+ const acIds = pending.filter((p) => p.resolvedAcId !== null).map((p) => p.resolvedAcId as string);
124
+ const orphanAcTags: OrphanAcTagDto[] = pending
125
+ .filter((p) => p.resolvedAcId === null && p.reason !== null)
126
+ .map((p) => ({ storyId, filePath: relativePath, testName, rawTag: p.rawTag, reason: p.reason as OrphanAcTagDto['reason'] }));
127
+
128
+ return {
129
+ ...base,
130
+ ...(acIds.length > 0 ? { acIds: Object.freeze([...new Set(acIds)]) } : {}),
131
+ ...(orphanAcTags.length > 0 ? { orphanAcTags: Object.freeze(orphanAcTags) } : {}),
132
+ };
133
+ }
134
+
135
+ /**
136
+ * 1 個の @ac トークンを解釈する。
137
+ * - 絶対形式 `HXX-YY-N`: story が一致すれば `AC-N` に解決、不一致なら ac-not-in-story orphan。
138
+ * - 相対形式 `AC-N`: @story が単一なら `AC-N` に解決、複数なら relative-multi-story orphan。
139
+ */
140
+ private resolveAcToken(token: string, storyId: string, storyCount: number): PendingAcToken {
141
+ const absolute = token.match(AC_ABSOLUTE_TOKEN);
142
+ if (absolute) {
143
+ const [, tokenStory, acNumber] = absolute;
144
+ if (tokenStory === storyId) {
145
+ return { rawTag: token, resolvedAcId: `AC-${acNumber}`, reason: null };
146
+ }
147
+ return { rawTag: token, resolvedAcId: null, reason: 'ac-not-in-story' };
148
+ }
149
+ const relative = token.match(AC_RELATIVE_TOKEN);
150
+ if (relative) {
151
+ if (storyCount === 1) {
152
+ return { rawTag: token, resolvedAcId: `AC-${relative[1]}`, reason: null };
153
+ }
154
+ return { rawTag: token, resolvedAcId: null, reason: 'relative-multi-story' };
155
+ }
156
+ // 形式不明トークンは advisory orphan として ac-not-in-story 扱い。
157
+ return { rawTag: token, resolvedAcId: null, reason: 'ac-not-in-story' };
56
158
  }
57
159
  }
@@ -1,8 +1,13 @@
1
1
  /**
2
2
  * @layer infrastructure
3
3
  * @unit nyquist-validation
4
+ * @work-item-id WI-222
4
5
  *
5
6
  * requirement-test-matrix.schema.json ローダー(シングルトンキャッシュ付き)
7
+ *
8
+ * HF2-05 (schema 1.1): testReferences に optional な `binding` enum("ac"|"file")を追加。
9
+ * 1.1 は追加フィールドが optional かつ additionalProperties:false を満たすため、
10
+ * `binding` を持たない 1.0 マトリクスも引き続き検証を通過する(後方互換)。
6
11
  */
7
12
  import { readFile } from 'node:fs/promises';
8
13
  import { resolve, join } from 'node:path';
@@ -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';
@@ -23,6 +23,8 @@ import type { DeadCodeDetectionService } from '../../domain/services/l4/dead-cod
23
23
  import type { ArchitectureSemanticAnalysisService } from '../../domain/services/l4/architecture-semantic-analysis-service.js';
24
24
  import { SkillCatalogDriftService } from '../../domain/services/l4/skill-catalog-drift-service.js';
25
25
  import type { SkillCatalogDriftPort } from '../../domain/ports/skill-catalog-drift-port.js';
26
+ import { AcLevelTraceabilityService } from '../../domain/services/l4/ac-level-traceability-service.js';
27
+ import type { AcLevelTraceabilityPort } from '../../domain/ports/ac-level-traceability-port.js';
26
28
 
27
29
  interface ScheduledHarnessErrorContract {
28
30
  readonly severity: string;
@@ -73,6 +75,7 @@ export interface RunL4ValidatorsUseCaseDeps {
73
75
  deadCodeDetectionService?: DeadCodeDetectionService;
74
76
  architectureSemanticAnalysisService?: ArchitectureSemanticAnalysisService;
75
77
  skillCatalogDriftPort?: SkillCatalogDriftPort;
78
+ acLevelTraceabilityPort?: AcLevelTraceabilityPort;
76
79
  pathRoots?: {
77
80
  readonly inceptionRoot: string;
78
81
  readonly designRoot: string;
@@ -92,6 +95,8 @@ export class RunL4ValidatorsUseCase {
92
95
  private readonly architectureSemanticAnalysisService?: ArchitectureSemanticAnalysisService;
93
96
  private readonly skillCatalogDriftPort?: SkillCatalogDriftPort;
94
97
  private readonly skillCatalogDriftService = new SkillCatalogDriftService();
98
+ private readonly acLevelTraceabilityPort?: AcLevelTraceabilityPort;
99
+ private readonly acLevelTraceabilityService = new AcLevelTraceabilityService();
95
100
  private readonly pathRoots: { readonly inceptionRoot: string; readonly designRoot: string };
96
101
  private readonly checkDocFreshnessUseCase?: CheckDocFreshnessUseCasePort;
97
102
  private readonly validateDocPointersUseCase?: ValidateDocPointersUseCasePort;
@@ -107,6 +112,7 @@ export class RunL4ValidatorsUseCase {
107
112
  this.deadCodeDetectionService = deps.deadCodeDetectionService;
108
113
  this.architectureSemanticAnalysisService = deps.architectureSemanticAnalysisService;
109
114
  this.skillCatalogDriftPort = deps.skillCatalogDriftPort;
115
+ this.acLevelTraceabilityPort = deps.acLevelTraceabilityPort;
110
116
  this.pathRoots = deps.pathRoots ?? {
111
117
  inceptionRoot: 'docs/inception',
112
118
  designRoot: 'docs/product/construction',
@@ -263,6 +269,23 @@ export class RunL4ValidatorsUseCase {
263
269
  }
264
270
  }
265
271
 
272
+ if (this.acLevelTraceabilityPort) {
273
+ const l4007Result = overrideMap.get('L4-007');
274
+ // default-OFF: L4-007 が enabled validator set に含まれず skip される場合は上書きしない。
275
+ if (l4007Result && !l4007Result.skipped) {
276
+ const snapshot = await this.acLevelTraceabilityPort.collect();
277
+ const report = this.acLevelTraceabilityService.check(snapshot);
278
+ // advisory-only: finding があっても severity は必ず warning。overall gate は
279
+ // failOnWarning=false(既定)で PASS のまま(ADR-019 §5)。finding が無ければ PASS。
280
+ overrideMap.set(
281
+ 'L4-007',
282
+ report.hasFindings()
283
+ ? ValidationResult.fail(ValidatorId.create('L4-007'), report.toHarnessErrors(), 0)
284
+ : ValidationResult.pass(ValidatorId.create('L4-007'), 0),
285
+ );
286
+ }
287
+ }
288
+
266
289
  const finalResults = definitions.map(
267
290
  (definition) => overrideMap.get(definition.validatorId.value) ?? ValidationResult.skip(definition.validatorId),
268
291
  );
@@ -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';
@@ -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';
@@ -39,6 +40,7 @@ import { AdrFoundationReferenceAdapter } from './infrastructure/adapters/adr-fou
39
40
  import { ImportGraphSourceAnalysisAdapter } from './infrastructure/adapters/import-graph-source-analysis-adapter.js';
40
41
  import { FileSystemArchitectureSemanticSourceAdapter } from './infrastructure/adapters/file-system-architecture-semantic-source-adapter.js';
41
42
  import { FileSystemSkillCatalogDriftAdapter } from './infrastructure/adapters/file-system-skill-catalog-drift-adapter.js';
43
+ import { NyquistAcLevelTraceabilityAdapter } from './infrastructure/adapters/nyquist-ac-level-traceability-adapter.js';
42
44
  import { FileSystemWorkItemReflectionAdapter } from './infrastructure/adapters/file-system-work-item-reflection-adapter.js';
43
45
  import { DriftDetectionService } from './domain/services/l4/drift-detection-service.js';
44
46
  import { ConsistencyCheckService } from './domain/services/l4/consistency-check-service.js';
@@ -111,12 +113,18 @@ export function buildDefaultRegistry(): ValidatorRegistry {
111
113
  createDef('L3-002', 'L3', 'strictOnly'),
112
114
  createDef('L3-003', 'L3', 'always'),
113
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'),
114
119
  createDef('L4-001', 'L4', 'always'),
115
120
  createDef('L4-002', 'L4', 'always'),
116
121
  createDef('L4-003', 'L4', 'strictOnly'),
117
122
  createDef('L4-004', 'L4', 'always'),
118
123
  createDef('L4-005', 'L4', 'always'),
119
124
  createDef('L4-006', 'L4', 'always', 'SkillCatalogDriftPort'),
125
+ // WI-222 / HF2-05: L4-007 (ac-level-traceability) は registry には登録するが
126
+ // default-OFF(DEFAULT_CONFIG.layers.L4.validators に含めない)。advisory-only。
127
+ createDef('L4-007', 'L4', 'always', 'AcLevelTraceabilityPort'),
120
128
  ];
121
129
 
122
130
  return new ValidatorRegistry(definitions);
@@ -150,6 +158,8 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
150
158
  const phaseGatePolicyPort = new PhaseDependencyPhaseGatePolicyAdapter();
151
159
  const metadataPolicyPort = new TraceabilityMetadataPolicyAdapter();
152
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();
153
163
  // L3-003: テストカバレッジレポート(vitest --coverage の json-summary 出力)を読み取る。
154
164
  // 未配線だと L3-003 は装飾的な pass になり 90% 閾値が形骸化するため必ず配線する。
155
165
  const coverageReportPort = new JsonCoverageReportAdapter(
@@ -189,15 +199,21 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
189
199
  contractTraceabilityPolicyPort,
190
200
  });
191
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
+
192
206
  const runL3ValidatorsUseCase = new RunL3ValidatorsUseCase({
193
207
  validatorRegistry: registry,
194
208
  validatorExecutionService: executionService,
195
209
  validatorConfigPort: configPort,
196
210
  contractMapper,
197
211
  acCoveragePolicyPort,
212
+ acBoundCoveragePolicyPort,
198
213
  coverageReportPort,
199
214
  securityScannerPort,
200
215
  performanceScannerPort,
216
+ acBoundStories,
201
217
  });
202
218
 
203
219
  const markdownDesignDocumentPort = new MarkdownDesignDocumentAdapter(docsRoot);
@@ -206,6 +222,11 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
206
222
  const sourceAnalysisPort = new ImportGraphSourceAnalysisAdapter();
207
223
  const architectureSemanticSourcePort = new FileSystemArchitectureSemanticSourceAdapter();
208
224
  const skillCatalogDriftPort = new FileSystemSkillCatalogDriftAdapter(cwd);
225
+ // WI-222 / HF2-05: L4-007 (advisory) 用の AC 単位トレーサビリティ port。
226
+ // matrix path は L3-004 と同じ config キーを参照する。
227
+ const acLevelTraceabilityPort = new NyquistAcLevelTraceabilityAdapter({
228
+ matrixFilePath: configData.layers?.L3?.requirementMatrixPath ?? '.harness/requirement-test-matrix.json',
229
+ });
209
230
  const workItemReflectionPort = new FileSystemWorkItemReflectionAdapter(cwd);
210
231
 
211
232
  const driftDetectionService = new DriftDetectionService({
@@ -236,6 +257,7 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
236
257
  deadCodeDetectionService,
237
258
  architectureSemanticAnalysisService,
238
259
  skillCatalogDriftPort,
260
+ acLevelTraceabilityPort,
239
261
  pathRoots: {
240
262
  inceptionRoot: inceptionDocsRoot,
241
263
  designRoot: designDocsRoot,
@@ -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
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @layer domain
3
+ * @unit validator-system
4
+ * @work-item-id WI-222
5
+ *
6
+ * HF2-05 / L4-007: nyquist-validation のマトリクス生成レポートを AC 単位トレーサビリティの
7
+ * snapshot へ橋渡しする port。実装(infrastructure adapter)が nyquist の generate-matrix
8
+ * usecase を実行し、acLevelCoverage / fileFallbackOnlyAcs / orphanAcTags を収集する。
9
+ */
10
+ import type { AcLevelTraceabilitySnapshot } from '../services/l4/ac-level-traceability-service.js';
11
+
12
+ export interface AcLevelTraceabilityPort {
13
+ collect(): Promise<AcLevelTraceabilitySnapshot>;
14
+ }