phasegate 0.229.0 → 0.264.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 (154) hide show
  1. package/CHANGELOG.md +73 -1
  2. package/README.ja.md +31 -1
  3. package/README.md +30 -0
  4. package/docs/ADR/031-world-model-ownership-and-corpus-lifecycle.md +155 -0
  5. package/docs/ADR/032-world-node-identity.md +198 -0
  6. package/docs/ADR/033-world-snapshot-canonicalization.md +246 -0
  7. package/docs/ADR/034-world-constraint-semantics.md +218 -0
  8. package/docs/ADR/035-world-adoption-baseline-and-waiver.md +341 -0
  9. package/docs/ADR/036-world-model-and-doc-freshness.md +169 -0
  10. package/docs/ADR/037-world-cli-and-output-contract.md +398 -0
  11. package/docs/contracts/attestation-v2.schema.json +110 -0
  12. package/docs/contracts/requirement-test-matrix.schema.json +15 -0
  13. package/docs/contracts/world-baseline.schema.json +35 -0
  14. package/docs/contracts/world-constraints.schema.json +56 -0
  15. package/docs/contracts/world-debts.schema.json +32 -0
  16. package/docs/contracts/world-obligation-report.schema.json +259 -0
  17. package/docs/contracts/world-waivers.schema.json +32 -0
  18. package/docs/guide/cli-reference.md +45 -0
  19. package/docs/guide/configuration.md +56 -0
  20. package/docs/templates/ci/aidlc-gate.yml +42 -2
  21. package/package.json +1 -1
  22. package/scripts/harness/agent-integration/application/dto/open-world-obligations-context-dto.ts +30 -0
  23. package/scripts/harness/agent-integration/application/ports/world-obligations-query-port.ts +30 -0
  24. package/scripts/harness/agent-integration/application/usecases/get-open-world-obligations-context-usecase.ts +66 -0
  25. package/scripts/harness/agent-integration/infrastructure/adapters/world-model-open-obligations-query-adapter.ts +58 -0
  26. package/scripts/harness/agent-integration/presentation/session-start-hook.ts +37 -1
  27. package/scripts/harness/agent-integration/presentation/world-obligations-session-context.ts +60 -0
  28. package/scripts/harness/attestation/application/dto/attestation-document.ts +17 -4
  29. package/scripts/harness/attestation/application/mappers/attestation-record-mapper.ts +55 -4
  30. package/scripts/harness/attestation/application/ports/sha256-capability.ts +25 -0
  31. package/scripts/harness/attestation/application/ports/world-snapshot-root-provider.ts +10 -0
  32. package/scripts/harness/attestation/application/usecases/produce-attestation-usecase.ts +33 -7
  33. package/scripts/harness/attestation/composition-root.ts +19 -4
  34. package/scripts/harness/attestation/domain/entities/attestation-record.ts +37 -2
  35. package/scripts/harness/attestation/index.ts +13 -2
  36. package/scripts/harness/attestation/infrastructure/adapters/node-crypto-content-hasher-adapter.ts +6 -6
  37. package/scripts/harness/attestation/infrastructure/adapters/node-crypto-sha256-capability.ts +19 -0
  38. package/scripts/harness/attestation/presentation/handlers/attest-handler.ts +5 -2
  39. package/scripts/harness/config-foundation/application/mappers/validator-system-config-mapper.ts +35 -8
  40. package/scripts/harness/config-foundation/application/mappers/world-model-config-mapper.ts +15 -0
  41. package/scripts/harness/config-foundation/domain/harness-config.ts +67 -87
  42. package/scripts/harness/config-foundation/domain/services/preset-resolution-service.ts +77 -88
  43. package/scripts/harness/config-foundation/domain/value-objects/world-config.ts +198 -0
  44. package/scripts/harness/config-foundation/index.ts +14 -15
  45. package/scripts/harness/config-foundation/infrastructure/presets/minimal.json +24 -0
  46. package/scripts/harness/config-foundation/infrastructure/presets/standard.json +24 -0
  47. package/scripts/harness/config-foundation/infrastructure/presets/strict.json +24 -0
  48. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v2.schema.json +83 -0
  49. package/scripts/harness/config-foundation/infrastructure/schemas/harness-config-v3.schema.json +83 -0
  50. package/scripts/harness/harness-api/domain/value-objects/ci-check-result.ts +18 -4
  51. package/scripts/harness/harness-api/domain/value-objects/known-harness-commands.ts +4 -1
  52. package/scripts/harness/integrations/pre-commit.ts +169 -27
  53. package/scripts/harness/main.ts +276 -126
  54. package/scripts/harness/nyquist-validation/application/dto/generate-matrix-output.ts +11 -0
  55. package/scripts/harness/nyquist-validation/application/usecases/check-ac-coverage-gate-usecase.ts +6 -0
  56. package/scripts/harness/nyquist-validation/application/usecases/generate-requirement-test-matrix-usecase.ts +8 -4
  57. package/scripts/harness/nyquist-validation/domain/entities/story-mapping.ts +29 -2
  58. package/scripts/harness/nyquist-validation/domain/services/ac-coverage-gate-policy.ts +28 -0
  59. package/scripts/harness/nyquist-validation/infrastructure/adapters/markdown-requirement-source-adapter.ts +71 -4
  60. package/scripts/harness/nyquist-validation/infrastructure/schema/matrix-schema-loader.ts +4 -0
  61. package/scripts/harness/traceability-model/application/dto/changed-design-fragment-dto.ts +31 -0
  62. package/scripts/harness/traceability-model/application/dto/traceability-world-read-dto.ts +67 -0
  63. package/scripts/harness/traceability-model/application/facades/design-change-read-facade.ts +14 -0
  64. package/scripts/harness/traceability-model/application/facades/traceability-world-read-facade.ts +372 -0
  65. package/scripts/harness/traceability-model/application/ports/staged-design-change-source-port.ts +9 -0
  66. package/scripts/harness/traceability-model/application/ports/traceability-world-read-source-port.ts +61 -0
  67. package/scripts/harness/traceability-model/composition-root.ts +26 -5
  68. package/scripts/harness/traceability-model/index.ts +26 -6
  69. package/scripts/harness/traceability-model/infrastructure/adapters/file-system-traceability-world-read-adapter.ts +223 -0
  70. package/scripts/harness/traceability-model/infrastructure/adapters/git-staged-design-change-adapter.ts +155 -0
  71. package/scripts/harness/traceability-model/infrastructure/parsers/story-catalog-parser.ts +86 -8
  72. package/scripts/harness/validator-system/application/use-cases/run-l2-validators-usecase.ts +31 -0
  73. package/scripts/harness/validator-system/application/use-cases/run-l3-validators-usecase.ts +34 -0
  74. package/scripts/harness/validator-system/composition-root.ts +21 -6
  75. package/scripts/harness/validator-system/domain/ports/world-constraint-admission-policy-port.ts +28 -0
  76. package/scripts/harness/validator-system/domain/ports/world-constraint-rederivation-policy-port.ts +11 -0
  77. package/scripts/harness/validator-system/domain/services/design-change-declaration-policy.ts +75 -0
  78. package/scripts/harness/validator-system/domain/services/world-constraint-admission-service.ts +78 -0
  79. package/scripts/harness/validator-system/domain/services/world-constraint-rederivation-service.ts +76 -0
  80. package/scripts/harness/validator-system/domain/value-objects/validator-id.ts +4 -0
  81. package/scripts/harness/validator-system/infrastructure/adapters/harness-config-validator-config-adapter.ts +38 -16
  82. package/scripts/harness/validator-system/infrastructure/adapters/world-model-constraint-admission-adapter.ts +56 -0
  83. package/scripts/harness/validator-system/infrastructure/adapters/world-model-constraint-rederivation-adapter.ts +56 -0
  84. package/scripts/harness/world-model/application/dto/pinned-design-endpoint-dto.ts +19 -0
  85. package/scripts/harness/world-model/application/dto/world-inspection-dto.ts +43 -0
  86. package/scripts/harness/world-model/application/dto/world-obligation-report-dto.ts +54 -0
  87. package/scripts/harness/world-model/application/dto/world-resolved-config-input.ts +127 -0
  88. package/scripts/harness/world-model/application/dto/world-snapshot-root-dto.ts +8 -0
  89. package/scripts/harness/world-model/application/facades/pinned-design-endpoint-facade.ts +63 -0
  90. package/scripts/harness/world-model/application/facades/world-snapshot-root-facade.ts +19 -0
  91. package/scripts/harness/world-model/application/ports/obligation-report-writer-port.ts +7 -0
  92. package/scripts/harness/world-model/application/ports/world-control-declaration-repository-port.ts +57 -0
  93. package/scripts/harness/world-model/application/ports/world-fact-source-port.ts +17 -0
  94. package/scripts/harness/world-model/application/usecases/build-snapshot-use-case.ts +111 -0
  95. package/scripts/harness/world-model/application/usecases/derive-obligations-use-case.ts +155 -0
  96. package/scripts/harness/world-model/application/usecases/derive-world-obligations-use-case.ts +146 -0
  97. package/scripts/harness/world-model/application/usecases/inspect-world-use-case.ts +74 -0
  98. package/scripts/harness/world-model/application/usecases/pin-constraint-endpoint-use-case.ts +139 -0
  99. package/scripts/harness/world-model/composition-root.ts +247 -0
  100. package/scripts/harness/world-model/domain/entities/constraint-record.ts +148 -0
  101. package/scripts/harness/world-model/domain/entities/control-declarations.ts +274 -0
  102. package/scripts/harness/world-model/domain/entities/edge.ts +41 -0
  103. package/scripts/harness/world-model/domain/entities/extraction-diagnostic.ts +60 -0
  104. package/scripts/harness/world-model/domain/entities/snapshot.ts +75 -0
  105. package/scripts/harness/world-model/domain/entities/world-node.ts +210 -0
  106. package/scripts/harness/world-model/domain/ports/world-hashing-port.ts +8 -0
  107. package/scripts/harness/world-model/domain/services/canonical-json-serializer.ts +113 -0
  108. package/scripts/harness/world-model/domain/services/constraint-evaluator.ts +486 -0
  109. package/scripts/harness/world-model/domain/services/obligation-derivation-service.ts +199 -0
  110. package/scripts/harness/world-model/domain/services/policy-inputs-digest-deriver.ts +66 -0
  111. package/scripts/harness/world-model/domain/services/snapshot-root-deriver.ts +218 -0
  112. package/scripts/harness/world-model/domain/services/text-content-normalizer.ts +30 -0
  113. package/scripts/harness/world-model/domain/services/violation-fingerprint-deriver.ts +183 -0
  114. package/scripts/harness/world-model/domain/value-objects/artifact-kind.ts +58 -0
  115. package/scripts/harness/world-model/domain/value-objects/change-provenance.ts +137 -0
  116. package/scripts/harness/world-model/domain/value-objects/corpus-role.ts +57 -0
  117. package/scripts/harness/world-model/domain/value-objects/declared-key.ts +35 -0
  118. package/scripts/harness/world-model/domain/value-objects/evaluation-id.ts +41 -0
  119. package/scripts/harness/world-model/domain/value-objects/explicit-constraint-relation.ts +53 -0
  120. package/scripts/harness/world-model/domain/value-objects/explicit-node-alias.ts +30 -0
  121. package/scripts/harness/world-model/domain/value-objects/node-pin.ts +34 -0
  122. package/scripts/harness/world-model/domain/value-objects/path-key.ts +103 -0
  123. package/scripts/harness/world-model/domain/value-objects/sha256-digest.ts +39 -0
  124. package/scripts/harness/world-model/domain/value-objects/violation-fingerprint.ts +36 -0
  125. package/scripts/harness/world-model/domain/value-objects/wcr-rule-id.ts +59 -0
  126. package/scripts/harness/world-model/domain/value-objects/world-node-id.ts +222 -0
  127. package/scripts/harness/world-model/index.ts +33 -0
  128. package/scripts/harness/world-model/infrastructure/adapters/adr-fact-extractor.ts +27 -0
  129. package/scripts/harness/world-model/infrastructure/adapters/assembled-world-fact-source.ts +17 -0
  130. package/scripts/harness/world-model/infrastructure/adapters/attestation-fact-extractor.ts +323 -0
  131. package/scripts/harness/world-model/infrastructure/adapters/attestation-sha256-world-hashing-adapter.ts +15 -0
  132. package/scripts/harness/world-model/infrastructure/adapters/composite-design-fact-source.ts +23 -0
  133. package/scripts/harness/world-model/infrastructure/adapters/design-corpus-fact-extractor.ts +285 -0
  134. package/scripts/harness/world-model/infrastructure/adapters/design-fact-extraction.ts +63 -0
  135. package/scripts/harness/world-model/infrastructure/adapters/file-system-obligation-report-writer-adapter.ts +37 -0
  136. package/scripts/harness/world-model/infrastructure/adapters/file-system-world-control-repository-adapters.ts +332 -0
  137. package/scripts/harness/world-model/infrastructure/adapters/integrity-manifest-fact-extractor.ts +101 -0
  138. package/scripts/harness/world-model/infrastructure/adapters/json-fact-extractor-support.ts +151 -0
  139. package/scripts/harness/world-model/infrastructure/adapters/markdown-design-fact-extractor.ts +493 -0
  140. package/scripts/harness/world-model/infrastructure/adapters/matrix-fact-extractor.ts +283 -0
  141. package/scripts/harness/world-model/infrastructure/adapters/product-fact-extractor.ts +29 -0
  142. package/scripts/harness/world-model/infrastructure/adapters/proposal-fact-extractor.ts +29 -0
  143. package/scripts/harness/world-model/infrastructure/adapters/runtime-fact-extraction.ts +19 -0
  144. package/scripts/harness/world-model/infrastructure/adapters/source-metadata-fact-extractor.ts +22 -0
  145. package/scripts/harness/world-model/infrastructure/adapters/test-reference-source-fact-extractor.ts +22 -0
  146. package/scripts/harness/world-model/infrastructure/adapters/traceability-design-fact-adapter.ts +112 -0
  147. package/scripts/harness/world-model/infrastructure/adapters/traceability-world-read-facade-merger.ts +38 -0
  148. package/scripts/harness/world-model/infrastructure/adapters/type-script-source-fact-extractor.ts +236 -0
  149. package/scripts/harness/world-model/infrastructure/adapters/unit-fact-extractor.ts +71 -0
  150. package/scripts/harness/world-model/infrastructure/adapters/world-control-declaration-mapper.ts +383 -0
  151. package/scripts/harness/world-model/presentation/cli/world-command-support.ts +60 -0
  152. package/scripts/harness/world-model/presentation/cli/world-derive-command-handler.ts +109 -0
  153. package/scripts/harness/world-model/presentation/cli/world-inspect-command-handler.ts +194 -0
  154. package/scripts/harness/world-model/presentation/cli/world-pin-command-handler.ts +100 -0
