phasegate 0.150.1 → 0.151.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 (28) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/docs/guide/layer-model.md +4 -0
  3. package/package.json +1 -1
  4. package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +1 -1
  5. package/scripts/harness/harness-api/domain/value-objects/harness-status-summary.ts +1 -0
  6. package/scripts/harness/phase2-extensions/application/dto/validate-doc-pointers-output.ts +5 -0
  7. package/scripts/harness/phase2-extensions/application/usecases/validate-doc-pointers-usecase.ts +24 -4
  8. package/scripts/harness/phase2-extensions/domain/aggregates/pointer-rule.ts +14 -0
  9. package/scripts/harness/phase2-extensions/domain/services/freshness-check-service.ts +11 -0
  10. package/scripts/harness/phase2-extensions/domain/value-objects/document-age.ts +3 -2
  11. package/scripts/harness/phase2-extensions/infrastructure/adapters/harness-config-freshness-adapter.ts +13 -0
  12. package/scripts/harness/phase2-extensions/presentation/formatters/pointer-result-formatter.ts +4 -1
  13. package/scripts/harness/validator-system/application/use-cases/run-l2-validators-usecase.ts +29 -1
  14. package/scripts/harness/validator-system/composition-root.ts +6 -2
  15. package/scripts/harness/validator-system/domain/ports/contract-traceability-policy-port.ts +9 -0
  16. package/scripts/harness/validator-system/domain/services/contract-traceability-coverage-service.ts +259 -0
  17. package/scripts/harness/validator-system/domain/services/l4/consistency-check-service.ts +16 -17
  18. package/scripts/harness/validator-system/domain/services/l4/drift-detection-service.ts +96 -51
  19. package/scripts/harness/validator-system/domain/services/l4/semantic-drift-service.ts +112 -0
  20. package/scripts/harness/validator-system/domain/value-objects/consistency-report.ts +3 -1
  21. package/scripts/harness/validator-system/domain/value-objects/contract-traceability-model.ts +124 -0
  22. package/scripts/harness/validator-system/domain/value-objects/semantic-drift-report.ts +50 -0
  23. package/scripts/harness/validator-system/domain/value-objects/validator-id.ts +2 -0
  24. package/scripts/harness/validator-system/index.ts +2 -0
  25. package/scripts/harness/validator-system/infrastructure/adapters/biome-ast-source-code-analyzer-adapter.ts +31 -2
  26. package/scripts/harness/validator-system/infrastructure/adapters/file-system-contract-traceability-policy-adapter.ts +115 -0
  27. package/scripts/harness/validator-system/infrastructure/adapters/harness-config-validator-config-adapter.ts +1 -1
  28. package/scripts/harness/validator-system/infrastructure/adapters/markdown-design-document-adapter.ts +111 -20
package/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.151.0] - 2026-05-12
11
+
12
+ ### Added
13
+
14
+ - **G4 / WI-132 / WI-133 / WI-136 / WI-137 / WI-138 — contract, boundary, state machine, error contract, and traceability coverage** — adds `L2-015 contract-traceability-coverage`, a semantic model for public contracts, test observations, boundary cases, state machines, error contracts, and traceability graph slices, plus opt-in `@phasegate-contract` / `@phasegate-observation` extraction and L2 result mapping.
15
+
16
+ ## [0.150.2] - 2026-05-12
17
+
18
+ ### Added
19
+
20
+ - **G3 / WI-117 / WI-118 / WI-122 / WI-139 — L4 drift, consistency, docs, and semantic drift semantics** — L4 drift detection now compares unit-scoped records, prefers `@unit` metadata, handles product construction docs and re-export/default export surfaces more precisely, reports real product-doc consistency targets, adds operational pointer/freshness semantics, and introduces semantic drift reports across design intent, implementation behavior, and test observations.
21
+
10
22
  ## [0.150.1] - 2026-05-12
11
23
 
12
24
  ### Fixed
