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,372 @@
1
+ // @unit traceability-model
2
+ // @layer application
3
+ // @work-item-id WI-288
4
+
5
+ import {
6
+ TRACEABILITY_WORLD_READ_SCHEMA_VERSION,
7
+ type TraceabilityAcceptanceCriterionDto,
8
+ type TraceabilityReadDiagnosticDto,
9
+ type TraceabilityStoryDto,
10
+ type TraceabilityTestReferenceDto,
11
+ type TraceabilityUnitDto,
12
+ type TraceabilityWorkItemDto,
13
+ type TraceabilityWorldReadDto,
14
+ } from "../dto/traceability-world-read-dto.js";
15
+ import type {
16
+ RawTraceabilityStory,
17
+ RawTraceabilityTestAnnotation,
18
+ TraceabilityWorldReadSourcePort,
19
+ } from "../ports/traceability-world-read-source-port.js";
20
+
21
+ const STORY_ID_PATTERN = /^H(?:F\d+|\d{2})-\d{2}$/;
22
+ const WORK_ITEM_ID_PATTERN = /^WI-\d+$/;
23
+ const AC_ID_PATTERN = /^AC-(\d+)$/;
24
+
25
+ const compareStrings = (left: string, right: string): number => (left < right ? -1 : left > right ? 1 : 0);
26
+
27
+ const compareAcIds = (left: string, right: string): number => {
28
+ const leftMatch = AC_ID_PATTERN.exec(left);
29
+ const rightMatch = AC_ID_PATTERN.exec(right);
30
+ if (leftMatch && rightMatch) {
31
+ const difference = Number(leftMatch[1]) - Number(rightMatch[1]);
32
+ if (difference !== 0) return difference;
33
+ }
34
+ return compareStrings(left, right);
35
+ };
36
+
37
+ const compareTuples = (
38
+ left: readonly (string | number | null)[],
39
+ right: readonly (string | number | null)[],
40
+ ): number => {
41
+ const length = Math.max(left.length, right.length);
42
+ for (let index = 0; index < length; index += 1) {
43
+ const leftValue = left[index] ?? "";
44
+ const rightValue = right[index] ?? "";
45
+ if (typeof leftValue === "number" && typeof rightValue === "number") {
46
+ if (leftValue !== rightValue) return leftValue - rightValue;
47
+ continue;
48
+ }
49
+ const difference = compareStrings(String(leftValue), String(rightValue));
50
+ if (difference !== 0) return difference;
51
+ }
52
+ return 0;
53
+ };
54
+
55
+ const groupBy = <T>(values: readonly T[], keyOf: (value: T) => string): ReadonlyMap<string, readonly T[]> => {
56
+ const groups = new Map<string, T[]>();
57
+ for (const value of values) {
58
+ const key = keyOf(value);
59
+ const current = groups.get(key) ?? [];
60
+ current.push(value);
61
+ groups.set(key, current);
62
+ }
63
+ return groups;
64
+ };
65
+
66
+ const diagnostic = (
67
+ code: string,
68
+ subjectId: string | null,
69
+ sourcePaths: readonly string[],
70
+ message: string,
71
+ ): TraceabilityReadDiagnosticDto => ({
72
+ code,
73
+ subjectId,
74
+ sourcePaths: [...sourcePaths].sort(compareStrings),
75
+ message,
76
+ });
77
+
78
+ export interface TraceabilityWorldReadFacadeDeps {
79
+ readonly sourcePort: TraceabilityWorldReadSourcePort;
80
+ }
81
+
82
+ export class TraceabilityWorldReadFacade {
83
+ private readonly sourcePort: TraceabilityWorldReadSourcePort;
84
+
85
+ constructor(deps: TraceabilityWorldReadFacadeDeps) {
86
+ this.sourcePort = deps.sourcePort;
87
+ }
88
+
89
+ async read(): Promise<TraceabilityWorldReadDto> {
90
+ const source = await this.sourcePort.read();
91
+ const diagnostics: TraceabilityReadDiagnosticDto[] = source.diagnostics.map((entry) =>
92
+ diagnostic(entry.code, entry.subjectId, entry.sourcePaths, entry.message),
93
+ );
94
+
95
+ const units = this.admitUnits(source.units, diagnostics);
96
+ const admittedStories = this.admitStories(source.stories, diagnostics);
97
+ const { stories, acceptanceCriteria } = this.projectStories(admittedStories, diagnostics);
98
+ const workItems = this.admitWorkItems(source.workItems, diagnostics);
99
+ const testReferences = this.projectTestReferences(
100
+ source.testAnnotations,
101
+ admittedStories,
102
+ acceptanceCriteria,
103
+ diagnostics,
104
+ );
105
+
106
+ return {
107
+ schemaVersion: TRACEABILITY_WORLD_READ_SCHEMA_VERSION,
108
+ units: units.sort((left, right) =>
109
+ compareTuples([left.unitId, left.definitionPath], [right.unitId, right.definitionPath]),
110
+ ),
111
+ stories: stories.sort((left, right) =>
112
+ compareTuples([left.storyId, left.sourcePath], [right.storyId, right.sourcePath]),
113
+ ),
114
+ acceptanceCriteria: acceptanceCriteria.sort((left, right) => {
115
+ const storyDifference = compareStrings(left.storyId, right.storyId);
116
+ if (storyDifference !== 0) return storyDifference;
117
+ const acDifference = compareAcIds(left.acId, right.acId);
118
+ if (acDifference !== 0) return acDifference;
119
+ return compareStrings(left.sourcePath, right.sourcePath);
120
+ }),
121
+ workItems: workItems.sort((left, right) =>
122
+ compareTuples([left.workItemId, left.descriptionPath], [right.workItemId, right.descriptionPath]),
123
+ ),
124
+ testReferences: testReferences.sort((left, right) => {
125
+ const storyDifference = compareStrings(left.storyId, right.storyId);
126
+ if (storyDifference !== 0) return storyDifference;
127
+ const acDifference = compareAcIds(left.acId, right.acId);
128
+ if (acDifference !== 0) return acDifference;
129
+ return compareTuples(
130
+ [left.binding, left.testType, left.filePath, left.testName],
131
+ [right.binding, right.testType, right.filePath, right.testName],
132
+ );
133
+ }),
134
+ diagnostics: diagnostics.sort((left, right) =>
135
+ compareTuples(
136
+ [left.code, left.subjectId, left.sourcePaths.join("\u0000")],
137
+ [right.code, right.subjectId, right.sourcePaths.join("\u0000")],
138
+ ),
139
+ ),
140
+ };
141
+ }
142
+
143
+ private admitUnits(
144
+ rawUnits: Awaited<ReturnType<TraceabilityWorldReadSourcePort["read"]>>["units"],
145
+ diagnostics: TraceabilityReadDiagnosticDto[],
146
+ ): TraceabilityUnitDto[] {
147
+ const result: TraceabilityUnitDto[] = [];
148
+ for (const [unitId, candidates] of groupBy(rawUnits, (entry) => entry.unitId)) {
149
+ if (candidates.length !== 1) {
150
+ diagnostics.push(
151
+ diagnostic(
152
+ "TM-WORLD-READ-DUPLICATE-UNIT",
153
+ unitId,
154
+ candidates.map((entry) => entry.definitionPath),
155
+ `Unit ID ${unitId} has ${candidates.length} owners`,
156
+ ),
157
+ );
158
+ continue;
159
+ }
160
+ const candidate = candidates[0];
161
+ result.push({
162
+ unitId: candidate.unitId,
163
+ definitionPath: candidate.definitionPath,
164
+ constructionRoot: candidate.constructionRoot,
165
+ });
166
+ }
167
+ return result;
168
+ }
169
+
170
+ private admitStories(
171
+ rawStories: readonly RawTraceabilityStory[],
172
+ diagnostics: TraceabilityReadDiagnosticDto[],
173
+ ): readonly RawTraceabilityStory[] {
174
+ const result: RawTraceabilityStory[] = [];
175
+ for (const [storyId, candidates] of groupBy(rawStories, (entry) => entry.storyId)) {
176
+ if (!STORY_ID_PATTERN.test(storyId)) {
177
+ diagnostics.push(
178
+ diagnostic(
179
+ "TM-WORLD-READ-MALFORMED-STORY-ID",
180
+ storyId,
181
+ candidates.map((entry) => entry.sourcePath),
182
+ `Story ID is not canonical: ${storyId}`,
183
+ ),
184
+ );
185
+ continue;
186
+ }
187
+ if (candidates.length !== 1) {
188
+ diagnostics.push(
189
+ diagnostic(
190
+ "TM-WORLD-READ-DUPLICATE-STORY",
191
+ storyId,
192
+ candidates.map((entry) => entry.sourcePath),
193
+ `Story ID ${storyId} has ${candidates.length} owners`,
194
+ ),
195
+ );
196
+ continue;
197
+ }
198
+ result.push(candidates[0]);
199
+ }
200
+ return result;
201
+ }
202
+
203
+ private projectStories(
204
+ rawStories: readonly RawTraceabilityStory[],
205
+ diagnostics: TraceabilityReadDiagnosticDto[],
206
+ ): {
207
+ readonly stories: TraceabilityStoryDto[];
208
+ readonly acceptanceCriteria: TraceabilityAcceptanceCriterionDto[];
209
+ } {
210
+ const stories: TraceabilityStoryDto[] = [];
211
+ const acceptanceCriteria: TraceabilityAcceptanceCriterionDto[] = [];
212
+ for (const story of rawStories) {
213
+ stories.push({
214
+ storyId: story.storyId,
215
+ legacyIds: [...new Set(story.legacyIds)].sort(compareStrings),
216
+ sourcePath: story.sourcePath,
217
+ line: story.line,
218
+ });
219
+ for (const [acId, candidates] of groupBy(story.acceptanceCriteria, (entry) => entry.acId)) {
220
+ if (!AC_ID_PATTERN.test(acId)) {
221
+ diagnostics.push(
222
+ diagnostic(
223
+ "TM-WORLD-READ-MALFORMED-AC-ID",
224
+ `${story.storyId}:${acId}`,
225
+ [story.sourcePath],
226
+ `Acceptance Criterion ID is not canonical: ${acId}`,
227
+ ),
228
+ );
229
+ continue;
230
+ }
231
+ if (candidates.length !== 1) {
232
+ diagnostics.push(
233
+ diagnostic(
234
+ "TM-WORLD-READ-DUPLICATE-AC",
235
+ `${story.storyId}:${acId}`,
236
+ [story.sourcePath],
237
+ `Acceptance Criterion ${story.storyId}:${acId} has ${candidates.length} owners`,
238
+ ),
239
+ );
240
+ continue;
241
+ }
242
+ acceptanceCriteria.push({
243
+ storyId: story.storyId,
244
+ acId,
245
+ sourcePath: story.sourcePath,
246
+ line: candidates[0].line,
247
+ });
248
+ }
249
+ }
250
+ return { stories, acceptanceCriteria };
251
+ }
252
+
253
+ private admitWorkItems(
254
+ rawWorkItems: Awaited<ReturnType<TraceabilityWorldReadSourcePort["read"]>>["workItems"],
255
+ diagnostics: TraceabilityReadDiagnosticDto[],
256
+ ): TraceabilityWorkItemDto[] {
257
+ const candidatesById = groupBy(rawWorkItems, (entry) => entry.workItemId);
258
+ const result: TraceabilityWorkItemDto[] = [];
259
+ for (const [workItemId, candidates] of candidatesById) {
260
+ if (!WORK_ITEM_ID_PATTERN.test(workItemId)) {
261
+ diagnostics.push(
262
+ diagnostic(
263
+ "TM-WORLD-READ-MALFORMED-WORK-ITEM-ID",
264
+ workItemId,
265
+ candidates.map((entry) => entry.descriptionPath),
266
+ `WorkItem ID is not canonical: ${workItemId}`,
267
+ ),
268
+ );
269
+ continue;
270
+ }
271
+ const matchingCandidates = candidates.filter((entry) => entry.directoryId === entry.workItemId);
272
+ for (const candidate of candidates) {
273
+ if (candidate.directoryId !== candidate.workItemId) {
274
+ diagnostics.push(
275
+ diagnostic(
276
+ "TM-WORLD-READ-WORK-ITEM-ID-MISMATCH",
277
+ workItemId,
278
+ [candidate.descriptionPath],
279
+ `WorkItem directory ${candidate.directoryId} does not match frontmatter ${candidate.workItemId}`,
280
+ ),
281
+ );
282
+ }
283
+ }
284
+ if (matchingCandidates.length === 0) continue;
285
+ if (candidates.length !== 1) {
286
+ diagnostics.push(
287
+ diagnostic(
288
+ "TM-WORLD-READ-DUPLICATE-WORK-ITEM",
289
+ workItemId,
290
+ candidates.map((entry) => entry.descriptionPath),
291
+ `WorkItem ID ${workItemId} has ${candidates.length} owners`,
292
+ ),
293
+ );
294
+ continue;
295
+ }
296
+ const candidate = matchingCandidates[0];
297
+ result.push({
298
+ workItemId,
299
+ legacyIds: candidate.legacyId ? [candidate.legacyId] : [],
300
+ type: candidate.type,
301
+ severity: candidate.severity,
302
+ status: candidate.status,
303
+ affects: [...new Set(candidate.affects)].sort(compareStrings),
304
+ descriptionPath: candidate.descriptionPath,
305
+ });
306
+ }
307
+ return result;
308
+ }
309
+
310
+ private projectTestReferences(
311
+ annotations: readonly RawTraceabilityTestAnnotation[],
312
+ stories: readonly RawTraceabilityStory[],
313
+ acceptanceCriteria: readonly TraceabilityAcceptanceCriterionDto[],
314
+ diagnostics: TraceabilityReadDiagnosticDto[],
315
+ ): TraceabilityTestReferenceDto[] {
316
+ const knownStories = new Map(stories.map((story) => [story.storyId, story]));
317
+ const criteriaByStory = groupBy(acceptanceCriteria, (entry) => entry.storyId);
318
+ const candidates: TraceabilityTestReferenceDto[] = [];
319
+
320
+ for (const annotation of annotations) {
321
+ if (!knownStories.has(annotation.storyId)) {
322
+ diagnostics.push(
323
+ diagnostic(
324
+ "TM-WORLD-READ-UNKNOWN-TEST-STORY",
325
+ annotation.storyId,
326
+ [annotation.filePath],
327
+ `Test annotation references unknown Story ${annotation.storyId}`,
328
+ ),
329
+ );
330
+ continue;
331
+ }
332
+ for (const criterion of criteriaByStory.get(annotation.storyId) ?? []) {
333
+ candidates.push({
334
+ storyId: annotation.storyId,
335
+ acId: criterion.acId,
336
+ binding: "file",
337
+ testType: annotation.testType,
338
+ filePath: annotation.filePath,
339
+ testName: null,
340
+ provenance: [
341
+ {
342
+ sourcePath: annotation.filePath,
343
+ line: annotation.line,
344
+ },
345
+ ],
346
+ });
347
+ }
348
+ }
349
+
350
+ const result: TraceabilityTestReferenceDto[] = [];
351
+ for (const [identity, references] of groupBy(candidates, (entry) =>
352
+ [entry.storyId, entry.acId, entry.binding, entry.testType, entry.filePath, entry.testName ?? "none"].join(
353
+ "\u0000",
354
+ ),
355
+ )) {
356
+ if (references.length !== 1) {
357
+ const first = references[0];
358
+ diagnostics.push(
359
+ diagnostic(
360
+ "TM-WORLD-READ-DUPLICATE-TEST-REFERENCE",
361
+ `${first.storyId}:${first.acId}`,
362
+ references.map((entry) => entry.filePath),
363
+ `TestReference identity ${identity} has ${references.length} owners`,
364
+ ),
365
+ );
366
+ continue;
367
+ }
368
+ result.push(references[0]);
369
+ }
370
+ return result;
371
+ }
372
+ }
@@ -0,0 +1,9 @@
1
+ // @unit traceability-model
2
+ // @layer application
3
+ // @work-item-id WI-305
4
+
5
+ import type { DesignChangeReadResultDto } from "../dto/changed-design-fragment-dto.js";
6
+
7
+ export interface StagedDesignChangeSourcePort {
8
+ observe(stagedFiles: readonly string[]): Promise<DesignChangeReadResultDto>;
9
+ }
@@ -0,0 +1,61 @@
1
+ // @unit traceability-model
2
+ // @layer application
3
+ // @work-item-id WI-288
4
+
5
+ import type { TraceabilityTestType } from "../dto/traceability-world-read-dto.js";
6
+
7
+ export interface RawTraceabilityUnit {
8
+ readonly unitId: string;
9
+ readonly definitionPath: string;
10
+ readonly constructionRoot: string | null;
11
+ }
12
+
13
+ export interface RawTraceabilityAcceptanceCriterion {
14
+ readonly acId: string;
15
+ readonly line: number;
16
+ }
17
+
18
+ export interface RawTraceabilityStory {
19
+ readonly storyId: string;
20
+ readonly legacyIds: readonly string[];
21
+ readonly sourcePath: string;
22
+ readonly line: number;
23
+ readonly acceptanceCriteria: readonly RawTraceabilityAcceptanceCriterion[];
24
+ }
25
+
26
+ export interface RawTraceabilityWorkItem {
27
+ readonly directoryId: string;
28
+ readonly workItemId: string;
29
+ readonly legacyId: string | null;
30
+ readonly type: string;
31
+ readonly severity: string | null;
32
+ readonly status: string | null;
33
+ readonly affects: readonly string[];
34
+ readonly descriptionPath: string;
35
+ }
36
+
37
+ export interface RawTraceabilityTestAnnotation {
38
+ readonly storyId: string;
39
+ readonly filePath: string;
40
+ readonly line: number;
41
+ readonly testType: TraceabilityTestType;
42
+ }
43
+
44
+ export interface RawTraceabilityDiagnostic {
45
+ readonly code: string;
46
+ readonly subjectId: string | null;
47
+ readonly sourcePaths: readonly string[];
48
+ readonly message: string;
49
+ }
50
+
51
+ export interface TraceabilityWorldReadSourceDto {
52
+ readonly units: readonly RawTraceabilityUnit[];
53
+ readonly stories: readonly RawTraceabilityStory[];
54
+ readonly workItems: readonly RawTraceabilityWorkItem[];
55
+ readonly testAnnotations: readonly RawTraceabilityTestAnnotation[];
56
+ readonly diagnostics: readonly RawTraceabilityDiagnostic[];
57
+ }
58
+
59
+ export interface TraceabilityWorldReadSourcePort {
60
+ read(): Promise<TraceabilityWorldReadSourceDto>;
61
+ }
@@ -2,11 +2,14 @@
2
2
  * @layer application
