@thomasminh1995/depverdict 0.6.0-alpha.1

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 (252) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +518 -0
  3. package/bin/depverdict.js +7 -0
  4. package/bin/upgradelens.js +7 -0
  5. package/docs/GR-01-Semantic-Grounding-Failure-Analysis.md +309 -0
  6. package/docs/GR-02-Versioned-Action-Evaluation-Criteria.md +187 -0
  7. package/docs/GR-03-Extractive-Contract-Safety-Experiment.md +242 -0
  8. package/docs/GR-04-Versioned-Production-Extractive-Contract.md +154 -0
  9. package/docs/IA-01-Repository-Usage-Discovery.md +122 -0
  10. package/docs/IA-02-Repository-Impact-Analysis.md +118 -0
  11. package/docs/IA-03-Repository-Impact-Evidence.md +160 -0
  12. package/docs/IA-04-CLI-Orchestration.md +235 -0
  13. package/docs/IA-05-Real-Provider-Validation.md +235 -0
  14. package/docs/IA-05-VinGrade-Validation.md +339 -0
  15. package/docs/MVP-01.md +48 -0
  16. package/docs/MVP-02-Architecture.md +536 -0
  17. package/docs/MVP-02-CLI-HTTP-Runtime.md +50 -0
  18. package/docs/MVP-02-HTTP-Lifecycle.md +37 -0
  19. package/docs/MVP-02-Knowledge-Manifest-Generation.md +75 -0
  20. package/docs/MVP-02-Knowledge-Manifest.md +279 -0
  21. package/docs/MVP-02-Knowledge-Research-Orchestration.md +62 -0
  22. package/docs/MVP-02-Knowledge-Store.md +87 -0
  23. package/docs/MVP-02-PyPI-Registry-Adapter.md +97 -0
  24. package/docs/MVP-02-Research-Planning.md +196 -0
  25. package/docs/MVP-02-Source-Provenance.md +81 -0
  26. package/docs/MVP-02-npm-Registry-Adapter.md +99 -0
  27. package/docs/RR-01-End-to-End-and-Real-Provider-Validation.md +393 -0
  28. package/docs/RR-01-RERUN-Extractive-Contract-Validation.md +566 -0
  29. package/docs/RR-02-Full-Product-Workflow-and-Developer-CLI-UX-Review.md +35 -0
  30. package/docs/RR02-FIX-01-Persistent-Qualification-Resolution.md +32 -0
  31. package/docs/RR02-FIX-02-Stage-aware-CLI-Progress-and-Heartbeat.md +28 -0
  32. package/docs/RR02-FIX-03-npm-Capture-Evidence-Exclusion.md +33 -0
  33. package/docs/RR02-FIX-03A-Complete-Package-Exclusion-and-Evidence-Commit.md +223 -0
  34. package/docs/RR02-FIX-04-Event-loop-safe-Heartbeat.md +393 -0
  35. package/docs/RR02-FIX-05-Materialize-Persisted-Qualification.md +207 -0
  36. package/docs/RR02-RERUN-CLI-Qualification-Progress-UX-and-Package-Validation.md +342 -0
  37. package/docs/VinGrade-MVP-02-Validation.md +480 -0
  38. package/docs/VinGrade-RC02-Live-Validation.md +273 -0
  39. package/docs/ai-capability-discovery.md +573 -0
  40. package/docs/ai-engineering-production-readiness.md +390 -0
  41. package/docs/ai-engineering-review.md +251 -0
  42. package/docs/ai-runtime-governance-discovery.md +754 -0
  43. package/docs/architecture-overview.md +78 -0
  44. package/docs/cli-progress.md +111 -0
  45. package/docs/decisions/diff-01-brand-distribution-identity.md +450 -0
  46. package/docs/decisions/diff-02-identity-compatibility-contract.md +274 -0
  47. package/docs/decisions/diff-03-repository-docs-community-migration.md +165 -0
  48. package/docs/decisions/diff-04-release-evidence-gap-acceptance.md +110 -0
  49. package/docs/discovery/mvp-05-ai-migration-planning-discovery.md +350 -0
  50. package/docs/gateway-runtime-discovery.md +614 -0
  51. package/docs/live-ai-validation.md +299 -0
  52. package/docs/migration-planning-qualification-resolution.md +99 -0
  53. package/docs/migrations/upgradelens-to-depverdict.md +97 -0
  54. package/docs/mp-r03-deterministic-upgrade-decision-architecture.md +135 -0
  55. package/docs/mp-r04-evidence-bounded-migration-handoff-architecture.md +116 -0
  56. package/docs/mp-r05-product-completion-and-decision-first-cli-architecture.md +162 -0
  57. package/docs/mvp-05-deterministic-context-runtime.md +127 -0
  58. package/docs/mvp-05-migration-checklist-contract.md +166 -0
  59. package/docs/mvp-05-migration-checklist-orchestration.md +105 -0
  60. package/docs/mvp-05-migration-evaluation-and-qualification.md +135 -0
  61. package/docs/mvp-05-provider-neutral-generator.md +79 -0
  62. package/docs/ollama-local-smoke-validation.md +156 -0
  63. package/docs/openai-compatible-runtime-discovery.md +789 -0
  64. package/docs/openrouter-one-dependency-validation.md +205 -0
  65. package/docs/oss-02-package-guard-hardening-architecture.md +131 -0
  66. package/docs/oss-04-public-ci-package-metadata-architecture.md +142 -0
  67. package/docs/package-content-policy.md +98 -0
  68. package/docs/releases/v0.5.0-technical-preview.md +114 -0
  69. package/docs/releases/v0.6.0-alpha.1-depverdict-preview.md +101 -0
  70. package/docs/reviews/diff-02-identity-contract-compatibility.md +413 -0
  71. package/docs/reviews/diff-03-repository-docs-community-migration.md +364 -0
  72. package/docs/reviews/diff-04-depverdict-distribution-identity-readiness-rereview.md +453 -0
  73. package/docs/reviews/diff-04-fix-post-rename-identity-release-remediation.md +310 -0
  74. package/docs/reviews/diff-05-final-preview-distribution-qualification.md +561 -0
  75. package/docs/reviews/mvp-05-final-product-value-workflow-rereview.md +471 -0
  76. package/docs/reviews/mvp-05-product-workflow-review.md +605 -0
  77. package/docs/reviews/oss-01-duplicate-artifact-investigation-cleanup.md +411 -0
  78. package/docs/reviews/oss-02-package-guard-hardening.md +303 -0
  79. package/docs/reviews/oss-03-community-scaffolding.md +378 -0
  80. package/docs/reviews/oss-04-public-ci-package-metadata.md +439 -0
  81. package/docs/reviews/oss-05-technical-preview-qualification.md +482 -0
  82. package/docs/reviews/upgradelens-vs-upgradedepdetective-source-comparison.md +355 -0
  83. package/docs/reviews/v0.5.0-pre-release-smoke.md +263 -0
  84. package/docs/reviews/v0.5.0-version-bump-release-verification.md +306 -0
  85. package/docs/runtime-contract-discovery.md +521 -0
  86. package/docs/structured-output-compatibility-report.md +100 -0
  87. package/docs/ts-fix-01-exact-duplicate-occurrence-target-selection-architecture.md +111 -0
  88. package/docs/version-analysis-architecture.md +827 -0
  89. package/eval/README.md +86 -0
  90. package/eval/datasets/generic/declared-constraint.json +59 -0
  91. package/eval/datasets/generic/evidence-conflict.json +73 -0
  92. package/eval/datasets/generic/major-breaking-release.json +66 -0
  93. package/eval/datasets/generic/missing-evidence.json +46 -0
  94. package/eval/datasets/generic/patch-release-low.json +59 -0
  95. package/eval/datasets/node/axios-patch-low.json +59 -0
  96. package/eval/datasets/node/react-major-breaking.json +66 -0
  97. package/eval/datasets/node/react-minor-compatibility.json +66 -0
  98. package/eval/datasets/python/fastapi-deprecation.json +66 -0
  99. package/eval/datasets/python/pydantic-major-breaking.json +66 -0
  100. package/eval/migration-planning/golden-dataset-v2.json +306 -0
  101. package/eval/migration-planning/golden-dataset.json +214 -0
  102. package/eval/schemas/expected-result.schema.json +88 -0
  103. package/eval/schemas/golden-case.schema.json +181 -0
  104. package/package.json +57 -0
  105. package/schemas/ai-scorecard.schema.json +132 -0
  106. package/schemas/benchmark-report.schema.json +189 -0
  107. package/schemas/benchmark.schema.json +55 -0
  108. package/schemas/capability-profile.schema.json +44 -0
  109. package/schemas/conformance-report.schema.json +150 -0
  110. package/schemas/deployment-profile.schema.json +64 -0
  111. package/schemas/evaluation-report.schema.json +158 -0
  112. package/schemas/knowledge-evidence-bundle.schema.json +150 -0
  113. package/schemas/knowledge-manifest.schema.json +548 -0
  114. package/schemas/metrics.schema.json +178 -0
  115. package/schemas/migration-checklist-extractive-candidate.schema.json +42 -0
  116. package/schemas/migration-checklist.schema.json +706 -0
  117. package/schemas/migration-evaluation-dataset-v2.schema.json +208 -0
  118. package/schemas/migration-evaluation-dataset.schema.json +204 -0
  119. package/schemas/migration-planning-qualification-record.schema.json +234 -0
  120. package/schemas/project-manifest.schema.json +308 -0
  121. package/schemas/qualification-record.schema.json +56 -0
  122. package/schemas/repository-impact-evidence.schema.json +232 -0
  123. package/schemas/repository-impact.schema.json +202 -0
  124. package/schemas/upgrade-decision.schema.json +273 -0
  125. package/schemas/usage-index.schema.json +179 -0
  126. package/schemas/version-analysis.schema.json +449 -0
  127. package/src/ai-runtime-debug.js +325 -0
  128. package/src/ai-runtime-error.js +42 -0
  129. package/src/ai-runtime.js +174 -0
  130. package/src/ai-scorecard.js +204 -0
  131. package/src/ai-version-analysis.js +484 -0
  132. package/src/artifact-root-compatibility.js +91 -0
  133. package/src/benchmark-report.js +111 -0
  134. package/src/benchmark-runner.js +191 -0
  135. package/src/canonical-json.js +69 -0
  136. package/src/cli.js +1299 -0
  137. package/src/conformance-report.js +158 -0
  138. package/src/conformance-runner.js +253 -0
  139. package/src/constants.js +73 -0
  140. package/src/cooperative-scheduler.js +79 -0
  141. package/src/dependencies.js +44 -0
  142. package/src/dependency-ai-context.js +625 -0
  143. package/src/detectors.js +253 -0
  144. package/src/discovery.js +234 -0
  145. package/src/ecosystem-version-adapter.js +294 -0
  146. package/src/environment-compatibility.js +77 -0
  147. package/src/evaluation-comparator.js +158 -0
  148. package/src/evaluation-report.js +76 -0
  149. package/src/evaluation-runner.js +248 -0
  150. package/src/evidence-source-adapter.js +472 -0
  151. package/src/files.js +89 -0
  152. package/src/governance-diagnostics.js +64 -0
  153. package/src/governance-loader.js +63 -0
  154. package/src/governance-metadata.js +346 -0
  155. package/src/governance-validator.js +360 -0
  156. package/src/http/bounded-fetch.js +278 -0
  157. package/src/http/cli-http-runtime.js +44 -0
  158. package/src/impact/input-loader.js +157 -0
  159. package/src/impact/matcher.js +40 -0
  160. package/src/impact/repository-impact.js +199 -0
  161. package/src/impact/runtime.js +24 -0
  162. package/src/impact/status.js +62 -0
  163. package/src/impact/writer.js +30 -0
  164. package/src/impact-evidence/input-loader.js +202 -0
  165. package/src/impact-evidence/repository-impact-evidence.js +234 -0
  166. package/src/impact-evidence/runtime.js +16 -0
  167. package/src/impact-evidence/writer.js +30 -0
  168. package/src/index.js +563 -0
  169. package/src/installed-version-baseline.js +196 -0
  170. package/src/knowledge-cache.js +324 -0
  171. package/src/knowledge-evidence-bundle.js +101 -0
  172. package/src/knowledge-evidence-producer.js +233 -0
  173. package/src/knowledge-manifest-builder.js +188 -0
  174. package/src/knowledge-manifest-writer.js +32 -0
  175. package/src/knowledge-manifest.js +255 -0
  176. package/src/knowledge-research.js +615 -0
  177. package/src/metrics-engine.js +205 -0
  178. package/src/migration-checklist/ai-candidate.js +320 -0
  179. package/src/migration-checklist/assembler.js +37 -0
  180. package/src/migration-checklist/context-runtime.js +828 -0
  181. package/src/migration-checklist/evaluation/action-criteria.js +244 -0
  182. package/src/migration-checklist/evaluation/comparator-v2.js +332 -0
  183. package/src/migration-checklist/evaluation/comparator.js +279 -0
  184. package/src/migration-checklist/evaluation/dataset-v2.js +227 -0
  185. package/src/migration-checklist/evaluation/dataset.js +336 -0
  186. package/src/migration-checklist/evaluation/extractive-fixtures-v2.js +148 -0
  187. package/src/migration-checklist/evaluation/metrics-v2.js +226 -0
  188. package/src/migration-checklist/evaluation/metrics.js +158 -0
  189. package/src/migration-checklist/evaluation/qualification-v2.js +321 -0
  190. package/src/migration-checklist/evaluation/qualification.js +239 -0
  191. package/src/migration-checklist/evaluation/runner-v2.js +294 -0
  192. package/src/migration-checklist/evaluation/runner.js +194 -0
  193. package/src/migration-checklist/evaluation/scorecard-v2.js +106 -0
  194. package/src/migration-checklist/evaluation/scorecard.js +86 -0
  195. package/src/migration-checklist/extractive-candidate.js +166 -0
  196. package/src/migration-checklist/extractive-prompt.js +62 -0
  197. package/src/migration-checklist/generator.js +702 -0
  198. package/src/migration-checklist/grounding-policy.js +117 -0
  199. package/src/migration-checklist/input-loader.js +613 -0
  200. package/src/migration-checklist/migration-checklist.js +635 -0
  201. package/src/migration-checklist/presentation.js +292 -0
  202. package/src/migration-checklist/progress.js +96 -0
  203. package/src/migration-checklist/prompt.js +83 -0
  204. package/src/migration-checklist/qualification-guard.js +462 -0
  205. package/src/migration-checklist/qualification-resolution.js +122 -0
  206. package/src/migration-checklist/qualification-store.js +225 -0
  207. package/src/migration-checklist/runtime.js +205 -0
  208. package/src/migration-checklist/verification.js +134 -0
  209. package/src/migration-checklist/writer.js +35 -0
  210. package/src/openai-compatible-provider.js +451 -0
  211. package/src/orchestration/failure-log.js +32 -0
  212. package/src/orchestration/pipeline.js +200 -0
  213. package/src/orchestration/progress-events.js +337 -0
  214. package/src/orchestration/progress-reporter.js +131 -0
  215. package/src/orchestration/text-writer.js +22 -0
  216. package/src/portable.js +13 -0
  217. package/src/product-completion.js +249 -0
  218. package/src/project-manifest-input.js +90 -0
  219. package/src/project-manifest.js +141 -0
  220. package/src/python-requirements.js +137 -0
  221. package/src/registry/npm-packument.js +256 -0
  222. package/src/registry/npm-registry-adapter.js +262 -0
  223. package/src/registry/pypi-project.js +300 -0
  224. package/src/registry/pypi-registry-adapter.js +235 -0
  225. package/src/registry/sanitize-registry-body.js +51 -0
  226. package/src/renderers/console.js +160 -0
  227. package/src/renderers/impact-presentation.js +278 -0
  228. package/src/renderers/markdown.js +172 -0
  229. package/src/research-plan.js +455 -0
  230. package/src/runtime-conformance.js +275 -0
  231. package/src/source-provenance.js +393 -0
  232. package/src/source-url.js +62 -0
  233. package/src/structured-output-schema.js +66 -0
  234. package/src/target-selector.js +306 -0
  235. package/src/upgrade-decision/input-loader.js +107 -0
  236. package/src/upgrade-decision/presentation.js +43 -0
  237. package/src/upgrade-decision/runtime.js +21 -0
  238. package/src/upgrade-decision/upgrade-decision.js +626 -0
  239. package/src/upgrade-decision/writer.js +30 -0
  240. package/src/usage/analyzer-registry.js +63 -0
  241. package/src/usage/coverage.js +116 -0
  242. package/src/usage/input-loader.js +139 -0
  243. package/src/usage/js/analyzer.js +240 -0
  244. package/src/usage/js/parser.js +21 -0
  245. package/src/usage/runtime.js +187 -0
  246. package/src/usage/scope.js +44 -0
  247. package/src/usage/source-files.js +50 -0
  248. package/src/usage/usage-index.js +217 -0
  249. package/src/usage/writer.js +31 -0
  250. package/src/version-analysis-loader.js +203 -0
  251. package/src/version-analysis-manifest.js +314 -0
  252. package/src/version-analysis-writer.js +30 -0