@@ -253,3 +253,7 @@ Presets control which layers are active. Choose based on project maturity and te
253
253
  - **strict** — Full defense. Enables scheduled drift detection (L4). Runtime L0 hooks are configured through the agent and Husky hook files, not through validator presets.
254
254
 
255
255
  Presets are configured in `phasegate.config.json`, the single source of truth for all quality settings.
256
+ <!-- @work-item-id WI-117, WI-118, WI-122, WI-139 -->
257
+ ## G3 L4 Advisory Preconditions
258
+
259
+ L4 drift / consistency / docs semantic findings are advisory by default. Use fail-on-warning only when reports include Unit-scoped drift keys, real product-doc consistency targets, pointer/freshness owner and policy metadata, and semantic drift behavior coverage. This avoids treating parser limitations as blocking quality gates.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phasegate",
3
- "version": "0.150.1",
3
+ "version": "0.151.0",
4
4
  "packageManager": "pnpm@10.30.1",
5
5
  "description": "Phasegate — AI-agnostic quality defense toolkit. Enforces structural integrity between design intent and code.",
6
6
  "license": "MIT",
@@ -11,7 +11,7 @@ export function toValidatorSystemConfig(resolvedConfig: HarnessConfigV2 | undefi
11
11
  return {
12
12
  project: { preset: resolvedConfig.project.preset },
13
13
  layers: {
14
- L2: { enabled: resolvedConfig.layers.L2.enabled, validators: ['L2-001', 'L2-002', 'L2-003', 'L2-013', 'L2-014'] },
14
+ L2: { enabled: resolvedConfig.layers.L2.enabled, validators: ['L2-001', 'L2-002', 'L2-003', 'L2-013', 'L2-014', 'L2-015'] },
15
15
  L3: { enabled: resolvedConfig.layers.L3.enabled },
16
16
  L4: { enabled: resolvedConfig.layers.L4.enabled, validators: resolvedConfig.layers.L4.validators },
17
17
  },
@@ -1,4 +1,5 @@
1
1
  // @layer domain
2
+ // @unit harness-api
2
3
  // harness-status-summary.ts — HarnessStatusSummary Value Object
3
4
 
4
5
  import type { LayerHealth, LayerId } from './layer-health.js';
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @layer application
3
3
  * @unit phase2-extensions
4
+ * @work-item-id WI-122
4
5
  */
5
6
  import type { HarnessErrorContract } from '../../../harness-error/application/dto/harness-error-contract.js';
6
7
 
@@ -8,8 +9,12 @@ export interface PointerValidationResultDto {
8
9
  documentPath: string;
9
10
  pointerTarget: string;
10
11
  pointerType: 'file-path' | 'url';
12
+ semanticPointerType: 'reference' | 'implementation' | 'adr' | 'product-doc' | 'external-url';
13
+ owner: string;
14
+ severity: 'fail' | 'warn' | 'skip';
11
15
  isResolvable: boolean;
12
16
  errorMessage: string | null;
17
+ nextAction: string;
13
18
  }
14
19
 
15
20
  export interface ValidateDocPointersOutput {
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @layer application
3
3
  * @unit phase2-extensions
4
+ * @work-item-id WI-122
4
5
  */
5
6
  import type { FreshnessConfigPort } from '../../domain/ports/freshness-config-port.js';
6
7
  import type { DocumentScannerPort } from '../../domain/ports/document-scanner-port.js';
@@ -35,6 +36,7 @@ export class ValidateDocPointersUseCase {
35
36
 
36
37
  const results = [];
37
38
  let totalDocuments = 0;
39
+ let skippedUrlPointers = 0;
38
40
 
39
41
  for (const rule of filteredRules) {
40
42
  const documentPaths = await this.documentScannerPort.scan(rule.documentPattern);
@@ -45,7 +47,13 @@ export class ValidateDocPointersUseCase {
45
47
  const validationResults = await this.pointerResolutionService.resolve(pointers);
46
48
 
47
49
  for (const validationResult of validationResults) {
48
- if (validationResult.pointer.isUrl() && !input.includeUrlPointers) {
50
+ const semanticPointerType = classifyPointerTarget(validationResult.pointer.target, validationResult.pointer.type);
51
+ const severity = validationResult.pointer.isUrl() && !input.includeUrlPointers
52
+ ? 'skip'
53
+ : rule.policyFor(semanticPointerType);
54
+
55
+ if (severity === 'skip') {
56
+ if (validationResult.pointer.isUrl()) skippedUrlPointers += 1;
49
57
  continue;
50
58
  }
51
59
 
@@ -53,8 +61,14 @@ export class ValidateDocPointersUseCase {
53
61
  documentPath,
54
62
  pointerTarget: validationResult.pointer.target,
55
63
  pointerType: validationResult.pointer.type,
64
+ semanticPointerType,
65
+ owner: rule.owner,
66
+ severity,
56
67
  isResolvable: validationResult.isResolvable,
57
68
  errorMessage: validationResult.errorMessage,
69
+ nextAction: validationResult.isResolvable
70
+ ? 'no action required'
71
+ : `Fix ${semanticPointerType} pointer or change policy for owner ${rule.owner}`,
58
72
  });
59
73
  }
60
74
 
@@ -66,9 +80,7 @@ export class ValidateDocPointersUseCase {
66
80
  }
67
81
 
68
82
  const brokenPointers = results.filter((result) => !result.isResolvable).length;
69
- const skippedUrlPointers = results.filter((result) => result.pointerType === 'url').length;
70
- const failingRules = filteredRules.filter((rule) => rule.shouldFailOnBroken());
71
- const passed = failingRules.length === 0 || brokenPointers === 0;
83
+ const passed = !results.some((result) => !result.isResolvable && result.severity === 'fail');
72
84
 
73
85
  return {
74
86
  results,
@@ -96,3 +108,11 @@ export class ValidateDocPointersUseCase {
96
108
  }
97
109
  }
98
110
  }
111
+
112
+ function classifyPointerTarget(target: string, rawType: 'file-path' | 'url'): 'reference' | 'implementation' | 'adr' | 'product-doc' | 'external-url' {
113
+ if (rawType === 'url') return 'external-url';
114
+ if (/docs\/ADR\/|ADR-\d{3}/i.test(target)) return 'adr';
115
+ if (/docs\/product\//.test(target)) return 'product-doc';
116
+ if (/scripts\/harness\/|\.ts$/.test(target)) return 'implementation';
117
+ return 'reference';
118
+ }
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @layer domain
3
3
  * @unit phase2-extensions
4
+ * @work-item-id WI-122
4
5
  */
5
6
  import { Phase2ExtensionsDomainError } from '../errors/phase2-extensions-domain-error.js';
6
7
 
@@ -8,17 +9,23 @@ export interface PointerRuleProps {
8
9
  ruleId: string;
9
10
  documentPattern: string;
10
11
  failOnBroken: boolean;
12
+ owner?: string;
13
+ pointerPolicies?: Record<string, 'fail' | 'warn' | 'skip'>;
11
14
  }
12
15
 
13
16
  export class PointerRule {
14
17
  readonly ruleId: string;
15
18
  readonly documentPattern: string;
16
19
  readonly failOnBroken: boolean;
20
+ readonly owner: string;
21
+ readonly pointerPolicies: Readonly<Record<string, 'fail' | 'warn' | 'skip'>>;
17
22
 
18
23
  private constructor(props: PointerRuleProps) {
19
24
  this.ruleId = props.ruleId;
20
25
  this.documentPattern = props.documentPattern;
21
26
  this.failOnBroken = props.failOnBroken;
27
+ this.owner = props.owner ?? 'unowned';
28
+ this.pointerPolicies = Object.freeze({ ...(props.pointerPolicies ?? {}) });
22
29
  Object.freeze(this);
23
30
  }
24
31
 
@@ -35,4 +42,11 @@ export class PointerRule {
35
42
  shouldFailOnBroken(): boolean {
36
43
  return this.failOnBroken;
37
44
  }
45
+
46
+ policyFor(pointerType: string): 'fail' | 'warn' | 'skip' {
47
+ if (pointerType === 'external-url' && this.pointerPolicies[pointerType] === undefined) {
48
+ return 'skip';
49
+ }
50
+ return this.pointerPolicies[pointerType] ?? (this.failOnBroken ? 'fail' : 'warn');
51
+ }
38
52
  }
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @layer domain
3
3
  * @unit phase2-extensions
4
+ * @work-item-id WI-122
4
5
  */
5
6
  import type { DocFreshnessRule } from '../aggregates/doc-freshness-rule.js';
6
7
  import type { DocumentAge, DocumentAgeSource } from '../value-objects/document-age.js';
@@ -10,8 +11,10 @@ export interface FreshnessCheckResult {
10
11
  documentPath: string;
11
12
  ageInDays: number;
12
13
  ageSource: DocumentAgeSource;
14
+ category: 'stable' | 'stale-after-source-change';
13
15
  level: 'ok' | 'warn' | 'error';
14
16
  message: string;
17
+ nextAction: string;
15
18
  }
16
19
 
17
20
  export class FreshnessCheckService {
@@ -22,8 +25,10 @@ export class FreshnessCheckService {
22
25
  documentPath,
23
26
  ageInDays: documentAge.ageInDays,
24
27
  ageSource: documentAge.source,
28
+ category: 'stable',
25
29
  level: 'ok',
26
30
  message: 'disabled rule skipped',
31
+ nextAction: 'no action required',
27
32
  };
28
33
  }
29
34
 
@@ -39,8 +44,14 @@ export class FreshnessCheckService {
39
44
  documentPath,
40
45
  ageInDays: documentAge.ageInDays,
41
46
  ageSource: documentAge.source,
47
+ category: documentAge.source === 'related-source-change' ? 'stale-after-source-change' : 'stable',
42
48
  level,
43
49
  message: `${documentPath} is ${documentAge.ageInDays} days old`,
50
+ nextAction: level === 'ok'
51
+ ? 'no action required'
52
+ : documentAge.source === 'related-source-change'
53
+ ? 'Refresh the document against the related WI/product/source change'
54
+ : 'Review whether this stable document should have a wider freshness threshold',
44
55
  };
45
56
  }
46
57
  }
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * @layer domain
3
3
  * @unit phase2-extensions
4
+ * @work-item-id WI-122
4
5
  */
5
6
  import { Phase2ExtensionsDomainError } from '../errors/phase2-extensions-domain-error.js';
6
7
 
7
- export type DocumentAgeSource = 'git-log' | 'file-mtime';
8
+ export type DocumentAgeSource = 'git-log' | 'file-mtime' | 'related-source-change';
8
9
 
9
10
  export interface DocumentAgeProps {
10
11
  ageInDays: number;
@@ -25,7 +26,7 @@ export class DocumentAge {
25
26
  if (!Number.isFinite(props.ageInDays) || props.ageInDays < 0) {
26
27
  throw new Phase2ExtensionsDomainError('L4-204', 'ageInDays は 0 以上である必要があります');
27
28
  }
28
- if (props.source !== 'git-log' && props.source !== 'file-mtime') {
29
+ if (props.source !== 'git-log' && props.source !== 'file-mtime' && props.source !== 'related-source-change') {
29
30
  throw new Phase2ExtensionsDomainError('L4-205', 'source が不正です');
30
31
  }
31
32
  return new DocumentAge({
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @layer infrastructure
3
3
  * @unit phase2-extensions
4
+ * @work-item-id WI-122
4
5
  */
5
6
  import type { HarnessConfigV2 } from '../../../config-foundation/domain/harness-config.js';
6
7
  import { DocFreshnessRule } from '../../domain/aggregates/doc-freshness-rule.js';
@@ -25,6 +26,8 @@ type Phase2RuleConfig = {
25
26
  ruleId: string;
26
27
  documentPattern: string;
27
28
  failOnBroken?: boolean;
29
+ owner?: string;
30
+ pointerPolicies?: Record<string, 'fail' | 'warn' | 'skip'>;
28
31
  }>;
29
32
  };
30
33
  };
@@ -73,6 +76,14 @@ export class HarnessConfigFreshnessAdapter implements FreshnessConfigPort {
73
76
  ruleId: 'default-pointer-rule',
74
77
  documentPattern: `${designDocsRoot}/**/*.md`,
75
78
  failOnBroken: true,
79
+ owner: 'documentation',
80
+ pointerPolicies: {
81
+ 'product-doc': 'fail',
82
+ adr: 'fail',
83
+ implementation: 'warn',
84
+ reference: 'warn',
85
+ 'external-url': 'skip',
86
+ },
76
87
  }),
77
88
  ];
78
89
  }
@@ -82,6 +93,8 @@ export class HarnessConfigFreshnessAdapter implements FreshnessConfigPort {
82
93
  ruleId: rule.ruleId,
83
94
  documentPattern: rule.documentPattern,
84
95
  failOnBroken: rule.failOnBroken ?? true,
96
+ owner: rule.owner,
97
+ pointerPolicies: rule.pointerPolicies,
85
98
  }),
86
99
  );
87
100
  }
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @layer presentation
3
3
  * @unit phase2-extensions
4
+ * @work-item-id WI-122
4
5
  */
5
6
  import type { ValidateDocPointersOutput } from '../../application/dto/validate-doc-pointers-output.js';
6
7
 
@@ -12,7 +13,9 @@ export class PointerResultFormatter {
12
13
  formatText(result: ValidateDocPointersOutput): string {
13
14
  return [
14
15
  `documents=${result.summary.totalDocuments} pointers=${result.summary.totalPointers} broken=${result.summary.brokenPointers}`,
15
- ...result.results.map((entry) => `${entry.isResolvable ? 'ok' : 'broken'}: ${entry.pointerTarget}`),
16
+ ...result.results.map((entry) =>
17
+ `${entry.isResolvable ? 'ok' : entry.severity}: ${entry.semanticPointerType} ${entry.pointerTarget} owner=${entry.owner} next=${entry.nextAction}`
18
+ ),
16
19
  ].join('\n');
17
20
  }
18
21
  }
@@ -3,7 +3,7 @@
3
3
  * @unit validator-system
4
4
  *
5
5
  * RunL2ValidatorsUseCase — H08-01: L2バリデータ実行
6
- * @work-item-id WI-110 / WI-111 / WI-140
6
+ * @work-item-id WI-110 / WI-111 / WI-140 / WI-132 / WI-133 / WI-136 / WI-137 / WI-138
7
7
  */
8
8
  import { readFile } from 'node:fs/promises';
9
9
  import { ValidatorId, InvalidValidatorIdError } from '../../domain/value-objects/validator-id.js';
@@ -21,6 +21,8 @@ import type { CliCommandRegistryPort } from '../../domain/ports/cli-command-regi
21
21
  import type { E2eTestFileRegistryPort } from '../../domain/ports/e2e-test-file-registry-port.js';
22
22
  import { CliE2eTestExistenceService } from '../../domain/services/cli-e2e-test-existence-service.js';
23
23
  import type { WorkItemStatusPolicyPort } from '../../domain/ports/work-item-status-policy-port.js';
24
+ import type { ContractTraceabilityPolicyPort } from '../../domain/ports/contract-traceability-policy-port.js';
25
+ import { ContractTraceabilityCoverageService } from '../../domain/services/contract-traceability-coverage-service.js';
24
26
 
25
27
  export interface RunL2ValidatorsUseCaseDeps {
26
28
  validatorRegistry: ValidatorRegistry;
@@ -33,6 +35,7 @@ export interface RunL2ValidatorsUseCaseDeps {
33
35
  cliCommandRegistryPort?: CliCommandRegistryPort;
34
36
  e2eTestFileRegistryPort?: E2eTestFileRegistryPort;
35
37
  workItemStatusPolicyPort?: WorkItemStatusPolicyPort;
38
+ contractTraceabilityPolicyPort?: ContractTraceabilityPolicyPort;
36
39
  }
37
40
 
38
41
  export class RunL2ValidatorsUseCase {
@@ -46,7 +49,9 @@ export class RunL2ValidatorsUseCase {
46
49
  private readonly cliCommandRegistryPort?: CliCommandRegistryPort;
47
50
  private readonly e2eTestFileRegistryPort?: E2eTestFileRegistryPort;
48
51
  private readonly workItemStatusPolicyPort?: WorkItemStatusPolicyPort;
52
+ private readonly contractTraceabilityPolicyPort?: ContractTraceabilityPolicyPort;
49
53
  private readonly cliE2eTestExistenceService = new CliE2eTestExistenceService();
54
+ private readonly contractTraceabilityCoverageService = new ContractTraceabilityCoverageService();
50
55
 
51
56
  constructor(deps: RunL2ValidatorsUseCaseDeps) {
52
57
  this.registry = deps.validatorRegistry;
@@ -59,6 +64,7 @@ export class RunL2ValidatorsUseCase {
59
64
  this.cliCommandRegistryPort = deps.cliCommandRegistryPort;
60
65
  this.e2eTestFileRegistryPort = deps.e2eTestFileRegistryPort;
61
66
  this.workItemStatusPolicyPort = deps.workItemStatusPolicyPort;
67
+ this.contractTraceabilityPolicyPort = deps.contractTraceabilityPolicyPort;
62
68
  }
63
69
 
64
70
  async execute(input: RunL2ValidatorsInput): Promise<readonly ValidationResultContract[]> {
@@ -180,6 +186,28 @@ export class RunL2ValidatorsUseCase {
180
186
  }
181
187
  }
182
188
 
189
+ if (this.contractTraceabilityPolicyPort) {
190
+ const l2015Result = overrideMap.get('L2-015');
191
+ if (l2015Result && !l2015Result.skipped) {
192
+ const inputModel = await this.contractTraceabilityPolicyPort.collect(input.targetPaths);
193
+ const report = this.contractTraceabilityCoverageService.check(inputModel);
194
+ if (report.hasFindings()) {
195
+ const errors = report.findings.map((finding) => ({
196
+ code: { value: 'L2-015', toString: () => 'L2-015' },
197
+ severity: { value: finding.severity, toString: () => finding.severity },
198
+ message: finding.message,
199
+ suggestion: finding.suggestion,
200
+ kind: finding.kind,
201
+ subject: finding.subject,
202
+ sourcePath: finding.sourcePath,
203
+ }));
204
+ overrideMap.set('L2-015', ValidationResult.fail(ValidatorId.create('L2-015'), errors, 0));
205
+ } else {
206
+ overrideMap.set('L2-015', ValidationResult.pass(ValidatorId.create('L2-015'), 0));
207
+ }
208
+ }
209
+ }
210
+
183
211
  const finalResults = definitions.map(
184
212
  (definition) => overrideMap.get(definition.validatorId.value) ?? ValidationResult.skip(definition.validatorId),
185
213
  );
@@ -3,7 +3,7 @@
3
3
  * @unit validator-system
4
4
  *
5
5
  * DI 組み立て — validator-system の全依存関係を構築する
6
- * @work-item-id WI-110 / WI-111
6
+ * @work-item-id WI-110 / WI-111 / WI-132 / WI-133 / WI-136 / WI-137 / WI-138
7
7
  */
8
8
  import { ValidatorId } from './domain/value-objects/validator-id.js';
9
9
  import { ValidatorDefinition } from './domain/value-objects/validator-definition.js';
@@ -24,6 +24,7 @@ import { SourceFileTextScannerAdapter } from './infrastructure/adapters/source-f
24
24
  import { E2eTestFileRegistryAdapter } from './infrastructure/adapters/e2e-test-file-registry-adapter.js';
25
25
  import { CliCommandRegistryAdapter } from './infrastructure/adapters/cli-command-registry-adapter.js';
26
26
  import { TraceabilityWorkItemStatusPolicyAdapter } from './infrastructure/adapters/traceability-work-item-status-policy-adapter.js';
27
+ import { FileSystemContractTraceabilityPolicyAdapter } from './infrastructure/adapters/file-system-contract-traceability-policy-adapter.js';
27
28
  import { PhaseDependencyPhaseGatePolicyAdapter } from './infrastructure/adapters/phase-dependency-phase-gate-policy-adapter.js';
28
29
  import { TraceabilityMetadataPolicyAdapter } from './infrastructure/adapters/traceability-metadata-policy-adapter.js';
29
30
  import { NyquistAcCoveragePolicyAdapter } from './infrastructure/adapters/nyquist-ac-coverage-policy-adapter.js';
@@ -47,7 +48,7 @@ import { join } from 'node:path';
47
48
  const DEFAULT_CONFIG = {
48
49
  preset: 'standard' as const,
49
50
  layers: {
50
- L2: { enabled: true, validators: ['L2-001', 'L2-002', 'L2-003', 'L2-013', 'L2-014'] },
51
+ L2: { enabled: true, validators: ['L2-001', 'L2-002', 'L2-003', 'L2-013', 'L2-014', 'L2-015'] },
51
52
  L3: { enabled: true, validators: ['L3-001', 'L3-002', 'L3-003', 'L3-004'], coverageThreshold: 90, bundleSizeLimit: 512000 },
52
53
  L4: { enabled: true, validators: ['L4-001', 'L4-002', 'L4-003', 'L4-004', 'L4-005'] },
53
54
  },
@@ -81,6 +82,7 @@ export function buildDefaultRegistry(): ValidatorRegistry {
81
82
  createDef('L2-003', 'L2', 'always'),
82
83
  createDef('L2-013', 'L2', 'always', 'CliE2eTestExistenceService'),
83
84
  createDef('L2-014', 'L2', 'always', 'WorkItemStatusPolicyPort'),
85
+ createDef('L2-015', 'L2', 'always', 'ContractTraceabilityPolicyPort'),
84
86
  createDef('L3-001', 'L3', 'always'),
85
87
  createDef('L3-002', 'L3', 'strictOnly'),
86
88
  createDef('L3-003', 'L3', 'always'),
@@ -127,6 +129,7 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
127
129
  const securityScannerPort = new FileSystemSecurityPatternScannerAdapter();
128
130
  const performanceScannerPort = new AstPerformanceScannerAdapter();
129
131
  const workItemStatusPolicyPort = new TraceabilityWorkItemStatusPolicyAdapter(process.cwd());
132
+ const contractTraceabilityPolicyPort = new FileSystemContractTraceabilityPolicyAdapter();
130
133
 
131
134
  const docsRoot = join(process.cwd(), 'docs/product/construction');
132
135
  const cwd = process.cwd();
@@ -151,6 +154,7 @@ export function createValidatorSystemModule(config?: object): ValidatorSystemMod
151
154
  e2eTestFileRegistryPort,
152
155
  cliCommandRegistryPort,
153
156
  workItemStatusPolicyPort,
157
+ contractTraceabilityPolicyPort,
154
158
  });
155
159
 
156
160
  const runL3ValidatorsUseCase = new RunL3ValidatorsUseCase({
@@ -0,0 +1,9 @@
1
+ // @unit validator-system
2
+ // @layer domain
3
+ // @work-item-id WI-132 / WI-133 / WI-136 / WI-137 / WI-138
4
+
5
+ import type { ContractTraceabilityInput } from '../value-objects/contract-traceability-model.js';
6
+
7
+ export interface ContractTraceabilityPolicyPort {
8
+ collect(targetPaths: readonly string[]): Promise<ContractTraceabilityInput>;
9
+ }