3
3
  * @unit traceability-model
4
4
  * @work-item-id WI-093
5
+ * @work-item-id WI-305
5
6
  *
6
7
  * traceability-model ユニットの Composition Root。
7
8
  * 全コンポーネントを生成・配線し、外部に公開するハンドラー群を返す。
8
9
  */
9
10
 
11
+ import { DesignChangeReadFacade } from "./application/facades/design-change-read-facade.js";
12
+ import { TraceabilityWorldReadFacade } from "./application/facades/traceability-world-read-facade.js";
10
13
  import { ApplyWorkItemMigrationUseCase } from "./application/usecases/apply-work-item-migration-usecase.js";
11
14
  import { ApplyWorkItemStatusUseCase } from "./application/usecases/apply-work-item-status-usecase.js";
12
15
  import { DeriveWorkItemStatusUseCase } from "./application/usecases/derive-work-item-status-usecase.js";
@@ -19,11 +22,13 @@ import { StoryIdAliasResolver } from "./domain/services/story-id-alias-resolver.
19
22
  import { TraceabilityChainBuilder } from "./domain/services/traceability-chain-builder.js";
20
23
  import { WorkItemStatusDerivationService } from "./domain/services/work-item-status-derivation-service.js";
21
24
  import { ProjectRelativePath } from "./domain/value-objects/project-relative-path.js";