@@ -0,0 +1,223 @@
1
+ // @unit traceability-model
2
+ // @layer infrastructure
3
+ // @work-item-id WI-288
4
+
5
+ import { readdir, readFile } from "node:fs/promises";
6
+ import path from "node:path";
7
+ import type {
8
+ RawTraceabilityDiagnostic,
9
+ RawTraceabilityStory,
10
+ RawTraceabilityTestAnnotation,
11
+ RawTraceabilityUnit,
12
+ RawTraceabilityWorkItem,
13
+ TraceabilityWorldReadSourceDto,
14
+ TraceabilityWorldReadSourcePort,
15
+ } from "../../application/ports/traceability-world-read-source-port.js";
16
+ import type { MarkdownUnitDefinitionGateway } from "../gateways/markdown-unit-definition-gateway.js";
17
+ import { parseTestTags } from "../parsers/source-metadata-parser.js";
18
+ import { parseStoryCatalogEntries } from "../parsers/story-catalog-parser.js";
19
+ import { parseWorkItemFrontmatter } from "../parsers/work-item-frontmatter-parser.js";
20
+
21
+ const compareStrings = (left: string, right: string): number => (left < right ? -1 : left > right ? 1 : 0);
22
+
23
+ const toProjectRelativePath = (rootDir: string, absolutePath: string): string =>
24
+ path.relative(rootDir, absolutePath).split(path.sep).join("/");
25
+
26
+ const collectFiles = async (rootDir: string, accept: (fileName: string) => boolean): Promise<readonly string[]> => {
27
+ const result: string[] = [];
28
+ const visit = async (directory: string): Promise<void> => {
29
+ const entries = await readdir(directory, { withFileTypes: true });
30
+ entries.sort((left, right) => compareStrings(left.name, right.name));
31
+ for (const entry of entries) {
32
+ const absolutePath = path.join(directory, entry.name);
33
+ if (entry.isDirectory()) {
34
+ await visit(absolutePath);
35
+ } else if (entry.isFile() && accept(entry.name)) {
36
+ result.push(absolutePath);
37
+ }
38
+ }
39
+ };
40
+ await visit(rootDir);
41
+ return result;
42
+ };
43
+
44
+ const sourceDiagnostic = (
45
+ code: string,
46
+ subjectId: string | null,
47
+ sourcePaths: readonly string[],
48
+ error: unknown,
49
+ ): RawTraceabilityDiagnostic => ({
50
+ code,
51
+ subjectId,
52
+ sourcePaths,
53
+ message: error instanceof Error ? error.message : String(error),
54
+ });
55
+
56
+ const classifyTestType = (filePath: string): RawTraceabilityTestAnnotation["testType"] => {
57
+ if (filePath.includes("/e2e/") || filePath.includes("/scenario/")) {
58
+ return "scenario";
59
+ }
60
+ if (filePath.includes("/integration/") || /\.it\.test\.[cm]?[jt]sx?$/.test(filePath)) {
61
+ return "it";
62
+ }
63
+ return "unit";
64
+ };
65
+
66
+ export interface FileSystemTraceabilityWorldReadAdapterDeps {
67
+ readonly rootDir: string;
68
+ readonly productDocsRoot: string;
69
+ readonly designDocsRoot: string;
70
+ readonly storyCatalogPath: string;
71
+ readonly inceptionRoot: string;
72
+ readonly testRoots: readonly string[];
73
+ readonly unitDefinitionGateway: MarkdownUnitDefinitionGateway;
74
+ }
75
+
76
+ export class FileSystemTraceabilityWorldReadAdapter implements TraceabilityWorldReadSourcePort {
77
+ private readonly deps: FileSystemTraceabilityWorldReadAdapterDeps;
78
+
79
+ constructor(deps: FileSystemTraceabilityWorldReadAdapterDeps) {
80
+ this.deps = deps;
81
+ }
82
+
83
+ async read(): Promise<TraceabilityWorldReadSourceDto> {
84
+ const diagnostics: RawTraceabilityDiagnostic[] = [];
85
+ const [units, stories, workItems, testAnnotations] = await Promise.all([
86
+ this.readUnits(diagnostics),
87
+ this.readStories(diagnostics),
88
+ this.readWorkItems(diagnostics),
89
+ this.readTestAnnotations(diagnostics),
90
+ ]);
91
+ return {
92
+ units,
93
+ stories,
94
+ workItems,
95
+ testAnnotations,
96
+ diagnostics,
97
+ };
98
+ }
99
+
100
+ private async readUnits(diagnostics: RawTraceabilityDiagnostic[]): Promise<readonly RawTraceabilityUnit[]> {
101
+ try {
102
+ const unitIds = await this.deps.unitDefinitionGateway.getAllUnitNames();
103
+ const result: RawTraceabilityUnit[] = [];
104
+ for (const unitId of unitIds) {
105
+ const constructionRoot = await this.deps.unitDefinitionGateway.findConstructionRoot(unitId);
106
+ result.push({
107
+ unitId,
108
+ definitionPath: path.posix.join(this.deps.productDocsRoot, "units", `${unitId}_unit.md`),
109
+ constructionRoot: constructionRoot?.value ?? null,
110
+ });
111
+ }
112
+ return result;
113
+ } catch (error) {
114
+ diagnostics.push(
115
+ sourceDiagnostic(
116
+ "TM-WORLD-READ-UNIT-SOURCE-ERROR",
117
+ null,
118
+ [path.posix.join(this.deps.productDocsRoot, "units")],
119
+ error,
120
+ ),
121
+ );
122
+ return [];
123
+ }
124
+ }
125
+
126
+ private async readStories(diagnostics: RawTraceabilityDiagnostic[]): Promise<readonly RawTraceabilityStory[]> {
127
+ try {
128
+ const absolutePath = path.join(this.deps.rootDir, this.deps.storyCatalogPath);
129
+ const content = await readFile(absolutePath, "utf8");
130
+ return parseStoryCatalogEntries(content).map((entry) => ({
131
+ storyId: entry.storyId,
132
+ legacyIds: [...entry.legacyIds],
133
+ sourcePath: this.deps.storyCatalogPath,
134
+ line: entry.lineNumber,
135
+ acceptanceCriteria: entry.acceptanceCriteria.map((criterion) => ({
136
+ acId: criterion.acId,
137
+ line: criterion.lineNumber,
138
+ })),
139
+ }));
140
+ } catch (error) {
141
+ diagnostics.push(sourceDiagnostic("TM-WORLD-READ-STORY-SOURCE-ERROR", null, [this.deps.storyCatalogPath], error));
142
+ return [];
143
+ }
144
+ }
145
+
146
+ private async readWorkItems(diagnostics: RawTraceabilityDiagnostic[]): Promise<readonly RawTraceabilityWorkItem[]> {
147
+ const absoluteRoot = path.join(this.deps.rootDir, this.deps.inceptionRoot);
148
+ let files: readonly string[];
149
+ try {
150
+ files = await collectFiles(absoluteRoot, (fileName) => fileName === "description.md");
151
+ } catch (error) {
152
+ diagnostics.push(
153
+ sourceDiagnostic("TM-WORLD-READ-WORK-ITEM-SOURCE-ERROR", null, [this.deps.inceptionRoot], error),
154
+ );
155
+ return [];
156
+ }
157
+
158
+ const result: RawTraceabilityWorkItem[] = [];
159
+ for (const absolutePath of files) {
160
+ const directoryId = path.basename(path.dirname(absolutePath));
161
+ if (!/^WI-/.test(directoryId)) continue;
162
+ const relativePath = toProjectRelativePath(this.deps.rootDir, absolutePath);
163
+ try {
164
+ const frontmatter = parseWorkItemFrontmatter(await readFile(absolutePath, "utf8"));
165
+ if (!frontmatter) {
166
+ diagnostics.push({
167
+ code: "TM-WORLD-READ-MISSING-WORK-ITEM-FRONTMATTER",
168
+ subjectId: directoryId,
169
+ sourcePaths: [relativePath],
170
+ message: `WorkItem ${directoryId} has no readable frontmatter`,
171
+ });
172
+ continue;
173
+ }
174
+ result.push({
175
+ directoryId,
176
+ workItemId: frontmatter.id,
177
+ legacyId: frontmatter.legacyId ?? null,
178
+ type: frontmatter.type,
179
+ severity: frontmatter.severity ?? null,
180
+ status: frontmatter.status ?? null,
181
+ affects: [...(frontmatter.affects ?? [])],
182
+ descriptionPath: relativePath,
183
+ });
184
+ } catch (error) {
185
+ diagnostics.push(sourceDiagnostic("TM-WORLD-READ-WORK-ITEM-PARSE-ERROR", directoryId, [relativePath], error));
186
+ }
187
+ }
188
+ return result;
189
+ }
190
+
191
+ private async readTestAnnotations(
192
+ diagnostics: RawTraceabilityDiagnostic[],
193
+ ): Promise<readonly RawTraceabilityTestAnnotation[]> {
194
+ const result: RawTraceabilityTestAnnotation[] = [];
195
+ for (const testRoot of this.deps.testRoots) {
196
+ const absoluteRoot = path.join(this.deps.rootDir, testRoot);
197
+ let files: readonly string[];
198
+ try {
199
+ files = await collectFiles(absoluteRoot, (fileName) => /\.(?:test|spec)\.[cm]?[jt]sx?$/.test(fileName));
200
+ } catch (error) {
201
+ diagnostics.push(sourceDiagnostic("TM-WORLD-READ-TEST-SOURCE-ERROR", null, [testRoot], error));
202
+ continue;
203
+ }
204
+ for (const absolutePath of files) {
205
+ const relativePath = toProjectRelativePath(this.deps.rootDir, absolutePath);
206
+ try {
207
+ const tags = parseTestTags(await readFile(absolutePath, "utf8"));
208
+ for (const tag of tags) {
209
+ result.push({
210
+ storyId: tag.value,
211
+ filePath: relativePath,
212
+ line: tag.lineNumber,
213
+ testType: classifyTestType(relativePath),
214
+ });
215
+ }
216
+ } catch (error) {
217
+ diagnostics.push(sourceDiagnostic("TM-WORLD-READ-TEST-PARSE-ERROR", null, [relativePath], error));
218
+ }
219
+ }
220
+ }
221
+ return result;
222
+ }
223
+ }
@@ -0,0 +1,155 @@
1
+ // @unit traceability-model
2
+ // @layer infrastructure
3
+ // @work-item-id WI-305
4
+
5
+ import { execFileSync } from "node:child_process";
6
+ import type {
7
+ ChangedDesignFragmentDto,
8
+ DesignChangeReadDiagnosticDto,
9
+ DesignChangeReadResultDto,
10
+ DesignFragmentCorpusRole,
11
+ } from "../../application/dto/changed-design-fragment-dto.js";
12
+ import type { StagedDesignChangeSourcePort } from "../../application/ports/staged-design-change-source-port.js";
13
+
14
+ const FRAGMENT = /^\s*<!--\s*@world-fragment-id\s+(\S+)\s*-->\s*$/;
15
+ const WORK_ITEM = /^\s*<!--\s*@work-item-id\s+(.+?)\s*-->\s*$/;
16
+ const REFLECTS = /^\s*<!--\s*@world-reflects\s+(\S+)\s*-->\s*$/;
17
+ const HEADING = /^#{1,6}\s+/;
18
+ const FENCE = /^\s*(`{3,}|~{3,})/;
19
+ const compare = (left: string, right: string): number => (left < right ? -1 : left > right ? 1 : 0);
20
+
21
+ interface ParsedFragment {
22
+ readonly declaredKey: string;
23
+ readonly content: string;
24
+ readonly workItemIds: readonly string[];
25
+ readonly reflectionTargets: readonly string[];
26
+ }
27
+
28
+ interface ParsedFragmentSet {
29
+ readonly fragments: readonly ParsedFragment[];
30
+ readonly validPrelude: boolean;
31
+ }
32
+
33
+ const corpusRoleOf = (filePath: string): DesignFragmentCorpusRole | null => {
34
+ if (filePath.startsWith("docs/product/")) return "product";
35
+ if (filePath.startsWith("docs/inception/")) return "inception";
36
+ return null;
37
+ };
38
+
39
+ const uniqueSorted = (values: readonly string[]): readonly string[] =>
40
+ Object.freeze([...new Set(values)].sort(compare));
41
+
42
+ const parseFragments = (text: string): ParsedFragmentSet => {
43
+ const lines = text.replace(/\r\n?/g, "\n").split("\n");
44
+ const markers: { readonly index: number; readonly key: string }[] = [];
45
+ let activeFence: string | null = null;
46
+ for (let index = 0; index < lines.length; index += 1) {
47
+ const fence = FENCE.exec(lines[index]);
48
+ if (fence) {
49
+ const character = fence[1][0];
50
+ activeFence = activeFence === null ? character : activeFence === character ? null : activeFence;
51
+ continue;
52
+ }
53
+ if (activeFence !== null) continue;
54
+ const marker = FRAGMENT.exec(lines[index]);
55
+ if (marker) markers.push({ index, key: marker[1] });
56
+ }
57
+
58
+ let validPrelude = true;
59
+ const fragments = markers.flatMap((marker, markerIndex): readonly ParsedFragment[] => {
60
+ const end = markers[markerIndex + 1]?.index ?? lines.length;
61
+ let headingIndex = marker.index + 1;
62
+ while (headingIndex < end && (WORK_ITEM.test(lines[headingIndex]) || REFLECTS.test(lines[headingIndex]))) {
63
+ headingIndex += 1;
64
+ }
65
+ if (headingIndex >= end || !HEADING.test(lines[headingIndex])) {
66
+ validPrelude = false;
67
+ return [];
68
+ }
69
+ const metadata = lines.slice(marker.index, headingIndex);
70
+ return [
71
+ Object.freeze({
72
+ declaredKey: marker.key,
73
+ content: lines.slice(marker.index, end).join("\n"),
74
+ workItemIds: uniqueSorted(metadata.flatMap((line) => WORK_ITEM.exec(line)?.[1].match(/\bWI-\d+\b/g) ?? [])),
75
+ reflectionTargets: uniqueSorted(metadata.flatMap((line) => REFLECTS.exec(line)?.[1] ?? [])),
76
+ }),
77
+ ];
78
+ });
79
+ return Object.freeze({ fragments: Object.freeze(fragments), validPrelude });
80
+ };
81
+
82
+ const indexByKey = (fragments: readonly ParsedFragment[]): Map<string, ParsedFragment> | null => {
83
+ const result = new Map<string, ParsedFragment>();
84
+ for (const fragment of fragments) {
85
+ if (result.has(fragment.declaredKey)) return null;
86
+ result.set(fragment.declaredKey, fragment);
87
+ }
88
+ return result;
89
+ };
90
+
91
+ export class GitStagedDesignChangeAdapter implements StagedDesignChangeSourcePort {
92
+ constructor(private readonly rootDir: string) {}
93
+
94
+ async observe(stagedFiles: readonly string[]): Promise<DesignChangeReadResultDto> {
95
+ const fragments: ChangedDesignFragmentDto[] = [];
96
+ const diagnostics: DesignChangeReadDiagnosticDto[] = [];
97
+ for (const filePath of [...new Set(stagedFiles)].sort(compare)) {
98
+ const corpusRole = corpusRoleOf(filePath);
99
+ if (corpusRole === null || !filePath.endsWith(".md")) continue;
100
+ const beforeText = this.readBlob(`HEAD:${filePath}`);
101
+ const afterText = this.readBlob(`:${filePath}`);
102
+ if (beforeText === null && afterText === null) {
103
+ diagnostics.push(Object.freeze({ code: "staged-design-content-unavailable", path: filePath }));
104
+ continue;
105
+ }
106
+ const beforeParsed = parseFragments(beforeText ?? "");
107
+ const afterParsed = parseFragments(afterText ?? "");
108
+ if (!beforeParsed.validPrelude || !afterParsed.validPrelude) {
109
+ diagnostics.push(Object.freeze({ code: "invalid-staged-fragment-prelude", path: filePath }));
110
+ continue;
111
+ }
112
+ const before = indexByKey(beforeParsed.fragments);
113
+ const after = indexByKey(afterParsed.fragments);
114
+ if (before === null || after === null) {
115
+ diagnostics.push(Object.freeze({ code: "duplicate-staged-fragment-key", path: filePath }));
116
+ continue;
117
+ }
118
+ const keys = uniqueSorted([...before.keys(), ...after.keys()]);
119
+ for (const key of keys) {
120
+ const oldFragment = before.get(key);
121
+ const newFragment = after.get(key);
122
+ if (oldFragment?.content === newFragment?.content) continue;
123
+ const observed = newFragment ?? oldFragment;
124
+ if (!observed) continue;
125
+ fragments.push(
126
+ Object.freeze({
127
+ corpusRole,
128
+ declaredKey: key,
129
+ path: filePath,
130
+ changeKind: oldFragment === undefined ? "added" : newFragment === undefined ? "deleted" : "modified",
131
+ workItemIds: observed.workItemIds,
132
+ reflectionTargets: observed.reflectionTargets,
133
+ }),
134
+ );
135
+ }
136
+ }
137
+ if (diagnostics.length > 0) {
138
+ return Object.freeze({ state: "unavailable", diagnostics: Object.freeze(diagnostics) });
139
+ }
140
+ return Object.freeze({ state: "available", fragments: Object.freeze(fragments), diagnostics: Object.freeze([]) });
141
+ }
142
+
143
+ private readBlob(spec: string): string | null {
144
+ try {
145
+ return execFileSync("git", ["show", spec], {
146
+ cwd: this.rootDir,
147
+ encoding: "utf8",
148
+ stdio: ["ignore", "pipe", "ignore"],
149
+ maxBuffer: 8 * 1024 * 1024,
150
+ });
151
+ } catch {
152
+ return null;
153
+ }
154
+ }
155
+ }
@@ -10,20 +10,29 @@ export interface ParsedStoryCatalog {
10
10
  readonly aliasMap: ReadonlyMap<string, string>;
11
11
  }
12
12
 
13
+ export interface ParsedStoryCatalogEntry {
14
+ readonly storyId: string;
15
+ readonly legacyIds: readonly string[];
16
+ readonly lineNumber: number;
17
+ readonly acceptanceCriteria: readonly {
18
+ readonly acId: string;
19
+ readonly lineNumber: number;
20
+ }[];
21
+ }
22
+
13
23
  const STORY_ID_LINE_PATTERN = /\bH(?:F\d+|[0-9]{2})-[0-9]{2}\b/g;
14
- const TABLE_ALIAS_PATTERN =
15
- /\|\s*(H(?:F\d+|[0-9]{2})-[0-9]{2})\s*\|.*?\|\s*(US-[0-9]{3})\s*\|/g;
24
+ const TABLE_ALIAS_PATTERN = /\|\s*(H(?:F\d+|[0-9]{2})-[0-9]{2})\s*\|.*?\|\s*(US-[0-9]{3})\s*\|/g;
16
25
  // 旧US ラベルは Markdown 強調(**旧US**:)で囲まれることがあるため `\**` を許容する。
17
- const INLINE_ALIAS_PATTERN =
18
- /(H(?:F\d+|[0-9]{2})-[0-9]{2}).*?旧US\**\s*[::]\s*(US-[0-9]{3})/g;
26
+ const INLINE_ALIAS_PATTERN = /(H(?:F\d+|[0-9]{2})-[0-9]{2}).*?旧US\**\s*[::]\s*(US-[0-9]{3})/g;
19
27
 
20
28
  function execAll(pattern: RegExp, text: string): RegExpExecArray[] {
21
29
  const results: RegExpExecArray[] = [];
22
- let match: RegExpExecArray | null;
23
30
  // Reset lastIndex to ensure clean iteration
24
31
  pattern.lastIndex = 0;
25
- while ((match = pattern.exec(text)) !== null) {
32
+ let match = pattern.exec(text);
33
+ while (match !== null) {
26
34
  results.push(match);
35
+ match = pattern.exec(text);
27
36
  }
28
37
  return results;
29
38
  }
@@ -37,7 +46,7 @@ export function parseStoryCatalog(content: string): ParsedStoryCatalog {
37
46
  const aliasMap = new Map<string, string>();
38
47
 
39
48
  // 正規StoryIdを収集
40
- const lines = content.split('\n');
49
+ const lines = content.split("\n");
41
50
  for (const line of lines) {
42
51
  const matches = execAll(STORY_ID_LINE_PATTERN, line);
43
52
  for (let j = 0; j < matches.length; j++) {
@@ -88,10 +97,79 @@ export function parseStoryCatalog(content: string): ParsedStoryCatalog {
88
97
  }
89
98
 
90
99
  const storyIds: string[] = [];
91
- storyIdSet.forEach((id) => storyIds.push(id));
100
+ storyIdSet.forEach((id) => {
101
+ storyIds.push(id);
102
+ });
92
103
 
93
104
  return {
94
105
  storyIds: Object.freeze(storyIds),
95
106
  aliasMap,
96
107
  };
97
108
  }
109
+
110
+ /**
111
+ * Story headingをowner locatorとして、heading scope内のlegacy IDとACを構造化する。
112
+ * `####`以下はStory内の補足見出しとして扱い、次のlevel 1-3見出しでscopeを閉じる。
113
+ */
114
+ export function parseStoryCatalogEntries(content: string): readonly ParsedStoryCatalogEntry[] {
115
+ const lines = content.split(/\r?\n/);
116
+ const STORY_HEADING_PATTERN = /^###\s+(H(?:F\d+|[0-9]{2})-[0-9]{2})\b/;
117
+ const CLOSING_HEADING_PATTERN = /^#{1,3}\s+/;
118
+ const LEGACY_PATTERN = /旧US\**\s*[::]\s*(US-[0-9]{3})/;
119
+ const AC_PATTERN = /^\s*-\s*\[[ xX]\]\s*(AC-[0-9]+)\s*[::]/;
120
+ const entries: {
121
+ storyId: string;
122
+ legacyIds: string[];
123
+ lineNumber: number;
124
+ acceptanceCriteria: { acId: string; lineNumber: number }[];
125
+ }[] = [];
126
+ let current:
127
+ | {
128
+ storyId: string;
129
+ legacyIds: string[];
130
+ lineNumber: number;
131
+ acceptanceCriteria: { acId: string; lineNumber: number }[];
132
+ }
133
+ | undefined;
134
+
135
+ for (let index = 0; index < lines.length; index += 1) {
136
+ const line = lines[index];
137
+ const storyHeading = STORY_HEADING_PATTERN.exec(line);
138
+ if (storyHeading) {
139
+ current = {
140
+ storyId: storyHeading[1],
141
+ legacyIds: [],
142
+ lineNumber: index + 1,
143
+ acceptanceCriteria: [],
144
+ };
145
+ entries.push(current);
146
+ continue;
147
+ }
148
+ if (CLOSING_HEADING_PATTERN.test(line)) {
149
+ current = undefined;
150
+ continue;
151
+ }
152
+ if (!current) continue;
153
+
154
+ const legacy = LEGACY_PATTERN.exec(line);
155
+ if (legacy && !current.legacyIds.includes(legacy[1])) {
156
+ current.legacyIds.push(legacy[1]);
157
+ }
158
+ const acceptanceCriterion = AC_PATTERN.exec(line);
159
+ if (acceptanceCriterion) {
160
+ current.acceptanceCriteria.push({
161
+ acId: acceptanceCriterion[1],
162
+ lineNumber: index + 1,
163
+ });
164
+ }
165
+ }
166
+
167
+ return entries.map((entry) => ({
168
+ storyId: entry.storyId,
169
+ legacyIds: [...entry.legacyIds],
170
+ lineNumber: entry.lineNumber,
171
+ acceptanceCriteria: entry.acceptanceCriteria.map((criterion) => ({
172
+ ...criterion,
173
+ })),
174
+ }));
175
+ }
@@ -4,6 +4,7 @@
4
4
  *
5
5
  * RunL2ValidatorsUseCase — H08-01: L2バリデータ実行
6
6
  * @work-item-id WI-110 / WI-111 / WI-140 / WI-132 / WI-133 / WI-136 / WI-137 / WI-138
7
+ * @work-item-id WI-301
7
8
  */
8
9
  import { readFile } from "node:fs/promises";
9
10
  import type { CliCommandRegistryPort } from "../../domain/ports/cli-command-registry-port.js";
@@ -15,6 +16,7 @@ import type { PhaseGatePolicyPort } from "../../domain/ports/phase-gate-policy-p
15
16
  import type { TestQualityAnalyzerPort } from "../../domain/ports/test-quality-analyzer-port.js";
16
17
  import type { ValidatorConfigPort } from "../../domain/ports/validator-config-port.js";
17
18
  import type { WorkItemStatusPolicyPort } from "../../domain/ports/work-item-status-policy-port.js";
19
+ import type { WorldConstraintAdmissionPolicyPort } from "../../domain/ports/world-constraint-admission-policy-port.js";
18
20
  import { CliE2eTestExistenceService } from "../../domain/services/cli-e2e-test-existence-service.js";
19
21
  import { ContractTraceabilityCoverageService } from "../../domain/services/contract-traceability-coverage-service.js";
20
22
  import { CoverageAttestationGatingService } from "../../domain/services/coverage-attestation-gating-service.js";
@@ -23,6 +25,7 @@ import {
23
25
  type ValidatorExecutionService,
24
26
  } from "../../domain/services/validator-execution-service.js";
25
27
  import type { ValidatorRegistry } from "../../domain/services/validator-registry.js";
28
+ import { WorldConstraintAdmissionService } from "../../domain/services/world-constraint-admission-service.js";
26
29
  import { type HarnessErrorLike, ValidationResult } from "../../domain/value-objects/validation-result.js";
27
30
  import { ValidatorId } from "../../domain/value-objects/validator-id.js";
28
31
  import type { RunL2ValidatorsInput } from "../dto/run-l2-validators-input.js";
@@ -42,6 +45,7 @@ export interface RunL2ValidatorsUseCaseDeps {
42
45
  workItemStatusPolicyPort?: WorkItemStatusPolicyPort;
43
46
  contractTraceabilityPolicyPort?: ContractTraceabilityPolicyPort;
44
47
  coverageAttestationGatingPolicyPort?: CoverageAttestationGatingPolicyPort;
48
+ worldConstraintAdmissionPolicyPort?: WorldConstraintAdmissionPolicyPort;
45
49
  }
46
50
 
47
51
  export class RunL2ValidatorsUseCase {
@@ -57,9 +61,11 @@ export class RunL2ValidatorsUseCase {
57
61
  private readonly workItemStatusPolicyPort?: WorkItemStatusPolicyPort;
58
62
  private readonly contractTraceabilityPolicyPort?: ContractTraceabilityPolicyPort;
59
63
  private readonly coverageAttestationGatingPolicyPort?: CoverageAttestationGatingPolicyPort;
64
+ private readonly worldConstraintAdmissionPolicyPort?: WorldConstraintAdmissionPolicyPort;
60
65
  private readonly cliE2eTestExistenceService = new CliE2eTestExistenceService();
61
66
  private readonly contractTraceabilityCoverageService = new ContractTraceabilityCoverageService();
62
67
  private readonly coverageAttestationGatingService = new CoverageAttestationGatingService();
68
+ private readonly worldConstraintAdmissionService = new WorldConstraintAdmissionService();
63
69
 
64
70
  constructor(deps: RunL2ValidatorsUseCaseDeps) {
65
71
  this.registry = deps.validatorRegistry;
@@ -74,6 +80,7 @@ export class RunL2ValidatorsUseCase {
74
80
  this.workItemStatusPolicyPort = deps.workItemStatusPolicyPort;
75
81
  this.contractTraceabilityPolicyPort = deps.contractTraceabilityPolicyPort;
76
82
  this.coverageAttestationGatingPolicyPort = deps.coverageAttestationGatingPolicyPort;
83
+ this.worldConstraintAdmissionPolicyPort = deps.worldConstraintAdmissionPolicyPort;
77
84
  }
78
85
 
79
86
  async execute(input: RunL2ValidatorsInput): Promise<readonly ValidationResultContract[]> {
@@ -241,6 +248,30 @@ export class RunL2ValidatorsUseCase {
241
248
  }
242
249
  }
243
250
 
251
+ if (this.worldConstraintAdmissionPolicyPort) {
252
+ const l2017Result = overrideMap.get("L2-017");
253
+ if (l2017Result && !l2017Result.skipped) {
254
+ const observation = await this.worldConstraintAdmissionPolicyPort.collect();
255
+ const findings = this.worldConstraintAdmissionService.evaluate(observation);
256
+ if (findings.length > 0) {
257
+ const errors: HarnessErrorLike[] = findings.map((finding) => ({
258
+ code: { value: "L2-017", toString: () => "L2-017" },
259
+ severity: { value: finding.severity, toString: () => finding.severity },
260
+ message: finding.message,
261
+ suggestion: finding.suggestion,
262
+ ruleId: finding.ruleId,
263
+ violationFingerprint: finding.violationFingerprint,
264
+ constraintId: finding.constraintId,
265
+ classification: finding.classification,
266
+ sourcePath: finding.sourcePath,
267
+ }));
268
+ overrideMap.set("L2-017", ValidationResult.fail(ValidatorId.create("L2-017"), errors, 0));
269
+ } else {
270
+ overrideMap.set("L2-017", ValidationResult.pass(ValidatorId.create("L2-017"), 0));
271
+ }
272
+ }
273
+ }
274
+
244
275
  const finalResults = definitions.map(
245
276
  (definition) => overrideMap.get(definition.validatorId.value) ?? ValidationResult.skip(definition.validatorId),
246
277
  );
@@ -2,6 +2,7 @@
2
2
  * @layer application
3
3
  * @unit validator-system
4
4
  * @work-item-id WI-212
5
+ * @work-item-id WI-302
5
6
  *
6
7
  * RunL3ValidatorsUseCase — H08-02: L3バリデータ実行
7
8
  */
@@ -13,6 +14,7 @@ import type { InjectionScanPolicyPort } from "../../domain/ports/injection-scan-
13
14
  import type { PerformanceScannerPort } from "../../domain/ports/performance-scanner-port.js";
14
15
  import type { SecurityPatternScannerPort } from "../../domain/ports/security-pattern-scanner-port.js";
15
16
  import type { ValidatorConfigPort } from "../../domain/ports/validator-config-port.js";
17
+ import type { WorldConstraintRederivationPolicyPort } from "../../domain/ports/world-constraint-rederivation-policy-port.js";
16
18
  import { CoverageAttestationVerificationService } from "../../domain/services/coverage-attestation-verification-service.js";
17
19
  import { InjectionPatternScanService } from "../../domain/services/injection-pattern-scan-service.js";
18
20
  import {
@@ -20,6 +22,7 @@ import {
20
22
  type ValidatorExecutionService,
21
23
  } from "../../domain/services/validator-execution-service.js";
22
24
  import { ValidatorLanguageCapabilityService } from "../../domain/services/validator-language-capability-service.js";
25
+ import { WorldConstraintRederivationService } from "../../domain/services/world-constraint-rederivation-service.js";
23
26
  import type { ValidatorRegistry } from "../../domain/services/validator-registry.js";
24
27
  import type { HarnessErrorLike } from "../../domain/value-objects/validation-result.js";
25
28
  import { ValidationResult } from "../../domain/value-objects/validation-result.js";
@@ -54,6 +57,8 @@ export interface RunL3ValidatorsUseCaseDeps {
54
57
  injectionScanPolicyPort?: InjectionScanPolicyPort;
55
58
  /** WI-268 / ADR-030 §Decision.1・§Decision.3.② 第2段: L3-007 (coverage-attestation-verification, fail-closed) 用ポート。 */
56
59
  coverageAttestationVerificationPolicyPort?: CoverageAttestationVerificationPolicyPort;
60
+ /** WI-302: L3-008 authoritative World clean-corpus re-derivation port. */
61
+ worldConstraintRederivationPolicyPort?: WorldConstraintRederivationPolicyPort;
57
62
  /** L3-005 のスコープ対象 story-id(config layers.L3.acBoundStories 由来。既定 [])。 */
58
63
  acBoundStories?: readonly string[];
59
64
  }
@@ -70,10 +75,12 @@ export class RunL3ValidatorsUseCase {
70
75
  private readonly performanceScannerPort?: PerformanceScannerPort;
71
76
  private readonly injectionScanPolicyPort?: InjectionScanPolicyPort;
72
77
  private readonly coverageAttestationVerificationPolicyPort?: CoverageAttestationVerificationPolicyPort;
78
+ private readonly worldConstraintRederivationPolicyPort?: WorldConstraintRederivationPolicyPort;
73
79
  private readonly acBoundStories: readonly string[];
74
80
  private readonly languageCapabilityService = new ValidatorLanguageCapabilityService();
75
81
  private readonly injectionScanService = new InjectionPatternScanService();
76
82
  private readonly coverageAttestationVerificationService = new CoverageAttestationVerificationService();
83
+ private readonly worldConstraintRederivationService = new WorldConstraintRederivationService();
77
84
 
78
85
  constructor(deps: RunL3ValidatorsUseCaseDeps) {
79
86
  this.registry = deps.validatorRegistry;
@@ -87,6 +94,7 @@ export class RunL3ValidatorsUseCase {
87
94
  this.performanceScannerPort = deps.performanceScannerPort;
88
95
  this.injectionScanPolicyPort = deps.injectionScanPolicyPort;
89
96
  this.coverageAttestationVerificationPolicyPort = deps.coverageAttestationVerificationPolicyPort;
97
+ this.worldConstraintRederivationPolicyPort = deps.worldConstraintRederivationPolicyPort;
90
98
  this.acBoundStories = deps.acBoundStories ?? [];
91
99
  }
92
100
 
@@ -305,6 +313,32 @@ export class RunL3ValidatorsUseCase {
305
313
  }
306
314
  }
307
315
 
316
+ // WI-302 / ADR-030 trust root: L3-008 independently re-derives World obligations from the
317
+ // current corpus and versioned control declarations. The adapter has no persisted-report input.
318
+ if (this.worldConstraintRederivationPolicyPort) {
319
+ const l3008Result = overrideMap.get("L3-008");
320
+ if (l3008Result && !l3008Result.skipped) {
321
+ const observation = await this.worldConstraintRederivationPolicyPort.collect();
322
+ const findings = this.worldConstraintRederivationService.evaluate(observation);
323
+ if (findings.length > 0) {
324
+ const errors: HarnessErrorLike[] = findings.map((finding) => ({
325
+ code: { value: "L3-008", toString: () => "L3-008" },
326
+ severity: { value: finding.severity, toString: () => finding.severity },
327
+ message: finding.message,
328
+ suggestion: finding.suggestion,
329
+ sourcePath: finding.sourcePath,
330
+ ruleId: finding.ruleId,
331
+ violationFingerprint: finding.violationFingerprint,
332
+ constraintId: finding.constraintId,
333
+ classification: finding.classification,
334
+ }));
335
+ overrideMap.set("L3-008", ValidationResult.fail(ValidatorId.create("L3-008"), errors, 0));
336
+ } else {
337
+ overrideMap.set("L3-008", ValidationResult.pass(ValidatorId.create("L3-008"), 0));
338
+ }
339
+ }
340
+ }
341
+
308
342
  const finalResults = definitions.map(
309
343
  (definition) => overrideMap.get(definition.validatorId.value) ?? ValidationResult.skip(definition.validatorId),
310
344
  );