@@ -0,0 +1,613 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+
5
+ import {
6
+ DEFAULT_KNOWLEDGE_MANIFEST_PATH,
7
+ DEFAULT_MANIFEST_PATH,
8
+ DEFAULT_REPOSITORY_IMPACT_EVIDENCE_PATH,
9
+ DEFAULT_REPOSITORY_IMPACT_PATH,
10
+ DEFAULT_USAGE_INDEX_PATH,
11
+ DEFAULT_VERSION_ANALYSIS_PATH
12
+ } from '../constants.js';
13
+ import { resolveArtifactRootChain } from '../artifact-root-compatibility.js';
14
+ import { validateRepositoryImpactEvidence } from '../impact-evidence/repository-impact-evidence.js';
15
+ import {
16
+ EXACT_SYMBOL_MATCHER_ID,
17
+ EXACT_SYMBOL_MATCHER_VERSION,
18
+ isMatchableUsageSymbol,
19
+ matchFindingToUsage
20
+ } from '../impact/matcher.js';
21
+ import {
22
+ classifyDependencyImpact,
23
+ classifyFindingImpact
24
+ } from '../impact/status.js';
25
+ import { validateRepositoryImpact } from '../impact/repository-impact.js';
26
+ import { isPortableRelativePath } from '../portable.js';
27
+ import { loadProjectManifestInput } from '../project-manifest-input.js';
28
+ import { validateUsageIndex } from '../usage/usage-index.js';
29
+ import { coverageForProject } from '../usage/coverage.js';
30
+ import {
31
+ DEFAULT_KNOWLEDGE_EVIDENCE_BUNDLE_PATH,
32
+ loadKnowledgeEvidenceBundleInput,
33
+ loadKnowledgeManifestInput
34
+ } from '../version-analysis-loader.js';
35
+ import { validateVersionAnalysisManifest } from '../version-analysis-manifest.js';
36
+
37
+ const ARTIFACTS = Object.freeze([
38
+ Object.freeze({ key: 'projectManifest', label: 'Project Manifest', artifact: DEFAULT_MANIFEST_PATH }),
39
+ Object.freeze({ key: 'knowledgeManifest', label: 'Knowledge Manifest', artifact: DEFAULT_KNOWLEDGE_MANIFEST_PATH }),
40
+ Object.freeze({
41
+ key: 'knowledgeEvidenceBundle',
42
+ label: 'Knowledge Evidence Bundle',
43
+ artifact: DEFAULT_KNOWLEDGE_EVIDENCE_BUNDLE_PATH
44
+ }),
45
+ Object.freeze({ key: 'versionAnalysis', label: 'Version Analysis', artifact: DEFAULT_VERSION_ANALYSIS_PATH }),
46
+ Object.freeze({ key: 'usageIndex', label: 'Usage Index', artifact: DEFAULT_USAGE_INDEX_PATH }),
47
+ Object.freeze({ key: 'repositoryImpact', label: 'Repository Impact', artifact: DEFAULT_REPOSITORY_IMPACT_PATH }),
48
+ Object.freeze({
49
+ key: 'repositoryImpactEvidence',
50
+ label: 'Repository Impact Evidence',
51
+ artifact: DEFAULT_REPOSITORY_IMPACT_EVIDENCE_PATH
52
+ })
53
+ ]);
54
+
55
+ const LINEAGE_FIELDS = Object.freeze({
56
+ projectManifest: Object.freeze([
57
+ 'schemaVersion',
58
+ 'artifact',
59
+ 'artifactDigest',
60
+ 'repository.name',
61
+ 'repository.root'
62
+ ]),
63
+ knowledgeManifest: Object.freeze(['schemaVersion', 'artifact', 'artifactDigest', 'researchId']),
64
+ artifact: Object.freeze(['schemaVersion', 'artifact', 'artifactDigest'])
65
+ });
66
+
67
+ export class MigrationChecklistInputError extends Error {
68
+ constructor(message, code = 'MIGRATION_CHECKLIST_INPUT_INVALID', details = {}) {
69
+ super(`Migration Checklist input error: ${message}`);
70
+ this.name = 'MigrationChecklistInputError';
71
+ this.code = code;
72
+ Object.assign(this, details);
73
+ }
74
+ }
75
+
76
+ function fail(message, code, details) {
77
+ throw new MigrationChecklistInputError(message, code, details);
78
+ }
79
+
80
+ function digest(bytes) {
81
+ return `sha256:${createHash('sha256').update(bytes).digest('hex')}`;
82
+ }
83
+
84
+ function readField(value, field) {
85
+ return field.split('.').reduce((current, segment) => current?.[segment], value);
86
+ }
87
+
88
+ function sameJson(left, right) {
89
+ return JSON.stringify(left) === JSON.stringify(right);
90
+ }
91
+
92
+ function deepFreeze(value) {
93
+ if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value;
94
+ for (const child of Object.values(value)) deepFreeze(child);
95
+ return Object.freeze(value);
96
+ }
97
+
98
+ function invocationSources(input, options) {
99
+ if (typeof input === 'string') {
100
+ return { repositoryRoot: input, sources: null, artifacts: options.artifacts ?? {} };
101
+ }
102
+ if (input instanceof URL) {
103
+ const rootUrl = new URL(input);
104
+ if (!rootUrl.pathname.endsWith('/')) rootUrl.pathname = `${rootUrl.pathname}/`;
105
+ return { repositoryRoot: null, sources: null, rootUrl, artifacts: options.artifacts ?? {} };
106
+ }
107
+ if (input === undefined || input === null) {
108
+ return { repositoryRoot: '.', sources: null, artifacts: options.artifacts ?? {} };
109
+ }
110
+ if (typeof input !== 'object') {
111
+ fail('input must be a repository root, URL, or an object containing repositoryRoot or sources.');
112
+ }
113
+ const directSources = ARTIFACTS.some(({ key }) => input[key] !== undefined)
114
+ ? input
115
+ : null;
116
+ const rootUrl = input.repositoryRoot instanceof URL ? new URL(input.repositoryRoot) : null;
117
+ if (rootUrl && !rootUrl.pathname.endsWith('/')) rootUrl.pathname = `${rootUrl.pathname}/`;
118
+ return {
119
+ repositoryRoot: input.repositoryRoot ?? (directSources ? null : '.'),
120
+ rootUrl,
121
+ sources: input.sources ?? directSources,
122
+ artifacts: { ...(options.artifacts ?? {}), ...(input.artifacts ?? {}) }
123
+ };
124
+ }
125
+
126
+ function sourceFor(descriptor, invocation) {
127
+ const alias = descriptor.key === 'knowledgeEvidenceBundle'
128
+ ? invocation.sources?.evidenceBundle
129
+ : undefined;
130
+ const explicit = invocation.sources?.[descriptor.key] ?? alias;
131
+ if (explicit !== undefined) return explicit;
132
+ if (invocation.sources) return undefined;
133
+ if (invocation.rootUrl) return new URL(descriptor.artifact, invocation.rootUrl);
134
+ return path.resolve(
135
+ invocation.repositoryRoot,
136
+ invocation.selectedArtifacts?.[descriptor.key] ?? descriptor.artifact
137
+ );
138
+ }
139
+
140
+ function artifactFor(descriptor, source, invocation) {
141
+ const explicitArtifact = source && typeof source === 'object' && !(source instanceof URL)
142
+ ? source.artifact
143
+ : undefined;
144
+ const artifact = explicitArtifact
145
+ ?? invocation.artifacts[descriptor.key]
146
+ ?? invocation.selectedArtifacts?.[descriptor.key]
147
+ ?? descriptor.artifact;
148
+ if (!isPortableRelativePath(artifact)) {
149
+ fail(`${descriptor.label} artifact must be a portable repository-relative path.`, 'INVALID_ARTIFACT_PATH');
150
+ }
151
+ return artifact;
152
+ }
153
+
154
+ async function readArtifact(descriptor, source, artifact) {
155
+ if (source === undefined) {
156
+ fail(`${descriptor.label} artifact is required.`, 'MISSING_ARTIFACT', { artifact: descriptor.key });
157
+ }
158
+ try {
159
+ let bytes;
160
+ if (typeof source === 'string' || source instanceof URL) {
161
+ bytes = await readFile(source);
162
+ } else if (source && typeof source === 'object' && source.bytes instanceof Uint8Array) {
163
+ bytes = Buffer.from(source.bytes);
164
+ } else if (source && typeof source === 'object' && (typeof source.path === 'string' || source.path instanceof URL)) {
165
+ bytes = await readFile(source.path);
166
+ } else {
167
+ fail(
168
+ `${descriptor.label} source must be a file path, URL, or { bytes, artifact }.`,
169
+ 'INVALID_ARTIFACT_SOURCE',
170
+ { artifact: descriptor.key }
171
+ );
172
+ }
173
+ let value;
174
+ try {
175
+ value = JSON.parse(bytes.toString('utf8'));
176
+ } catch {
177
+ fail(`${descriptor.label} bytes are not valid JSON.`, 'INVALID_JSON', { artifact: descriptor.key });
178
+ }
179
+ return { bytes, artifact, value };
180
+ } catch (error) {
181
+ if (error instanceof MigrationChecklistInputError) throw error;
182
+ if (error?.code === 'ENOENT') {
183
+ fail(`${descriptor.label} artifact is missing.`, 'MISSING_ARTIFACT', { artifact: descriptor.key });
184
+ }
185
+ fail(`${descriptor.label} could not be read.`, 'ARTIFACT_READ_FAILED', { artifact: descriptor.key });
186
+ }
187
+ }
188
+
189
+ function lineageMetadata(records, projectInput, knowledgeInput, bundleInput) {
190
+ const metadata = {
191
+ projectManifest: projectInput.input.projectManifest,
192
+ knowledgeManifest: knowledgeInput.input.knowledgeManifest,
193
+ knowledgeEvidenceBundle: bundleInput.input.evidenceArtifact
194
+ };
195
+ for (const key of ['versionAnalysis', 'usageIndex', 'repositoryImpact', 'repositoryImpactEvidence']) {
196
+ metadata[key] = {
197
+ schemaVersion: records[key].value.schemaVersion,
198
+ artifact: records[key].artifact,
199
+ artifactDigest: digest(records[key].bytes)
200
+ };
201
+ }
202
+ return metadata;
203
+ }
204
+
205
+ function assertLineage(consumer, upstream, expected, actual, fields) {
206
+ for (const field of fields) {
207
+ const expectedValue = readField(expected, field);
208
+ const actualValue = readField(actual, field);
209
+ if (expectedValue !== actualValue) {
210
+ fail(
211
+ `lineage mismatch: ${consumer} declares ${upstream} ${field} as ${String(expectedValue)}; actual value is ${String(actualValue)}.`,
212
+ 'LINEAGE_MISMATCH',
213
+ { consumer, upstream, field, expected: expectedValue, actual: actualValue }
214
+ );
215
+ }
216
+ }
217
+ }
218
+
219
+ export function validateMigrationChecklistInputLineage(artifacts) {
220
+ const { input } = artifacts;
221
+ const checks = [
222
+ ['Knowledge Manifest', 'Project Manifest', artifacts.knowledgeManifest.input.projectManifest,
223
+ input.projectManifest, LINEAGE_FIELDS.projectManifest],
224
+ ['Knowledge Evidence Bundle', 'Knowledge Manifest', artifacts.knowledgeEvidenceBundle.input.knowledgeManifest,
225
+ input.knowledgeManifest, LINEAGE_FIELDS.knowledgeManifest],
226
+ ['Version Analysis', 'Project Manifest', artifacts.versionAnalysis.input.projectManifest,
227
+ input.projectManifest, LINEAGE_FIELDS.projectManifest],
228
+ ['Version Analysis', 'Knowledge Manifest', artifacts.versionAnalysis.input.knowledgeManifest,
229
+ input.knowledgeManifest, LINEAGE_FIELDS.knowledgeManifest],
230
+ ['Version Analysis', 'Knowledge Evidence Bundle', artifacts.versionAnalysis.input.evidenceArtifact,
231
+ input.knowledgeEvidenceBundle, LINEAGE_FIELDS.artifact],
232
+ ['Usage Index', 'Project Manifest', artifacts.usageIndex.input.projectManifest,
233
+ input.projectManifest, LINEAGE_FIELDS.projectManifest],
234
+ ['Usage Index', 'Version Analysis', artifacts.usageIndex.input.versionAnalysis,
235
+ input.versionAnalysis, LINEAGE_FIELDS.artifact],
236
+ ['Repository Impact', 'Project Manifest', artifacts.repositoryImpact.input.projectManifest,
237
+ input.projectManifest, LINEAGE_FIELDS.projectManifest],
238
+ ['Repository Impact', 'Version Analysis', artifacts.repositoryImpact.input.versionAnalysis,
239
+ input.versionAnalysis, LINEAGE_FIELDS.artifact],
240
+ ['Repository Impact', 'Usage Index', artifacts.repositoryImpact.input.usageIndex,
241
+ input.usageIndex, LINEAGE_FIELDS.artifact],
242
+ ['Repository Impact Evidence', 'Project Manifest', artifacts.repositoryImpactEvidence.input.projectManifest,
243
+ input.projectManifest, LINEAGE_FIELDS.projectManifest],
244
+ ['Repository Impact Evidence', 'Version Analysis', artifacts.repositoryImpactEvidence.input.versionAnalysis,
245
+ input.versionAnalysis, LINEAGE_FIELDS.artifact],
246
+ ['Repository Impact Evidence', 'Usage Index', artifacts.repositoryImpactEvidence.input.usageIndex,
247
+ input.usageIndex, LINEAGE_FIELDS.artifact],
248
+ ['Repository Impact Evidence', 'Repository Impact', artifacts.repositoryImpactEvidence.input.repositoryImpact,
249
+ input.repositoryImpact, LINEAGE_FIELDS.artifact]
250
+ ];
251
+ for (const check of checks) assertLineage(...check);
252
+ return artifacts;
253
+ }
254
+
255
+ function occurrenceKey(dependency) {
256
+ return [
257
+ dependency.projectId,
258
+ dependency.packageId,
259
+ dependency.manifest,
260
+ dependency.dependencyType,
261
+ dependency.declaredName,
262
+ dependency.versions?.declaredVersion ?? dependency.declaredVersion ?? ''
263
+ ].join('\0');
264
+ }
265
+
266
+ function usageIdentityKey(value) {
267
+ return `${value.projectId}\0${value.packageId}`;
268
+ }
269
+
270
+ function versionResultsByUsageIdentity(results) {
271
+ const indexed = new Map();
272
+ for (const result of results) {
273
+ const key = usageIdentityKey(result.dependency);
274
+ if (!indexed.has(key)) indexed.set(key, []);
275
+ indexed.get(key).push(result);
276
+ }
277
+ return indexed;
278
+ }
279
+
280
+ function validateDependencyIdentities(artifacts) {
281
+ const projects = new Map(artifacts.projectManifest.projects.map((project) => [project.id, project]));
282
+ const packages = new Map(artifacts.knowledgeManifest.packages.map((item) => [item.id, item]));
283
+ const occurrenceOwners = new Set();
284
+ const resultIds = new Set();
285
+
286
+ for (const result of artifacts.versionAnalysis.results) {
287
+ const dependency = result.dependency;
288
+ if (resultIds.has(result.id)) {
289
+ fail(`Version Analysis result ${result.id} is duplicated.`, 'AMBIGUOUS_REFERENCE');
290
+ }
291
+ resultIds.add(result.id);
292
+ const project = projects.get(dependency.projectId);
293
+ if (!project) fail(`Version Analysis result ${result.id} references unknown project ${dependency.projectId}.`, 'REFERENCE_MISMATCH');
294
+ const declared = project.dependencies.filter((item) => (
295
+ item.name === dependency.declaredName
296
+ && item.normalizedName === dependency.normalizedName
297
+ && item.declaredVersion === result.versions.declaredVersion
298
+ && item.type === dependency.dependencyType
299
+ && item.manifest === dependency.manifest
300
+ ));
301
+ if (declared.length !== 1 || project.ecosystem !== dependency.ecosystem) {
302
+ fail(`Version Analysis result ${result.id} does not match one exact Project Manifest dependency occurrence.`, 'REFERENCE_MISMATCH');
303
+ }
304
+ const packageRecord = packages.get(dependency.packageId);
305
+ if (!packageRecord
306
+ || packageRecord.ecosystem !== dependency.ecosystem
307
+ || packageRecord.identity.normalizedName !== dependency.normalizedName
308
+ || packageRecord.identity.registry !== dependency.registry) {
309
+ fail(`Version Analysis result ${result.id} does not match Knowledge package ${dependency.packageId}.`, 'REFERENCE_MISMATCH');
310
+ }
311
+ const occurrences = packageRecord.occurrences.filter((item) => (
312
+ item.projectId === dependency.projectId
313
+ && item.projectPath === project.path
314
+ && item.manifest === dependency.manifest
315
+ && item.dependencyType === dependency.dependencyType
316
+ && item.declaredName === dependency.declaredName
317
+ && item.declaredVersion === result.versions.declaredVersion
318
+ ));
319
+ if (occurrences.length !== 1) {
320
+ fail(`Version Analysis result ${result.id} does not match one exact Knowledge occurrence.`, 'REFERENCE_MISMATCH');
321
+ }
322
+ const key = occurrenceKey({ ...dependency, versions: result.versions });
323
+ if (occurrenceOwners.has(key)) {
324
+ fail(`Version Analysis occurrence for ${dependency.projectId}/${dependency.packageId} is ambiguous.`, 'AMBIGUOUS_REFERENCE');
325
+ }
326
+ occurrenceOwners.add(key);
327
+ }
328
+ }
329
+
330
+ function validateEvidenceReferences(artifacts) {
331
+ const bundleEvidence = new Map(artifacts.knowledgeEvidenceBundle.evidence.map((item) => [item.id, item]));
332
+ const sources = new Map(artifacts.knowledgeManifest.sources.map((item) => [item.id, item]));
333
+ const packages = new Map(artifacts.knowledgeManifest.packages.map((item) => [item.id, item]));
334
+
335
+ for (const item of artifacts.knowledgeEvidenceBundle.evidence) {
336
+ const packageRecord = packages.get(item.packageId);
337
+ const source = sources.get(item.sourceId);
338
+ if (!packageRecord || !source || !packageRecord.sourceIds.includes(item.sourceId)) {
339
+ fail(`Knowledge evidence ${item.id} has cross-package or unknown provenance.`, 'REFERENCE_MISMATCH');
340
+ }
341
+ }
342
+
343
+ for (const result of artifacts.versionAnalysis.results) {
344
+ const resultEvidence = new Map(result.evidence.map((item) => [item.id, item]));
345
+ const findingIds = new Set();
346
+ for (const finding of result.findings) {
347
+ if (findingIds.has(finding.id)) {
348
+ fail(`Version Analysis result ${result.id} has duplicate finding ${finding.id}.`, 'AMBIGUOUS_REFERENCE');
349
+ }
350
+ findingIds.add(finding.id);
351
+ for (const ref of finding.evidenceRefs) {
352
+ if (!resultEvidence.has(ref)) {
353
+ fail(`Finding ${result.id}/${finding.id} references unknown Version Analysis evidence ${ref}.`, 'REFERENCE_MISMATCH');
354
+ }
355
+ }
356
+ }
357
+ for (const evidence of result.evidence) {
358
+ const bundled = bundleEvidence.get(evidence.id);
359
+ const source = sources.get(evidence.sourceId);
360
+ if (!bundled || bundled.packageId !== result.dependency.packageId || !source) {
361
+ fail(`Version Analysis evidence ${evidence.id} has cross-package or unknown provenance.`, 'REFERENCE_MISMATCH');
362
+ }
363
+ const expected = {
364
+ id: bundled.id,
365
+ kind: bundled.kind,
366
+ sourceId: bundled.sourceId,
367
+ sourceUrl: source.url,
368
+ authority: source.authority,
369
+ trust: source.trust,
370
+ retrievedAt: bundled.retrievedAt,
371
+ contentDigest: bundled.contentDigest,
372
+ locator: bundled.locator,
373
+ releaseVersions: bundled.releaseVersions
374
+ };
375
+ if (!sameJson(evidence, expected)) {
376
+ fail(`Version Analysis evidence ${evidence.id} does not exactly match Knowledge evidence metadata.`, 'REFERENCE_MISMATCH');
377
+ }
378
+ }
379
+ }
380
+ }
381
+
382
+ function validateUsageReferences(artifacts) {
383
+ const results = versionResultsByUsageIdentity(artifacts.versionAnalysis.results);
384
+ for (const usage of artifacts.usageIndex.dependencies) {
385
+ const matching = results.get(usageIdentityKey(usage)) ?? [];
386
+ if (matching.length === 0
387
+ || matching.some((result) => result.dependency.declaredName !== usage.name)) {
388
+ fail(`Usage Index dependency ${usage.projectId}/${usage.packageId} has no exact Version Analysis identity.`, 'REFERENCE_MISMATCH');
389
+ }
390
+ }
391
+ const projects = new Map(artifacts.projectManifest.projects.map((project) => [project.id, project]));
392
+ for (const coverage of artifacts.usageIndex.analysis.coverage ?? []) {
393
+ const project = projects.get(coverage.projectId);
394
+ if (!project || project.path !== coverage.projectPath || project.ecosystem !== coverage.ecosystem) {
395
+ fail(`Usage Index coverage ${coverage.projectId} has no exact Project Manifest identity.`, 'REFERENCE_MISMATCH');
396
+ }
397
+ }
398
+ }
399
+
400
+ function validateImpactReferences(artifacts) {
401
+ if (artifacts.repositoryImpact.analysis.matcher.id !== EXACT_SYMBOL_MATCHER_ID
402
+ || artifacts.repositoryImpact.analysis.matcher.version !== EXACT_SYMBOL_MATCHER_VERSION) {
403
+ fail(
404
+ `Repository Impact matcher must be ${EXACT_SYMBOL_MATCHER_ID}@${EXACT_SYMBOL_MATCHER_VERSION}.`,
405
+ 'MATCHER_UNSUPPORTED'
406
+ );
407
+ }
408
+ const results = new Map(artifacts.versionAnalysis.results.map((result) => [result.id, result]));
409
+ const usages = new Map(artifacts.usageIndex.dependencies.map((usage) => (
410
+ [`${usage.projectId}\0${usage.packageId}`, usage]
411
+ )));
412
+ const seen = new Set();
413
+ for (const dependency of artifacts.repositoryImpact.dependencies) {
414
+ const result = results.get(dependency.analysisResultId);
415
+ if (!result || seen.has(dependency.analysisResultId)) {
416
+ fail(`Repository Impact has unknown or duplicate result ${dependency.analysisResultId}.`, 'REFERENCE_MISMATCH');
417
+ }
418
+ seen.add(dependency.analysisResultId);
419
+ if (dependency.projectId !== result.dependency.projectId
420
+ || dependency.packageId !== result.dependency.packageId
421
+ || dependency.name !== result.dependency.declaredName) {
422
+ fail(`Repository Impact identity differs for result ${dependency.analysisResultId}.`, 'REFERENCE_MISMATCH');
423
+ }
424
+ const expectedFindings = result.findings.filter((finding) => finding.kind === 'breakingChange');
425
+ if (dependency.findings.length !== expectedFindings.length) {
426
+ fail(`Repository Impact finding count differs for result ${dependency.analysisResultId}.`, 'REFERENCE_MISMATCH');
427
+ }
428
+ const usage = usages.get(`${dependency.projectId}\0${dependency.packageId}`) ?? null;
429
+ const coverage = dependency.coverage ?? coverageForProject(
430
+ artifacts.usageIndex,
431
+ dependency.projectId,
432
+ result.dependency.ecosystem
433
+ );
434
+ const impacts = new Map(dependency.findings.map((finding) => [finding.id, finding]));
435
+ for (const finding of expectedFindings) {
436
+ const impact = impacts.get(finding.id);
437
+ const expectedMatches = matchFindingToUsage(finding, usage);
438
+ const expectedState = classifyFindingImpact({
439
+ versionStatus: result.status,
440
+ coverage,
441
+ usage,
442
+ matches: expectedMatches
443
+ });
444
+ if (!impact
445
+ || impact.kind !== finding.kind
446
+ || impact.summary !== finding.summary
447
+ || impact.impacted !== (expectedMatches.length > 0)
448
+ || (impact.status !== undefined
449
+ && (impact.status !== expectedState.status || impact.reasonCode !== expectedState.reasonCode))
450
+ || !sameJson(impact.matches, expectedMatches)) {
451
+ fail(`Repository Impact finding ${dependency.analysisResultId}/${finding.id} is inconsistent with Version Analysis or Usage Index.`, 'REFERENCE_MISMATCH');
452
+ }
453
+ }
454
+ if (dependency.status !== undefined) {
455
+ const expectedState = classifyDependencyImpact({
456
+ versionStatus: result.status,
457
+ coverage,
458
+ usage,
459
+ findings: dependency.findings
460
+ });
461
+ if (dependency.status !== expectedState.status
462
+ || dependency.reasonCode !== expectedState.reasonCode
463
+ || !sameJson(dependency.coverage, coverage)) {
464
+ fail(`Repository Impact state ${dependency.analysisResultId} is inconsistent with usage coverage.`, 'REFERENCE_MISMATCH');
465
+ }
466
+ }
467
+ }
468
+ if (seen.size !== results.size) {
469
+ fail('Repository Impact does not contain every Version Analysis result exactly once.', 'REFERENCE_MISMATCH');
470
+ }
471
+ }
472
+
473
+ function expectedMatchedSymbols(impactFinding) {
474
+ return impactFinding.matches.map((match) => ({
475
+ symbol: match.symbol,
476
+ usages: match.files.map((file) => ({ file }))
477
+ }));
478
+ }
479
+
480
+ function expectedImpactEvidenceReason(impactFinding, usage) {
481
+ if (impactFinding.status === 'COVERAGE_UNAVAILABLE') return 'COVERAGE_UNAVAILABLE';
482
+ if (impactFinding.status === 'NOT_ANALYZED') return 'NOT_ANALYZED';
483
+ if (impactFinding.status === 'USAGE_NOT_FOUND') return 'USAGE_NOT_FOUND';
484
+ if (impactFinding.impacted) return 'EXACT_SYMBOL_USAGE_FOUND';
485
+ if (!usage) return 'DEPENDENCY_NOT_USED';
486
+ if (!usage.symbols.some((symbol) => isMatchableUsageSymbol(symbol.name))) {
487
+ return 'NO_MATCHABLE_SYMBOL_FOUND';
488
+ }
489
+ return 'NO_EXACT_SYMBOL_USAGE_FOUND';
490
+ }
491
+
492
+ function validateImpactEvidenceReferences(artifacts) {
493
+ const impacts = new Map(artifacts.repositoryImpact.dependencies.map((dependency) => [dependency.analysisResultId, dependency]));
494
+ const usages = new Map(artifacts.usageIndex.dependencies.map((usage) => (
495
+ [`${usage.projectId}\0${usage.packageId}`, usage]
496
+ )));
497
+ const seen = new Set();
498
+ for (const dependency of artifacts.repositoryImpactEvidence.dependencies) {
499
+ const impact = impacts.get(dependency.analysisResultId);
500
+ if (!impact || seen.has(dependency.analysisResultId)) {
501
+ fail(`Repository Impact Evidence has unknown or duplicate result ${dependency.analysisResultId}.`, 'REFERENCE_MISMATCH');
502
+ }
503
+ seen.add(dependency.analysisResultId);
504
+ if (dependency.projectId !== impact.projectId
505
+ || dependency.packageId !== impact.packageId
506
+ || dependency.name !== impact.name
507
+ || dependency.impacted !== impact.impacted
508
+ || dependency.findings.length !== impact.findings.length) {
509
+ fail(`Repository Impact Evidence identity differs for result ${dependency.analysisResultId}.`, 'REFERENCE_MISMATCH');
510
+ }
511
+ const evidenceFindings = new Map(dependency.findings.map((finding) => [finding.findingId, finding]));
512
+ const usage = usages.get(`${dependency.projectId}\0${dependency.packageId}`) ?? null;
513
+ for (const finding of impact.findings) {
514
+ const evidence = evidenceFindings.get(finding.id);
515
+ if (!evidence
516
+ || evidence.kind !== finding.kind
517
+ || evidence.summary !== finding.summary
518
+ || evidence.impacted !== finding.impacted
519
+ || (finding.status !== undefined && evidence.status !== finding.status)
520
+ || evidence.reasonCode !== expectedImpactEvidenceReason(finding, usage)
521
+ || !sameJson(evidence.matchedSymbols, expectedMatchedSymbols(finding))) {
522
+ fail(`Repository Impact Evidence location ${dependency.analysisResultId}/${finding.id} is inconsistent with Repository Impact.`, 'REFERENCE_MISMATCH');
523
+ }
524
+ }
525
+ if (impact.status !== undefined
526
+ && (dependency.status !== impact.status
527
+ || dependency.reasonCode !== impact.reasonCode
528
+ || !sameJson(dependency.coverage, impact.coverage))) {
529
+ fail(`Repository Impact Evidence state ${dependency.analysisResultId} is inconsistent with Repository Impact.`, 'REFERENCE_MISMATCH');
530
+ }
531
+ }
532
+ if (seen.size !== impacts.size) {
533
+ fail('Repository Impact Evidence does not contain every Repository Impact result exactly once.', 'REFERENCE_MISMATCH');
534
+ }
535
+ }
536
+
537
+ export function validateMigrationChecklistInputReferences(artifacts) {
538
+ validateDependencyIdentities(artifacts);
539
+ validateEvidenceReferences(artifacts);
540
+ validateUsageReferences(artifacts);
541
+ validateImpactReferences(artifacts);
542
+ validateImpactEvidenceReferences(artifacts);
543
+ return artifacts;
544
+ }
545
+
546
+ async function validateParsedArtifacts(records) {
547
+ let projectInput;
548
+ let knowledgeInput;
549
+ let bundleInput;
550
+ try {
551
+ [projectInput, knowledgeInput, bundleInput] = await Promise.all([
552
+ loadProjectManifestInput(
553
+ { bytes: records.projectManifest.bytes, artifact: records.projectManifest.artifact }
554
+ ),
555
+ loadKnowledgeManifestInput(
556
+ { bytes: records.knowledgeManifest.bytes, artifact: records.knowledgeManifest.artifact }
557
+ ),
558
+ loadKnowledgeEvidenceBundleInput(
559
+ { bytes: records.knowledgeEvidenceBundle.bytes, artifact: records.knowledgeEvidenceBundle.artifact }
560
+ )
561
+ ]);
562
+ validateVersionAnalysisManifest(records.versionAnalysis.value);
563
+ validateUsageIndex(records.usageIndex.value);
564
+ validateRepositoryImpact(records.repositoryImpact.value);
565
+ validateRepositoryImpactEvidence(records.repositoryImpactEvidence.value);
566
+ } catch (error) {
567
+ if (error instanceof MigrationChecklistInputError) throw error;
568
+ fail(error.message, error.code ?? 'ARTIFACT_VALIDATION_FAILED');
569
+ }
570
+ return { projectInput, knowledgeInput, bundleInput };
571
+ }
572
+
573
+ /** Load, schema-validate, and cross-check the seven immutable MVP-05 inputs. */
574
+ export async function loadMigrationChecklistInputs(input, options = {}) {
575
+ let invocation = invocationSources(input, options);
576
+ if (
577
+ !invocation.sources
578
+ && !invocation.rootUrl
579
+ && Object.keys(invocation.artifacts).length === 0
580
+ ) {
581
+ const selection = await resolveArtifactRootChain(
582
+ invocation.repositoryRoot,
583
+ ARTIFACTS.map(({ artifact }) => artifact),
584
+ { onDiagnostic: options.onCompatibilityDiagnostic }
585
+ );
586
+ invocation = {
587
+ ...invocation,
588
+ selectedArtifacts: Object.fromEntries(
589
+ ARTIFACTS.map(({ key }, index) => [key, selection.artifacts[index]])
590
+ )
591
+ };
592
+ }
593
+ const records = {};
594
+ for (const descriptor of ARTIFACTS) {
595
+ const source = sourceFor(descriptor, invocation);
596
+ const artifact = artifactFor(descriptor, source, invocation);
597
+ records[descriptor.key] = await readArtifact(descriptor, source, artifact);
598
+ }
599
+ const { projectInput, knowledgeInput, bundleInput } = await validateParsedArtifacts(records);
600
+ const artifacts = {
601
+ projectManifest: projectInput.manifest,
602
+ knowledgeManifest: knowledgeInput.manifest,
603
+ knowledgeEvidenceBundle: bundleInput.bundle,
604
+ versionAnalysis: records.versionAnalysis.value,
605
+ usageIndex: records.usageIndex.value,
606
+ repositoryImpact: records.repositoryImpact.value,
607
+ repositoryImpactEvidence: records.repositoryImpactEvidence.value,
608
+ input: lineageMetadata(records, projectInput, knowledgeInput, bundleInput)
609
+ };
610
+ validateMigrationChecklistInputLineage(artifacts);
611
+ validateMigrationChecklistInputReferences(artifacts);
612
+ return deepFreeze(structuredClone(artifacts));
613
+ }