25
+ import { FileSystemTraceabilityWorldReadAdapter } from "./infrastructure/adapters/file-system-traceability-world-read-adapter.js";
26
+ import { GitStagedDesignChangeAdapter } from "./infrastructure/adapters/git-staged-design-change-adapter.js";
22
27
  import { FileSystemInceptionPlanGateway } from "./infrastructure/gateways/file-system-inception-plan-gateway.js";
23
28
  import { FileSystemMetadataReader } from "./infrastructure/gateways/file-system-metadata-reader.js";
29
+ import { FileSystemWorkItemIdentityGateway } from "./infrastructure/gateways/file-system-work-item-identity-gateway.js";
24
30
  import { FileSystemWorkItemMigrationApplyGateway } from "./infrastructure/gateways/file-system-work-item-migration-apply-gateway.js";
25
31
  import { FileSystemWorkItemMigrationSourceGateway } from "./infrastructure/gateways/file-system-work-item-migration-source-gateway.js";
26
- import { FileSystemWorkItemIdentityGateway } from "./infrastructure/gateways/file-system-work-item-identity-gateway.js";
27
32
  import { FileSystemWorkItemStatusGateway } from "./infrastructure/gateways/file-system-work-item-status-gateway.js";
28
33
  import { MarkdownDesignDocumentGateway } from "./infrastructure/gateways/markdown-design-document-gateway.js";
