@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,75 @@
1
+ # MVP-02 Knowledge Manifest Generation and CLI
2
+
3
+ MVP-02-08 turns the validated internal `KnowledgeResearchResult` into the portable Knowledge Manifest consumed by MVP-03. The public artifact is written only after JSON Schema and runtime invariant validation pass.
4
+
5
+ ```text
6
+ Project Manifest → Research Plan → Knowledge Research
7
+
8
+
9
+ KnowledgeResearchResult
10
+
11
+
12
+ Knowledge Manifest builder
13
+
14
+
15
+ schema + runtime validation
16
+
17
+
18
+ atomic knowledge-manifest.json write
19
+ ```
20
+
21
+ ## Builder and public contract
22
+
23
+ The builder creates Knowledge Manifest schema `1.0.0` without mutating the internal research result. It preserves package and source IDs, occurrences, registry facts, provenance, warnings, execution timestamps, and public cache totals. Internal execution-only fields such as adapter invocation detail, cache corruption bookkeeping, invalid occurrence evidence, unsupported ecosystem aggregates, and source-candidate field paths do not enter the public artifact.
24
+
25
+ Research occurrences contain internal ecosystem and normalized-name fields while planning and orchestration run. The builder projects them onto the public occurrence contract, which retains project path, manifest path, dependency type, declared name, and declared version only.
26
+
27
+ The builder validates with the existing Draft 2020-12 [Knowledge Manifest schema](../schemas/knowledge-manifest.schema.json) and `validateKnowledgeManifestInvariants`. Invalid input throws before any writer is called.
28
+
29
+ ## Deterministic research identity
30
+
31
+ `research.researchId` is `sha256` over canonical JSON containing:
32
+
33
+ 1. the exact Project Manifest artifact digest;
34
+ 2. a canonical fingerprint of the public research policy; and
35
+ 3. source digests ordered by source ID.
36
+
37
+ A source uses its snapshot content digest when available. An unfetched source has a digest of its canonical public provenance fields instead. Timestamps, temporary filenames, adapter completion order, and random values do not affect the ID.
38
+
39
+ ## Atomic serialization
40
+
41
+ The writer uses UTF-8, two-space JSON indentation, and one final newline. It creates the parent directory, writes a sibling temporary file, flushes it, closes it, then renames it over the destination. Existing manifests are replaced atomically; a failed write never exposes a partial target file.
42
+
43
+ The default artifact location is:
44
+
45
+ ```text
46
+ .upgradelens/knowledge-manifest.json
47
+ ```
48
+
49
+ ## CLI
50
+
51
+ Run discovery first, then research the generated Project Manifest:
52
+
53
+ ```bash
54
+ upgradelens discover .
55
+ upgradelens research .
56
+ ```
57
+
58
+ `research` supports:
59
+
60
+ ```bash
61
+ upgradelens research .
62
+ upgradelens research . --output artifacts/knowledge.json
63
+ upgradelens research . --stdout
64
+ upgradelens research . --offline
65
+ ```
66
+
67
+ `--stdout` emits only pretty Knowledge Manifest JSON. Normal execution reports concise progress to standard error. `--output` is a portable path relative to the repository root.
68
+
69
+ ## Offline mode
70
+
71
+ `--offline` configures npm and PyPI adapters for fresh-cache-only research. No registry request is attempted. Fresh cache entries normalize normally. Missing, expired, or corrupted entries do not fall back to networking or stale data; the package becomes `unavailable` with an `OFFLINE_CACHE_MISS` warning. Existing cache details, paths, keys, headers, validators, and response bodies remain private.
72
+
73
+ ## Boundary
74
+
75
+ MVP-02-08 writes the public Knowledge Manifest but does not implement version comparison, release analysis, GitHub APIs, documentation crawling, source-content fetching, breaking-change analysis, impact analysis, migration planning, AI, or a new analysis artifact. MVP-03 consumes the immutable manifest as its input.
@@ -0,0 +1,279 @@
1
+ # MVP-02 Knowledge Manifest Contract
2
+
3
+ The Knowledge Manifest is the versioned public artifact produced by UpgradeLens Knowledge Research. It captures normalized package facts, source provenance, execution metadata, cache outcomes, and non-fatal warnings for one Project Manifest. MVP-02 collects facts; it does not compare versions, identify breaking changes, assess repository impact, or recommend migrations.
4
+
5
+ The contract is defined by the [Knowledge Manifest JSON Schema](../schemas/knowledge-manifest.schema.json). Schema version `1.0.0` is independent from Project Manifest schema `2.0.0`, the npm package version, and later analysis-artifact versions.
6
+
7
+ ## Ownership and immutability
8
+
9
+ MVP-02 owns the Knowledge Manifest. Once a manifest has passed schema and runtime invariant validation and has been published atomically, it is immutable. Downstream stages consume it without repairing or enriching it in place:
10
+
11
+ ```text
12
+ Project Manifest (MVP-01)
13
+
14
+
15
+ Knowledge Manifest (MVP-02)
16
+
17
+
18
+ Version Analysis Manifest (MVP-03)
19
+ ```
20
+
21
+ If an upstream fact is missing, the owning stage must produce a new versioned artifact. MVP-03 may not modify the Knowledge Manifest or rescan the repository.
22
+
23
+ ## Knowledge Store versus Knowledge Manifest
24
+
25
+ The Knowledge Store and Knowledge Manifest serve different purposes:
26
+
27
+ | Knowledge Store | Knowledge Manifest |
28
+ | --- | --- |
29
+ | Internal reusable cache | Portable public artifact |
30
+ | Content-addressed response envelopes | Normalized facts and provenance |
31
+ | Owns ETag, Last-Modified, TTL state, cache keys, and storage layout | Owns portable content digests, source freshness, and cache counts |
32
+ | May be deleted and rebuilt | Immutable after successful publication |
33
+ | Not consumed as an MVP-03 contract | Consumed directly by downstream MVPs |
34
+
35
+ The manifest rejects store paths, internal cache keys, HTTP validators, authorization data, and unversioned storage-envelope fields through `additionalProperties: false`. It also rejects local absolute paths. Content digests identify evidence without exposing how or where it is stored.
36
+
37
+ ## Top-level contract
38
+
39
+ Every Knowledge Manifest requires these fields:
40
+
41
+ | Field | Purpose |
42
+ | --- | --- |
43
+ | `schemaVersion` | Knowledge Manifest contract version; exactly `1.0.0` |
44
+ | `generatedAt` | UTC date-time when the validated artifact was assembled |
45
+ | `generator` | UpgradeLens name and producing package version |
46
+ | `input` | Project Manifest lineage without duplicating the input artifact |
47
+ | `policy` | Non-secret research behavior for this execution |
48
+ | `research` | Execution identity, timestamps, counts, retries, and partial failures |
49
+ | `summary` | Canonical package/source/status/warning/cache totals |
50
+ | `packages` | Package-centric research records |
51
+ | `sources` | Deduplicated source and snapshot provenance records |
52
+ | `cache` | Portable cache outcome summary |
53
+ | `warnings` | Deterministically ordered non-fatal conditions |
54
+
55
+ All public objects reject undeclared fields. The schema document itself has no custom top-level `schemaVersion` keyword; the version is expressed only as `properties.schemaVersion.const`.
56
+
57
+ ## Generator and schema versioning
58
+
59
+ `generator.name` is always `UpgradeLens`. `generator.version` records the package version that produced the artifact but is intentionally not a schema constant. A package release can continue producing Knowledge Manifest `1.0.0` while implementation details evolve compatibly.
60
+
61
+ An incompatible public-contract change requires a new Knowledge Manifest schema version. Project Manifest schema changes independently. Consumers must validate the version they support before reading nested records.
62
+
63
+ ## Project Manifest lineage
64
+
65
+ `input.projectManifest` records:
66
+
67
+ - Project Manifest schema version `2.0.0`;
68
+ - its repository-relative artifact path;
69
+ - a `sha256:<64 lowercase hexadecimal characters>` digest;
70
+ - repository name and root `.`.
71
+
72
+ `artifactDigest` fingerprints the exact bytes of the Project Manifest, not a reserialized object. This preserves lineage without copying the complete Project Manifest into the Knowledge Manifest. Artifact, project, and dependency-manifest paths use portable forward-slash relative paths; absolute paths, Windows drive paths, backslashes, and parent traversal are rejected.
73
+
74
+ ## Research policy
75
+
76
+ `policy` records execution behavior rather than an upgrade recommendation:
77
+
78
+ - `mode`: `online` or `offline`;
79
+ - `policyVersion`;
80
+ - public HTTPS `registryBases` for npm and PyPI;
81
+ - `ttlPolicyVersion`;
82
+ - `sourceAllowlistVersion`;
83
+ - `includePrereleases`.
84
+
85
+ Only public npm and PyPI bases exist in the MVP-02 contract. Tokens, authorization headers, signed URLs, credentials, and arbitrary secret fields are not allowed.
86
+
87
+ ## Research execution metadata
88
+
89
+ `research` contains a deterministic `researchId`, `startedAt`, `completedAt`, derived `durationMs`, input occurrence/package counts, researched package and source counts, cache hit/miss/revalidation counts, retry count, and partial-failure count.
90
+
91
+ The research ID is a SHA-256 identity derived from the input artifact digest, policy fingerprint, and canonically ordered source content digests. It is not a transient process/run ID. Timestamps and duration are execution facts and can differ between executions even when normalized package facts are unchanged.
92
+
93
+ Runtime invariants define:
94
+
95
+ - `inputPackageCount` as the number of package records;
96
+ - `researchedPackageCount` as package records excluding `invalid` identities;
97
+ - `partialFailureCount` as package records whose status is not `resolved`;
98
+ - `durationMs` as `completedAt - startedAt` in milliseconds.
99
+
100
+ ## Summary and relational invariants
101
+
102
+ The summary reports input occurrences, unique package records, every package-status count, sources, warnings, cache hits/misses, and stale sources. These are counts of known manifest structures, not placeholders for unavailable external facts.
103
+
104
+ JSON Schema validates field presence, type, and non-negative integer ranges. The focused validator in `src/knowledge-manifest.js` validates relationships JSON Schema cannot express cleanly:
105
+
106
+ - package status counts sum to `packageCount`;
107
+ - package, occurrence, source, warning, and stale-source counts match their arrays;
108
+ - research and cache counts agree with the summary;
109
+ - duration agrees with execution timestamps;
110
+ - package/source IDs are unique and references resolve;
111
+ - package IDs agree with their registry and normalized identity;
112
+ - all contract arrays use canonical ordering.
113
+
114
+ Writers must pass both layers before atomically publishing the artifact.
115
+
116
+ ## Package identity and status
117
+
118
+ MVP-02 package IDs are package-centric:
119
+
120
+ ```text
121
+ npm:<normalized-name>
122
+ pypi:<normalized-name>
123
+ ```
124
+
125
+ Scoped npm IDs such as `npm:@scope/package` are valid. `identity.observedDeclaredNames` preserves declared spelling from the Project Manifest, while `normalizedName` is the registry lookup identity. `registryBaseUrl`, canonical package page, and API URL are HTTPS.
126
+
127
+ Package status is one of:
128
+
129
+ - `resolved`: required registry facts are available;
130
+ - `partial`: useful facts exist but one or more sources failed, conflicted, or are stale;
131
+ - `notFound`: the authoritative registry confirmed no package;
132
+ - `invalid`: the Project Manifest reference cannot form a supported public identity;
133
+ - `unavailable`: research could not obtain usable facts because a source was unavailable.
134
+
135
+ Every package contains `metadata`, `latest`, and `releaseIndex`. Missing optional metadata is omitted or represented by `null`; it is never represented by fake counts or invented values. `notFound`, `invalid`, and `unavailable` packages require `latest: null` and an empty release index, preventing unresolved facts from masquerading as valid zero values.
136
+
137
+ ## Occurrences
138
+
139
+ Package deduplication never discards Project Manifest occurrences. Each occurrence retains:
140
+
141
+ - project ID and repository-relative project path;
142
+ - repository-relative dependency manifest path;
143
+ - dependency type;
144
+ - declared name;
145
+ - declared version/reference, or `null` when unversioned.
146
+
147
+ Multiple projects can reference one package record, and duplicate declarations remain separate occurrences. This preserves repository context while keeping external knowledge package-centric.
148
+
149
+ ## Registry-designated latest and releases
150
+
151
+ `latest` records a registry fact, not a recommended target. npm uses `dist-tag:latest`; PyPI uses `project-info-version`. MVP-02 does not compute a maximum version or interpret npm SemVer or PEP 440.
152
+
153
+ Latest facts retain version, selection mechanism, optional publication/release URL data, source ID, and nullable prerelease/yanked/deprecated flags. `null` means the source did not provide that fact; `false` means it explicitly reported false.
154
+
155
+ Release entries contain either an opaque `version` or opaque `tag`, optional publication time/URL, nullable status flags, and source IDs. MVP-02 sorts identifiers lexically and performs no semantic ordering.
156
+
157
+ ## Source authority, trust, and snapshots
158
+
159
+ Each source has a stable ID, kind, field-specific authority, categorical trust, canonical HTTPS URL, status, supported evidence roles, discovery provenance, trust evidence, and an optional portable snapshot.
160
+
161
+ Source kinds are registry, official documentation, source repository, release feed, or community. Authority is `registryAuthoritative`, `officialProject`, `publisherProvided`, or `community`. Trust is categorical—`official`, `publisher`, `verified`, `community`, or `unknown`—and has no numeric score.
162
+
163
+ Registry records are authoritative for registry publication facts, but descriptions and links inside them remain publisher-controlled. Official documentation is preferred for project policy and usage statements. Community sources never override official or registry-authoritative facts.
164
+
165
+ A snapshot contains only content digest, media type, retrieval time, and `fresh`/`stale` state. `discoveredFrom`, `trustEvidenceSourceIds`, and `conflictsWith` reference top-level source IDs and are checked at runtime.
166
+
167
+ ## Warning behavior
168
+
169
+ Warnings represent non-fatal research conditions:
170
+
171
+ - `INVALID_PACKAGE_REFERENCE`
172
+ - `PACKAGE_NOT_FOUND`
173
+ - `REGISTRY_UNAVAILABLE`
174
+ - `REGISTRY_RATE_LIMITED`
175
+ - `REGISTRY_RESPONSE_INVALID`
176
+ - `DOCUMENTATION_NOT_FOUND`
177
+ - `REPOSITORY_NOT_FOUND`
178
+ - `RELEASE_METADATA_NOT_FOUND`
179
+ - `CACHE_EXPIRED`
180
+ - `OFFLINE_CACHE_MISS`
181
+ - `SOURCE_CONFLICT`
182
+
183
+ Each warning has a deterministic sanitized message and `retryable` flag. Package/source IDs are present when the condition can be scoped. A package stores warning-code references only; full records remain at the top level. Runtime validation checks both directions of that relationship.
184
+
185
+ Warnings never contain stack traces, secrets, absolute paths, raw headers, or complete upstream error bodies. A warning does not by itself make the artifact invalid; package and source statuses expose the resulting completeness.
186
+
187
+ ## Cache summary
188
+
189
+ The public `cache` object includes only mode, TTL policy version, hit/miss/revalidation counts, and stale-entry count. Counts must agree with `research` and `summary`. Store paths, keys, validators, envelopes, and persistence APIs remain private to the Knowledge Store.
190
+
191
+ ## Deterministic ordering
192
+
193
+ Writers use code-unit lexical comparison, not locale-sensitive comparison:
194
+
195
+ 1. packages by `id`;
196
+ 2. occurrences by project ID, manifest, dependency type, declared name, and declared version;
197
+ 3. releases by opaque version/tag, then joined source IDs;
198
+ 4. sources by `id`;
199
+ 5. observed names, source ID arrays, warning codes, evidence roles, trust evidence, and conflict IDs lexically;
200
+ 6. warnings by package ID, code, source ID, and message.
201
+
202
+ JSON Schema cannot assert array ordering or referential integrity. The runtime invariant validator rejects unsorted output and unknown references, preventing asynchronous completion or source payload enumeration from affecting the artifact.
203
+
204
+ ## Example
205
+
206
+ This empty but complete manifest is valid:
207
+
208
+ ```json
209
+ {
210
+ "schemaVersion": "1.0.0",
211
+ "generatedAt": "2026-07-14T00:00:00.000Z",
212
+ "generator": { "name": "UpgradeLens", "version": "<package version>" },
213
+ "input": {
214
+ "projectManifest": {
215
+ "schemaVersion": "2.0.0",
216
+ "artifact": ".upgradelens/project-manifest.json",
217
+ "artifactDigest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
218
+ "repository": { "name": "example-project", "root": "." }
219
+ }
220
+ },
221
+ "policy": {
222
+ "mode": "online",
223
+ "policyVersion": "1",
224
+ "registryBases": {
225
+ "npm": "https://registry.npmjs.org",
226
+ "pypi": "https://pypi.org"
227
+ },
228
+ "ttlPolicyVersion": "1",
229
+ "sourceAllowlistVersion": "1",
230
+ "includePrereleases": false
231
+ },
232
+ "research": {
233
+ "researchId": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
234
+ "startedAt": "2026-07-14T00:00:00.000Z",
235
+ "completedAt": "2026-07-14T00:00:00.000Z",
236
+ "durationMs": 0,
237
+ "inputOccurrenceCount": 0,
238
+ "inputPackageCount": 0,
239
+ "researchedPackageCount": 0,
240
+ "sourceCount": 0,
241
+ "cacheHitCount": 0,
242
+ "cacheMissCount": 0,
243
+ "cacheRevalidationCount": 0,
244
+ "retryCount": 0,
245
+ "partialFailureCount": 0
246
+ },
247
+ "summary": {
248
+ "inputOccurrenceCount": 0,
249
+ "packageCount": 0,
250
+ "resolvedPackageCount": 0,
251
+ "partialPackageCount": 0,
252
+ "notFoundPackageCount": 0,
253
+ "invalidPackageCount": 0,
254
+ "unavailablePackageCount": 0,
255
+ "sourceCount": 0,
256
+ "warningCount": 0,
257
+ "cacheHitCount": 0,
258
+ "cacheMissCount": 0,
259
+ "staleSourceCount": 0
260
+ },
261
+ "packages": [],
262
+ "sources": [],
263
+ "cache": {
264
+ "mode": "online",
265
+ "policyVersion": "1",
266
+ "hitCount": 0,
267
+ "missCount": 0,
268
+ "revalidationCount": 0,
269
+ "staleEntryCount": 0
270
+ },
271
+ "warnings": []
272
+ }
273
+ ```
274
+
275
+ Additional valid examples are under `test/fixtures/knowledge-manifest/`.
276
+
277
+ ## Limitations
278
+
279
+ This contract supports public npm and PyPI package facts only. It does not implement networking, registry adapters, Knowledge Store persistence, GitHub fetching, private registries, version comparison, changelog interpretation, breaking-change analysis, impact analysis, migration planning, AI/LLM reasoning, or a research CLI command.
@@ -0,0 +1,62 @@
1
+ # MVP-02 Knowledge Research Orchestration
2
+
3
+ MVP-02-07 connects the validated Research Plan, injected npm/PyPI registry adapters, and Source Provenance Resolution into one deterministic internal execution. It consumes the Project Manifest lineage already captured by Research Planning; it does not rescan a repository or read package-manager files.
4
+
5
+ > Knowledge Research Orchestration executes and combines deterministic research components. It does not publish the Knowledge Manifest, compare versions, or perform AI reasoning.
6
+
7
+ ```text
8
+ Loaded Project Manifest → Research Plan → bounded adapter research
9
+
10
+
11
+ complete registry result set
12
+
13
+
14
+ Source Provenance Resolution
15
+
16
+
17
+ internal KnowledgeResearchResult
18
+ ```
19
+
20
+ ## Input, adapter selection, and execution
21
+
22
+ The orchestrator first calls `validateResearchPlan`. An invalid plan is a fatal internal contract failure and no adapter is invoked. A researchable package selects its adapter solely through `package.registry`: `npm` selects the npm-compatible adapter and `pypi` selects the PyPI adapter. Package-manager, installer, manifest, dependency type, and package-name heuristics never affect selection.
23
+
24
+ Adapters are injected explicitly. The default concurrency is four and the accepted range is 1–32. A small worker pool schedules at most that many requests at once; output is sorted after all work completes, so adapter completion order does not affect the result. Cancellation is intentionally deferred because current adapters do not expose an orchestration-level signal contract.
25
+
26
+ Registry base URLs and TTLs remain adapter configuration rather than a duplicate orchestration policy. The orchestration policy presently owns only bounded concurrency; it accepts no credentials, private-registry settings, installer configuration, or source-crawling policy.
27
+
28
+ Each package is isolated. Adapter `resolved`, `partial`, `notFound`, and `unavailable` facts are preserved. A missing adapter or unexpected adapter exception becomes a sanitized `unavailable` result with `REGISTRY_UNAVAILABLE`; no stack, response, header, token, or cache path is retained. A missing adapter is a defensive integration diagnostic, not a new public warning code.
29
+
30
+ ## Result contract
31
+
32
+ `KnowledgeResearchResult` is internal and starts at `resultVersion: "1"`; it is independent from the Knowledge Manifest schema version. It contains:
33
+
34
+ - immutable Project Manifest lineage and Research Plan version;
35
+ - execution timestamps, duration, concurrency, total and per-registry invocation counts, source/cache/warning counts, and a sorted per-package outcome list;
36
+ - package records close to the Knowledge Manifest package shape, with all Research Plan occurrences attached;
37
+ - normalized source records, warnings, invalid occurrences, and unsupported ecosystem aggregates;
38
+ - canonical summary totals.
39
+
40
+ Invalid references remain in `invalidOccurrences`, with an `INVALID_PACKAGE_REFERENCE` warning that has no invented package ID. Unsupported ecosystem aggregates remain in `unsupported` and do not invoke an adapter or create a fake npm/PyPI package. `partialFailureCount` counts only researched package records whose status is not `resolved`; invalid occurrences are reported separately. A registry-resolved package becomes `partial` when Source Provenance Resolution reports a `SOURCE_CONFLICT` for that package. Missing optional URLs do not make a package partial.
41
+
42
+ ## Sources, cache outcomes, and warnings
43
+
44
+ Source Provenance Resolution runs once after every registry result is available. This allows source conflicts and future corroboration rules to see the complete package result set. Its package source IDs replace adapter-only package source IDs, and every source reference is validated before the result is returned.
45
+
46
+ Cache aggregation preserves current adapter meanings:
47
+
48
+ | Outcome | Counted as |
49
+ | --- | --- |
50
+ | `hit` | fresh cache hit |
51
+ | `miss` | fetched after no usable cache entry |
52
+ | `revalidated` | expired entry replaced by a normal fresh fetch, not HTTP `304` |
53
+ | `corrupted-replaced` | corrupted entry replaced after a valid fetch |
54
+ | `corrupted` | detected corruption without a successful replacement |
55
+
56
+ Adapter, provenance, and invalid-reference warnings are normalized, deduplicated, and sorted by package ID, code, source ID, and message. Unsupported planning warnings remain internal planning metadata until a later public contract explicitly adopts them.
57
+
58
+ ## Invariants and privacy
59
+
60
+ The runtime validator checks Research Plan lineage, one result per researchable package, status/cache/summary arithmetic, source and warning references, occurrence attachment, canonical ordering, provenance graph validity, timestamps, and result version. It rejects absolute local paths, credentials, query tokens, cache keys, store paths, ETag/Last-Modified values, raw headers, response bodies, and stack traces.
61
+
62
+ No public artifact is written. Knowledge Manifest assembly, a research CLI, GitHub/documentation fetching, retry orchestration, version comparison, breaking-change analysis, and AI behavior remain outside MVP-02-07.
@@ -0,0 +1,87 @@
1
+ # MVP-02 Lightweight Knowledge Store
2
+
3
+ MVP-02-03 implements the smallest useful part of the Knowledge Store: a private, deterministic filesystem cache for future npm and PyPI registry adapters. It prevents repeat retrieval work once adapters exist, while keeping cache implementation details out of the portable Knowledge Manifest.
4
+
5
+ The approved MVP-02 architecture remains the long-term direction. This implementation deliberately does not yet provide its complete content-addressed evidence store, HTTP validator handling, or multi-repository configuration. It is easy to delete and rebuild because it stores only cache envelopes, never a repository scan or public artifact.
6
+
7
+ ## Boundary and location
8
+
9
+ The store is private to MVP-02 implementation:
10
+
11
+ ```text
12
+ Research package
13
+
14
+
15
+ Lightweight Knowledge Store
16
+
17
+
18
+ Registry adapter decision
19
+ ```
20
+
21
+ Its default root is relative to the process working directory:
22
+
23
+ ```text
24
+ .upgradelens/cache/knowledge/v1/
25
+ ```
26
+
27
+ Internal callers may configure a different root, which makes the store straightforward to test. The directory layout, filesystem path, cache key, and envelope are not part of either the Project Manifest or Knowledge Manifest contract. No downstream MVP consumes this directory.
28
+
29
+ ## Identity and key
30
+
31
+ Each entry is package/source-centric. `createCacheIdentity` accepts exactly these portable fields:
32
+
33
+ - `adapter` (for example, `npm` or `pypi`)
34
+ - `resourceKind` (for example, `registry-package`)
35
+ - `packageId` (for example, `npm:react` or `npm:@scope/package`)
36
+ - `resourceVariant` (for example, `full-metadata`)
37
+ - `adapterVersion`
38
+
39
+ The cache calculates a SHA-256 filename from the canonical JSON identity. Scoped npm names and other package characters therefore never become directory names. Repository paths, Project Manifest digests, timestamps, retry counters, credentials, authorization, user agents, and volatile request headers are deliberately absent from this identity. The same resource request can be reused across runs without exposing its storage key in a public artifact.
40
+
41
+ ## Envelope contract
42
+
43
+ Every stored JSON file has this strict internal shape:
44
+
45
+ ```json
46
+ {
47
+ "envelopeVersion": "1",
48
+ "identity": {
49
+ "adapter": "npm",
50
+ "resourceKind": "registry-package",
51
+ "packageId": "npm:react",
52
+ "resourceVariant": "full-metadata",
53
+ "adapterVersion": "1"
54
+ },
55
+ "storedAt": "2026-07-14T00:00:00.000Z",
56
+ "expiresAt": "2026-07-15T00:00:00.000Z",
57
+ "bodyDigest": "sha256:<64 lowercase hexadecimal characters>",
58
+ "body": {}
59
+ }
60
+ ```
61
+
62
+ `bodyDigest` is SHA-256 over UTF-8 canonical JSON for `body`: object keys are sorted recursively and array order is preserved. Functions, symbols, BigInt, undefined values, non-finite numbers, circular references, and non-plain objects are rejected. This makes logically equal JSON objects produce the same digest regardless of insertion order, while preserving meaningful list order.
63
+
64
+ The store rejects envelope fields beyond this contract and validates the version, identity, dates, expiry order, body digest, and body privacy on every read. Envelopes never persist cache keys, absolute local paths, credentials, authorization data, HTTP headers, ETag, Last-Modified, or raw error stacks.
65
+
66
+ ## Read states and TTL
67
+
68
+ `cache.read(identity)` returns a structured result instead of turning normal cache conditions into exceptions:
69
+
70
+ | State | Meaning |
71
+ | --- | --- |
72
+ | `fresh` | A valid entry exists and `now < expiresAt`; its body and metadata are returned. |
73
+ | `expired` | A valid entry exists and `now >= expiresAt`; its body and metadata are returned for the adapter to decide whether stale data is usable. |
74
+ | `missing` | No entry exists. |
75
+ | `corrupted` | The file cannot be trusted (for example invalid JSON, wrong identity, unsupported envelope version, invalid dates, or digest mismatch). The body is never returned. |
76
+
77
+ TTL is caller policy. `cache.write(identity, body, { ttlMs })` requires a non-negative integer TTL and computes `expiresAt = storedAt + ttlMs` using an injectable clock. The cache does not select registry TTLs, refresh records, make stale-fallback decisions, schedule invalidation, cache failures, or clean old files in the background.
78
+
79
+ Operational filesystem failures—such as permission errors, unavailable storage, or an atomic rename failure—throw focused internal `KnowledgeCacheError` values with stable codes. They are not converted into registry warnings by this layer.
80
+
81
+ ## Atomic writes and concurrency
82
+
83
+ Writes create parent directories, serialize the full envelope, write and flush a temporary file beside the target, close it, then rename it to the SHA-256 final filename. A failure cannot leave a partial final entry; temporary-file cleanup is best effort. The implementation intentionally does not provide locks or cross-process coordination. Concurrent-write policy is deferred until registry orchestration has a concrete need.
84
+
85
+ ## Intentional limitations
86
+
87
+ MVP-02-03 does not implement HTTP fetching, npm/PyPI adapters, ETag, Last-Modified, conditional requests, shared-cache configuration, databases, background cleanup, distributed locking, portable evidence bundles, Knowledge Manifest generation, version analysis, or AI behavior. Those capabilities remain outside this small internal cache layer.
@@ -0,0 +1,97 @@
1
+ # MVP-02 PyPI Registry Adapter
2
+
3
+ MVP-02-05 adds an internal adapter for public Python package facts from PyPI. It consumes a package-centric Research Plan record, uses the private Knowledge Store, and returns internal package, registry-source, cache-outcome, and warning records. It does not assemble or write a Knowledge Manifest.
4
+
5
+ > The `pypi:` identity represents the PyPI registry ecosystem. It does not mean that the source project uses pip.
6
+
7
+ ## Python installers and PyPI identity
8
+
9
+ pip, pip-tools, Poetry, uv, and Pipenv can all declare the same public PyPI project. The adapter uses only the normalized registry identity:
10
+
11
+ ```text
12
+ pypi:<normalized-name>
13
+ ```
14
+
15
+ Names follow the PyPA-compatible rule already used by Research Planning: lowercase and collapse runs of `.`, `_`, and `-` to `-`. For example, `LangChain_OpenAI` and `langchain-openai` identify `pypi:langchain-openai`; `zope.interface` identifies `pypi:zope-interface`.
16
+
17
+ The adapter does not invoke installer CLIs and does not read `pip.conf`, Poetry/uv/Pipenv lockfiles, requirements options, or repository configuration. Installer identity never enters requests or cache keys.
18
+
19
+ ## Public API and request construction
20
+
21
+ The primary official API is PyPI project JSON:
22
+
23
+ ```text
24
+ GET https://pypi.org/pypi/<normalized-project-name>/json
25
+ ```
26
+
27
+ The default metadata base is `https://pypi.org`; the future JSON Index base is `https://pypi.org/simple`. Both may be supplied as explicit HTTPS policy inputs for tests or later orchestration. HTTP, credentials, query-bearing bases, and repository-derived custom indexes are rejected.
28
+
29
+ MVP-02-05 makes only the project JSON request. It does not scrape HTML, search PyPI, or issue a second Index API request. The existing bounded JSON fetcher provides GET-only requests, JSON `Accept`, a stable UpgradeLens user agent, omitted credentials, disabled redirects, timeout, and response-size limits.
30
+
31
+ PyPI project JSON is generally smaller than a full npm packument, but established projects can still exceed 1 MB. The adapter owns an **8 MiB** default (`8 * 1024 * 1024`). Callers may explicitly override `maxResponseBytes` for tests or a future execution policy; it must be a positive safe integer and must not exceed the 64 MiB safety ceiling. This execution setting is not emitted in the Knowledge Manifest. Oversized project JSON retains the internal `PYPI_RESPONSE_TOO_LARGE` code, maps to the existing public `REGISTRY_RESPONSE_INVALID` warning, and is never cached.
32
+
33
+ ## Cache behavior
34
+
35
+ The private [Lightweight Knowledge Store](MVP-02-Knowledge-Store.md) uses this identity:
36
+
37
+ ```json
38
+ {
39
+ "adapter": "pypi",
40
+ "resourceKind": "registry-package",
41
+ "packageId": "pypi:fastapi",
42
+ "resourceVariant": "project-json",
43
+ "adapterVersion": "1"
44
+ }
45
+ ```
46
+
47
+ The default TTL is 24 hours, supplied by the adapter rather than the generic store. Fresh entries are normalized without a request. Missing entries fetch, validate, atomically cache, and normalize. Expired entries fetch a replacement; failure produces unavailable facts rather than silently returning stale data. Corrupted entries are never used and are overwritten only after a valid response. Cache outcomes do not expose cache paths, filenames, keys, validators, or envelopes.
48
+
49
+ ## Project JSON normalization
50
+
51
+ The adapter validates `info`, `info.name`, and the requested normalized identity before caching. `info.name` can differ in case, underscore, dot, or hyphen spelling only when its normalized form matches the requested `pypi:` identity. `info.version` is optional: if absent or malformed, the adapter does not select another release and returns partial facts.
52
+
53
+ `info.version` is the only registry-designated latest fact. It is recorded as `selection: project-info-version`; no PEP 440 parsing, semantic ordering, prerelease filtering, range comparison, or upgrade recommendation occurs.
54
+
55
+ When project JSON exposes `releases`, release identifiers are sorted with code-unit lexical ordering. Each entry contains only the opaque version, earliest valid upload time, PyPI version page, nullable yanked state, and source ID. File URLs, hashes, wheels, dependency graphs, and build details are omitted. The normalizer is isolated so a later task can use the JSON Index API without changing common output fields.
56
+
57
+ Yanked state uses only explicit file metadata:
58
+
59
+ - all files explicitly `yanked: true` → `true`;
60
+ - at least one file explicitly `yanked: false` → `false`;
61
+ - otherwise → `null`.
62
+
63
+ Missing file metadata is never converted to `false`.
64
+
65
+ ## Metadata and Project URLs
66
+
67
+ The adapter normalizes a safe summary/description, preferring `license_expression` over `license`, and retains an explicit Development Status classifier in the existing `projectStatus` field. It does not infer licenses from classifiers or assign risk/recommendation meaning to project status.
68
+
69
+ Publisher-controlled `info.project_urls` labels are classified case-insensitively:
70
+
71
+ - `Documentation` / `Docs` → `documentationUrl`
72
+ - `Source` / `Repository` / `Code` → `repositoryUrl`
73
+ - `Issues` / `Tracker` / `Bug Tracker` → `issueUrl`
74
+ - `Homepage` / `Home` → `homepageUrl`
75
+
76
+ `Changelog`, `Changes`, `Release Notes`, and `Releases` are retained only as sanitized internal source candidates for Source Provenance Resolution; they are not public metadata fields and are not emitted in a Knowledge Manifest until later assembly. Unknown labels are ignored rather than guessed from domains or URL paths. `home_page` is used only as a safe homepage fallback when no classified homepage exists.
77
+
78
+ Only HTTPS public URLs are retained. Credentialed URLs, query strings, and fragments are rejected or removed. `git+https:` repository URLs are normalized to HTTPS and GitHub `.git` suffixes are removed for display. Documentation and repository links remain publisher-trust candidates; MVP-02-05 does not corroborate them as official sources.
79
+
80
+ ## Source provenance and errors
81
+
82
+ A successful result creates a registry source such as `pypi:fastapi:registry`, with registry authority, publisher trust, PyPI project/API URLs, supported fact roles, portable content digest, retrieval timestamp, and fresh snapshot. Store internals, ETag, Last-Modified, headers, credentials, and local paths never appear in this output.
83
+
84
+ | Condition | Package status | Warning |
85
+ | --- | --- | --- |
86
+ | PyPI `404` | `notFound` | `PACKAGE_NOT_FOUND` |
87
+ | PyPI `429` | `unavailable` | `REGISTRY_RATE_LIMITED` |
88
+ | `5xx`, timeout, or transport failure | `unavailable` | `REGISTRY_UNAVAILABLE` |
89
+ | Invalid JSON/media/size/project shape/name mismatch | `unavailable` | `REGISTRY_RESPONSE_INVALID` |
90
+
91
+ Errors are sanitized; raw response bodies, headers, stack traces, tokens, credentialed URLs, and cache details are never returned. Negative responses, invalid responses, rate limits, and transport failures are not cached.
92
+
93
+ All response bodies are consumed or explicitly cancelled. In particular, unused non-200 responses and wrong-media responses are cancelled, and an oversized stream cancels its reader before releasing the lock. Timeout handles are cleared in `finally` after every outcome. Online CLI research injects a scoped, CLI-owned fetch and closes its Agent after completion; direct callers retain ownership of injected fetch resources.
94
+
95
+ ## Intentional limitations
96
+
97
+ This adapter does not implement private indexes, authentication, Python installer execution/configuration/lockfile parsing, dependency resolution, PEP 440 comparison, target selection, documentation fetching, GitHub Releases, source corroboration, PyPI JSON Index requests, Knowledge Manifest orchestration, a research CLI, breaking-change analysis, or AI behavior.