29
34
  import { MarkdownStoryCatalogGateway } from "./infrastructure/gateways/markdown-story-catalog-gateway.js";
@@ -34,6 +39,8 @@ import { WorkItemStatusCommandHandler } from "./presentation/cli/work-item-statu
34
39
 
35
40
  export interface TraceabilityModelPathRoots {
36
41
  readonly designDocsRoot: string;
42
+ readonly inceptionRoot?: string;
43
+ readonly testRoots?: readonly string[];
37
44
  }
38
45
 
39
46
  export interface TraceabilityModelModuleOptions {
@@ -48,13 +55,12 @@ const deriveProductDocsRoot = (designDocsRoot: string): string => {
48
55
  return normalized;
49
56
  };
50
57
 
51
- export function createTraceabilityModelModule(
52
- rootDir: string,
53
- options: TraceabilityModelModuleOptions = {},
54
- ) {
58
+ export function createTraceabilityModelModule(rootDir: string, options: TraceabilityModelModuleOptions = {}) {
55
59
  const designDocsRoot = options.pathRoots?.designDocsRoot ?? "docs/product/construction";
56
60
  const productDocsRoot = deriveProductDocsRoot(designDocsRoot);
57
61
  const storyCatalogPath = `${productDocsRoot}/user_stories.md`;
62
+ const inceptionRoot = options.pathRoots?.inceptionRoot ?? "docs/inception";
63
+ const testRoots = options.pathRoots?.testRoots ?? ["scripts/harness/__tests__"];
58
64
 
59
65
  // Infrastructure gateways
60
66
  const storyCatalog = new MarkdownStoryCatalogGateway({ rootDir, storyCatalogPath });
@@ -70,6 +76,15 @@ export function createTraceabilityModelModule(
70
76
  const workItemMigrationApply = new FileSystemWorkItemMigrationApplyGateway({ rootDir });
71
77
  const workItemIdentity = new FileSystemWorkItemIdentityGateway({ rootDir });
72
78
  const workItemStatus = new FileSystemWorkItemStatusGateway({ rootDir });
79
+ const worldReadSource = new FileSystemTraceabilityWorldReadAdapter({
80
+ rootDir,
81
+ productDocsRoot,
82
+ designDocsRoot,
83
+ storyCatalogPath,
84
+ inceptionRoot,
85
+ testRoots,
86
+ unitDefinitionGateway: unitDefinition,
87
+ });
73
88
 
74
89
  // Domain services
75
90
  const metadataValidator = new MetadataValidator({
@@ -86,6 +101,10 @@ export function createTraceabilityModelModule(
86
101
  });
87
102
  const storyIdAliasResolver = new StoryIdAliasResolver(storyCatalog);
88
103
  const workItemStatusDerivationService = new WorkItemStatusDerivationService();
104
+ const worldReadFacade = new TraceabilityWorldReadFacade({
105
+ sourcePort: worldReadSource,
106
+ });
107
+ const designChangeReadFacade = new DesignChangeReadFacade(new GitStagedDesignChangeAdapter(rootDir));
89
108
 
90
109
  // Usecases
91
110
  const validateImplementationMetadataUseCase = new ValidateImplementationMetadataUseCase({
@@ -141,5 +160,7 @@ export function createTraceabilityModelModule(
141
160
  storyCatalog,
142
161
  traceabilityChainBuilder,
143
162
  storyIdAliasResolver,
163
+ worldReadFacade,
164
+ designChangeReadFacade,
144
165
  } as const;
145
166
  }
@@ -1,16 +1,36 @@
1
1
  /**
2
2
  * @layer public-api
3
3
  * @unit traceability-model
4
+ * @work-item-id WI-305
4
5
  *
5
6
  * traceability-model ユニットのバレルエクスポート。
6
7
  */
7
8
 
9
+ export type {
10
+ ChangedDesignFragmentDto,
11
+ DesignChangeReadDiagnosticDto,
12
+ DesignChangeReadResultDto,
13
+ DesignFragmentChangeKind,
14
+ DesignFragmentCorpusRole,
15
+ } from "./application/dto/changed-design-fragment-dto.js";
16
+ export type {
17
+ TraceabilityAcceptanceCriterionDto,
18
+ TraceabilityReadDiagnosticDto,
19
+ TraceabilityStoryDto,
20
+ TraceabilityTestReferenceDto,
21
+ TraceabilityTestType,
22
+ TraceabilityUnitDto,
23
+ TraceabilityWorkItemDto,
24
+ TraceabilityWorldReadDto,
25
+ } from "./application/dto/traceability-world-read-dto.js";
26
+ export { TRACEABILITY_WORLD_READ_SCHEMA_VERSION } from "./application/dto/traceability-world-read-dto.js";
27
+ export { DesignChangeReadFacade } from "./application/facades/design-change-read-facade.js";
28
+ // Application — public World read facade and plain DTO contract
29
+ export { TraceabilityWorldReadFacade } from "./application/facades/traceability-world-read-facade.js";
8
30
  // Composition Root
9
- export { createTraceabilityModelModule } from './composition-root.js';
10
-
31
+ export { createTraceabilityModelModule } from "./composition-root.js";
11
32
  // Domain — value objects
12
- export { ProjectRelativePath } from './domain/value-objects/project-relative-path.js';
13
- export { StoryId } from './domain/value-objects/story-id.js';
14
-
33
+ export { ProjectRelativePath } from "./domain/value-objects/project-relative-path.js";
34
+ export { StoryId } from "./domain/value-objects/story-id.js";
15
35
  // Presentation — CLI handlers
16
- export { ValidateMetadataCommandHandler } from './presentation/cli/validate-metadata-command-handler.js';
36
+ export { ValidateMetadataCommandHandler } from "./presentation/cli/validate-metadata-command-handler.js";