PTSIP 0.3.8b3__py3-none-win_amd64.whl

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 (600) hide show
  1. agent_contracts/__init__.py +5 -0
  2. agent_contracts/actions/analyze-source-profile.yaml +13 -0
  3. agent_contracts/actions/apply-adoption.yaml +12 -0
  4. agent_contracts/actions/apply-profile-transition.yaml +11 -0
  5. agent_contracts/actions/build-adoption-plan.yaml +10 -0
  6. agent_contracts/actions/build-transition-plan.yaml +10 -0
  7. agent_contracts/actions/capture-repository-snapshot.yaml +13 -0
  8. agent_contracts/actions/collect-conformance-evidence.yaml +13 -0
  9. agent_contracts/actions/compare-profile-authority.yaml +10 -0
  10. agent_contracts/actions/emit-conformance-result.yaml +10 -0
  11. agent_contracts/actions/evaluate-conformance-rules.yaml +10 -0
  12. agent_contracts/actions/map-profile-target.yaml +10 -0
  13. agent_contracts/actions/project-authority-winner.yaml +13 -0
  14. agent_contracts/actions/read-relevant-authority.yaml +13 -0
  15. agent_contracts/actions/resolve-adoption-facts.yaml +11 -0
  16. agent_contracts/actions/resolve-profile-schema.yaml +14 -0
  17. agent_contracts/actions/resolve-reconciliation-state.yaml +10 -0
  18. agent_contracts/actions/validate-profile-structure.yaml +10 -0
  19. agent_contracts/actions/validate-responsibility-map-semantics.yaml +10 -0
  20. agent_contracts/bindings/current.yaml +125 -0
  21. agent_contracts/candidate.py +383 -0
  22. agent_contracts/canonicalization/ptsip-canonical-json-v1.json +81 -0
  23. agent_contracts/conditions/adopt-apply-requested.yaml +10 -0
  24. agent_contracts/conditions/adopt-authority-fresh.yaml +10 -0
  25. agent_contracts/conditions/adopt-facts-resolved.yaml +10 -0
  26. agent_contracts/conditions/adopt-profile-fresh.yaml +10 -0
  27. agent_contracts/conditions/adopt-repository-fresh.yaml +10 -0
  28. agent_contracts/conditions/adopt-snapshot-identified.yaml +10 -0
  29. agent_contracts/conditions/adopt-write-authorized.yaml +10 -0
  30. agent_contracts/conditions/authority-authority-fresh.yaml +10 -0
  31. agent_contracts/conditions/authority-coordination-available.yaml +10 -0
  32. agent_contracts/conditions/authority-profile-fresh.yaml +10 -0
  33. agent_contracts/conditions/authority-projection-required.yaml +10 -0
  34. agent_contracts/conditions/authority-semantic-conflict-absent.yaml +10 -0
  35. agent_contracts/conditions/authority-write-authorized.yaml +10 -0
  36. agent_contracts/conditions/conform-profile-ready.yaml +10 -0
  37. agent_contracts/conditions/conform-snapshot-identified.yaml +10 -0
  38. agent_contracts/conditions/migrate-apply-requested.yaml +10 -0
  39. agent_contracts/conditions/migrate-source-fresh.yaml +10 -0
  40. agent_contracts/conditions/migrate-source-profile-identified.yaml +10 -0
  41. agent_contracts/conditions/migrate-target-contract-resolved.yaml +10 -0
  42. agent_contracts/conditions/migrate-target-fresh.yaml +10 -0
  43. agent_contracts/conditions/migrate-write-authorized.yaml +10 -0
  44. agent_contracts/conditions/validate-contract-resolved.yaml +10 -0
  45. agent_contracts/conditions/validate-profile-selected.yaml +10 -0
  46. agent_contracts/conformance/current.json +987 -0
  47. agent_contracts/contracts/current.json +3978 -0
  48. agent_contracts/digests/policy-v1.json +7746 -0
  49. agent_contracts/digests/projection-v1.json +7698 -0
  50. agent_contracts/gates/adoption-write.yaml +18 -0
  51. agent_contracts/gates/authority-projection-write.yaml +18 -0
  52. agent_contracts/gates/profile-transition-write.yaml +15 -0
  53. agent_contracts/index.json +427 -0
  54. agent_contracts/index.yaml +113 -0
  55. agent_contracts/machine_resolver.py +81 -0
  56. agent_contracts/operations/adopt.yaml +99 -0
  57. agent_contracts/operations/conform.yaml +57 -0
  58. agent_contracts/operations/migrate-profile.yaml +85 -0
  59. agent_contracts/operations/reconcile-authority.yaml +87 -0
  60. agent_contracts/operations/validate.yaml +53 -0
  61. agent_contracts/promotion/current.json +50 -0
  62. agent_contracts/promotion/index.json +13 -0
  63. agent_contracts/promotion/specification-contract.json +49 -0
  64. agent_contracts/resolver.py +253 -0
  65. agent_contracts/schemas/candidate-group.schema.json +107 -0
  66. agent_contracts/schemas/candidate-index.schema.json +246 -0
  67. agent_contracts/schemas/candidate-vectors.schema.json +49 -0
  68. agent_contracts/schemas/contracts/action.schema.json +21 -0
  69. agent_contracts/schemas/contracts/binding.schema.json +32 -0
  70. agent_contracts/schemas/contracts/condition.schema.json +25 -0
  71. agent_contracts/schemas/contracts/index.schema.json +27 -0
  72. agent_contracts/schemas/contracts/io.schema.json +12 -0
  73. agent_contracts/schemas/contracts/mutation-gate.schema.json +17 -0
  74. agent_contracts/schemas/contracts/operation.schema.json +259 -0
  75. agent_contracts/schemas/contracts/promotion.schema.json +81 -0
  76. agent_contracts/schemas/contracts/spec.schema.json +13 -0
  77. agent_contracts/schemas/contracts/vocabulary.schema.json +11 -0
  78. agent_contracts/schemas/io/adopt-request.schema.json +8 -0
  79. agent_contracts/schemas/io/adopt-result.schema.json +6 -0
  80. agent_contracts/schemas/io/adopt-state.schema.json +12 -0
  81. agent_contracts/schemas/io/authority-request.schema.json +8 -0
  82. agent_contracts/schemas/io/authority-result.schema.json +6 -0
  83. agent_contracts/schemas/io/authority-state.schema.json +13 -0
  84. agent_contracts/schemas/io/conform-request.schema.json +6 -0
  85. agent_contracts/schemas/io/conform-result.schema.json +6 -0
  86. agent_contracts/schemas/io/conform-state.schema.json +6 -0
  87. agent_contracts/schemas/io/migrate-request.schema.json +11 -0
  88. agent_contracts/schemas/io/migrate-result.schema.json +6 -0
  89. agent_contracts/schemas/io/migrate-state.schema.json +11 -0
  90. agent_contracts/schemas/io/validate-request.schema.json +6 -0
  91. agent_contracts/schemas/io/validate-result.schema.json +6 -0
  92. agent_contracts/schemas/io/validate-state.schema.json +6 -0
  93. agent_contracts/spec/adoption-procedure.yaml +148 -0
  94. agent_contracts/spec/adoption.yaml +33 -0
  95. agent_contracts/spec/agent-behavior.yaml +148 -0
  96. agent_contracts/spec/authority.yaml +40 -0
  97. agent_contracts/spec/classification.yaml +64 -0
  98. agent_contracts/spec/conformance-detail.yaml +120 -0
  99. agent_contracts/spec/conformance.yaml +43 -0
  100. agent_contracts/spec/core.yaml +29 -0
  101. agent_contracts/spec/governance.yaml +30 -0
  102. agent_contracts/spec/profile-transition-detail.yaml +78 -0
  103. agent_contracts/spec/profile-transition.yaml +30 -0
  104. agent_contracts/spec/repository-boundaries.yaml +148 -0
  105. agent_contracts/spec/responsibility-map-detail.yaml +92 -0
  106. agent_contracts/spec/responsibility-map.yaml +36 -0
  107. agent_contracts/spec/specification-governance.yaml +91 -0
  108. agent_contracts/validator.py +723 -0
  109. agent_contracts/vocabulary/authority.yaml +20 -0
  110. agent_contracts/vocabulary/diagnostics.yaml +23 -0
  111. agent_contracts/vocabulary/lifecycle.yaml +20 -0
  112. agent_contracts/vocabulary/outcomes.yaml +26 -0
  113. agent_contracts/vocabulary/terminology.yaml +428 -0
  114. ptsip/__init__.py +5 -0
  115. ptsip/__main__.py +4 -0
  116. ptsip/adoption.py +225 -0
  117. ptsip/agent_contracts/__init__.py +12 -0
  118. ptsip/agent_contracts/bindings/current.yaml +38 -0
  119. ptsip/agent_contracts/index.yaml +53 -0
  120. ptsip/agent_contracts/operations/adopt.yaml +49 -0
  121. ptsip/agent_contracts/operations/conform.yaml +42 -0
  122. ptsip/agent_contracts/operations/migrate-profile.yaml +45 -0
  123. ptsip/agent_contracts/operations/reconcile-authority.yaml +40 -0
  124. ptsip/agent_contracts/operations/validate.yaml +36 -0
  125. ptsip/agent_contracts/runtime.py +102 -0
  126. ptsip/agent_contracts/schemas/binding.schema.json +151 -0
  127. ptsip/agent_contracts/schemas/index.schema.json +51 -0
  128. ptsip/agent_contracts/schemas/operation.schema.json +122 -0
  129. ptsip/agent_contracts/schemas/spec.schema.json +96 -0
  130. ptsip/agent_contracts/schemas/vocabulary.schema.json +50 -0
  131. ptsip/agent_contracts/spec/adoption.yaml +23 -0
  132. ptsip/agent_contracts/spec/authority.yaml +32 -0
  133. ptsip/agent_contracts/spec/classification.yaml +31 -0
  134. ptsip/agent_contracts/spec/conformance.yaml +27 -0
  135. ptsip/agent_contracts/spec/core.yaml +21 -0
  136. ptsip/agent_contracts/spec/governance.yaml +17 -0
  137. ptsip/agent_contracts/spec/profile-transition.yaml +27 -0
  138. ptsip/agent_contracts/spec/responsibility-map.yaml +26 -0
  139. ptsip/agent_contracts/validator.py +195 -0
  140. ptsip/agent_contracts/vocabulary/authority.yaml +8 -0
  141. ptsip/agent_contracts/vocabulary/diagnostics.yaml +7 -0
  142. ptsip/agent_contracts/vocabulary/lifecycle.yaml +9 -0
  143. ptsip/agent_contracts/vocabulary/outcomes.yaml +8 -0
  144. ptsip/app/__init__.py +5 -0
  145. ptsip/app/client.py +53 -0
  146. ptsip/app/github_authority.py +659 -0
  147. ptsip/app/github_client.py +200 -0
  148. ptsip/app/github_reconciliation.py +405 -0
  149. ptsip/app/local_client.py +129 -0
  150. ptsip/app/server.py +130 -0
  151. ptsip/app/service.py +345 -0
  152. ptsip/app/store.py +283 -0
  153. ptsip/artifact_evidence.py +150 -0
  154. ptsip/build_resolution.py +315 -0
  155. ptsip/clarification/__init__.py +6 -0
  156. ptsip/clarification/generator.py +345 -0
  157. ptsip/clarification/generator_core.py +87 -0
  158. ptsip/clarification/i18n.py +110 -0
  159. ptsip/clarification/model.py +32 -0
  160. ptsip/clarification/render.py +79 -0
  161. ptsip/clarification/resolution/__init__.py +34 -0
  162. ptsip/clarification/resolution/model.py +90 -0
  163. ptsip/clarification/resolution/parser.py +97 -0
  164. ptsip/clarification/resolution/profile_projection.py +467 -0
  165. ptsip/clarification/resolution/resolver.py +72 -0
  166. ptsip/clarification/transports/__init__.py +1 -0
  167. ptsip/clarification/transports/github_issue.py +162 -0
  168. ptsip/cli.py +1181 -0
  169. ptsip/conformance.py +627 -0
  170. ptsip/conformance_audit.py +143 -0
  171. ptsip/conformance_engine.py +409 -0
  172. ptsip/constants.py +9 -0
  173. ptsip/context_plane.py +687 -0
  174. ptsip/dependency_analysis.py +232 -0
  175. ptsip/dependency_cache.py +110 -0
  176. ptsip/dependency_reconciliation.py +248 -0
  177. ptsip/dependency_review.py +174 -0
  178. ptsip/dependency_validation.py +106 -0
  179. ptsip/doctor.py +33 -0
  180. ptsip/engine_installation.py +191 -0
  181. ptsip/engines/context/Cargo.lock +792 -0
  182. ptsip/engines/context/Cargo.toml +16 -0
  183. ptsip/engines/context/contract.json +218 -0
  184. ptsip/engines/context/contracts/1.0.0.json +86 -0
  185. ptsip/engines/context/execution-bindings.json +19 -0
  186. ptsip/engines/context/manifest.json +22 -0
  187. ptsip/engines/context/runtime-contracts.json +34 -0
  188. ptsip/engines/context/src/loading.rs +15 -0
  189. ptsip/engines/context/src/main.rs +126 -0
  190. ptsip/engines/context/src/source.rs +185 -0
  191. ptsip/engines/context/transport.json +113 -0
  192. ptsip/engines/context/version.yaml +3 -0
  193. ptsip/engines/contracts/capability-owner-map.json +94 -0
  194. ptsip/engines/contracts/context-projection-handoff.json +12 -0
  195. ptsip/engines/contracts/dependency-graph.json +47 -0
  196. ptsip/engines/contracts/installation-plan.json +126 -0
  197. ptsip/engines/contracts/native-runtime.json +21 -0
  198. ptsip/engines/contracts/schemas/attempt-result.schema.json +87 -0
  199. ptsip/engines/contracts/schemas/capability-contract.schema.json +100 -0
  200. ptsip/engines/contracts/schemas/consumer-handoff.schema.json +135 -0
  201. ptsip/engines/contracts/schemas/contract-relation.schema.json +123 -0
  202. ptsip/engines/contracts/schemas/core-manifest.schema.json +148 -0
  203. ptsip/engines/contracts/schemas/dependency-graph.schema.json +130 -0
  204. ptsip/engines/contracts/schemas/derived-state.schema.json +64 -0
  205. ptsip/engines/contracts/schemas/effect-evidence.schema.json +58 -0
  206. ptsip/engines/contracts/schemas/execution-binding.schema.json +73 -0
  207. ptsip/engines/contracts/schemas/federated-source.schema.json +97 -0
  208. ptsip/engines/contracts/schemas/impact-result.schema.json +60 -0
  209. ptsip/engines/contracts/schemas/native-invocation-registry.schema.json +206 -0
  210. ptsip/engines/contracts/schemas/registry-snapshot.schema.json +163 -0
  211. ptsip/engines/contracts/schemas/registry-verification-input.schema.json +32 -0
  212. ptsip/engines/contracts/schemas/runtime-authority-consumption.schema.json +120 -0
  213. ptsip/engines/contracts/schemas/stop-ack.schema.json +28 -0
  214. ptsip/engines/contracts/schemas/stop-request.schema.json +25 -0
  215. ptsip/engines/contracts/schemas/stop-result.schema.json +61 -0
  216. ptsip/engines/contracts/schemas/storage-mediation.schema.json +73 -0
  217. ptsip/engines/contracts/schemas/transport-binding.schema.json +49 -0
  218. ptsip/engines/contracts/stdio-transport.json +113 -0
  219. ptsip/engines/contracts/vpms-caller-transition-plan.json +224 -0
  220. ptsip/engines/contracts/vpms-handoff.json +84 -0
  221. ptsip/engines/contracts/vpms-scope-mapping.json +116 -0
  222. ptsip/engines/contracts/work-item-bindings.json +292 -0
  223. ptsip/engines/discovery/Cargo.lock +792 -0
  224. ptsip/engines/discovery/Cargo.toml +16 -0
  225. ptsip/engines/discovery/contract.json +611 -0
  226. ptsip/engines/discovery/contracts/1.0.0.json +172 -0
  227. ptsip/engines/discovery/contracts/2.0.0.json +481 -0
  228. ptsip/engines/discovery/execution-bindings.json +19 -0
  229. ptsip/engines/discovery/manifest.json +22 -0
  230. ptsip/engines/discovery/runtime-contracts.json +232 -0
  231. ptsip/engines/discovery/src/invocation.rs +17 -0
  232. ptsip/engines/discovery/src/main.rs +129 -0
  233. ptsip/engines/discovery/src/registry.rs +358 -0
  234. ptsip/engines/discovery/src/source.rs +305 -0
  235. ptsip/engines/discovery/transport.json +113 -0
  236. ptsip/engines/discovery/version.yaml +3 -0
  237. ptsip/engines/execution/Cargo.lock +792 -0
  238. ptsip/engines/execution/Cargo.toml +16 -0
  239. ptsip/engines/execution/contract.json +607 -0
  240. ptsip/engines/execution/contracts/1.0.0.json +356 -0
  241. ptsip/engines/execution/contracts/2.0.0.json +361 -0
  242. ptsip/engines/execution/execution-bindings.json +19 -0
  243. ptsip/engines/execution/manifest.json +22 -0
  244. ptsip/engines/execution/runtime-contracts.json +428 -0
  245. ptsip/engines/execution/src/authority.rs +99 -0
  246. ptsip/engines/execution/src/effect.rs +66 -0
  247. ptsip/engines/execution/src/invocation.rs +163 -0
  248. ptsip/engines/execution/src/main.rs +185 -0
  249. ptsip/engines/execution/src/source.rs +305 -0
  250. ptsip/engines/execution/transport.json +113 -0
  251. ptsip/engines/execution/version.yaml +3 -0
  252. ptsip/engines/index.json +178 -0
  253. ptsip/engines/planning/Cargo.lock +792 -0
  254. ptsip/engines/planning/Cargo.toml +16 -0
  255. ptsip/engines/planning/contract.json +535 -0
  256. ptsip/engines/planning/contracts/1.0.0.json +95 -0
  257. ptsip/engines/planning/contracts/2.0.0.json +346 -0
  258. ptsip/engines/planning/execution-bindings.json +19 -0
  259. ptsip/engines/planning/manifest.json +22 -0
  260. ptsip/engines/planning/runtime-contracts.json +282 -0
  261. ptsip/engines/planning/src/impact.rs +217 -0
  262. ptsip/engines/planning/src/installation.rs +195 -0
  263. ptsip/engines/planning/src/main.rs +179 -0
  264. ptsip/engines/planning/src/preparation.rs +58 -0
  265. ptsip/engines/planning/src/source.rs +302 -0
  266. ptsip/engines/planning/transport.json +113 -0
  267. ptsip/engines/planning/version.yaml +3 -0
  268. ptsip/engines/projection/Cargo.lock +792 -0
  269. ptsip/engines/projection/Cargo.toml +16 -0
  270. ptsip/engines/projection/contract.json +226 -0
  271. ptsip/engines/projection/contracts/1.0.0.json +92 -0
  272. ptsip/engines/projection/execution-bindings.json +19 -0
  273. ptsip/engines/projection/manifest.json +22 -0
  274. ptsip/engines/projection/runtime-contracts.json +62 -0
  275. ptsip/engines/projection/src/main.rs +149 -0
  276. ptsip/engines/projection/src/rendering.rs +70 -0
  277. ptsip/engines/projection/src/source.rs +185 -0
  278. ptsip/engines/projection/transport.json +113 -0
  279. ptsip/engines/projection/version.yaml +3 -0
  280. ptsip/engines/registries/derived-snapshot.json +135 -0
  281. ptsip/engines/registries/official-execution.json +34 -0
  282. ptsip/engines/registries/official-static.json +35 -0
  283. ptsip/engines/storage/Cargo.lock +792 -0
  284. ptsip/engines/storage/Cargo.toml +16 -0
  285. ptsip/engines/storage/contract.json +382 -0
  286. ptsip/engines/storage/contracts/1.0.0.json +138 -0
  287. ptsip/engines/storage/contracts/2.0.0.json +116 -0
  288. ptsip/engines/storage/execution-bindings.json +19 -0
  289. ptsip/engines/storage/manifest.json +22 -0
  290. ptsip/engines/storage/provider-registry.json +8 -0
  291. ptsip/engines/storage/runtime-contracts.json +282 -0
  292. ptsip/engines/storage/src/main.rs +123 -0
  293. ptsip/engines/storage/src/native.rs +233 -0
  294. ptsip/engines/storage/src/provider.rs +145 -0
  295. ptsip/engines/storage/src/source.rs +198 -0
  296. ptsip/engines/storage/transport.json +113 -0
  297. ptsip/engines/storage/version.yaml +3 -0
  298. ptsip/engines/validation/Cargo.lock +792 -0
  299. ptsip/engines/validation/Cargo.toml +16 -0
  300. ptsip/engines/validation/contract.json +1241 -0
  301. ptsip/engines/validation/contracts/1.0.0.json +356 -0
  302. ptsip/engines/validation/contracts/2.0.0.json +683 -0
  303. ptsip/engines/validation/execution-bindings.json +19 -0
  304. ptsip/engines/validation/manifest.json +22 -0
  305. ptsip/engines/validation/runtime-contracts.json +216 -0
  306. ptsip/engines/validation/src/cases.rs +102 -0
  307. ptsip/engines/validation/src/composition.rs +104 -0
  308. ptsip/engines/validation/src/handoff.rs +215 -0
  309. ptsip/engines/validation/src/main.rs +201 -0
  310. ptsip/engines/validation/src/source.rs +185 -0
  311. ptsip/engines/validation/transport.json +113 -0
  312. ptsip/engines/validation/version.yaml +3 -0
  313. ptsip/evidence/__init__.py +33 -0
  314. ptsip/evidence/adapters.py +486 -0
  315. ptsip/evidence/contract.py +230 -0
  316. ptsip/evidence/normalization.py +173 -0
  317. ptsip/governance/__init__.py +45 -0
  318. ptsip/governance/authority.py +714 -0
  319. ptsip/governance/authorization.py +55 -0
  320. ptsip/governance/model.py +232 -0
  321. ptsip/governance/subject_matching.py +78 -0
  322. ptsip/governance/support_assets.py +91 -0
  323. ptsip/inspection/__init__.py +1 -0
  324. ptsip/inspection/candidate_evidence.py +537 -0
  325. ptsip/inspection/components.py +74 -0
  326. ptsip/inspection/dependencies.py +502 -0
  327. ptsip/inspection/dependencies_030.py +69 -0
  328. ptsip/inspection/dotnet.py +188 -0
  329. ptsip/inspection/go.py +157 -0
  330. ptsip/inspection/inventory.py +161 -0
  331. ptsip/inspection/javascript.py +376 -0
  332. ptsip/inspection/lexing.py +121 -0
  333. ptsip/inspection/python_resolution.py +164 -0
  334. ptsip/inspection/source_adapters.py +60 -0
  335. ptsip/lifecycle_evidence.py +310 -0
  336. ptsip/migration/__init__.py +156 -0
  337. ptsip/migration/analysis/__init__.py +20 -0
  338. ptsip/migration/analysis/analyzer.py +160 -0
  339. ptsip/migration/analysis/direct/__init__.py +5 -0
  340. ptsip/migration/analysis/direct/convergence.py +133 -0
  341. ptsip/migration/analysis/direct/result.py +91 -0
  342. ptsip/migration/analysis/direct/source_projection.py +42 -0
  343. ptsip/migration/analysis/direct/target_projection.py +71 -0
  344. ptsip/migration/analysis/findings/__init__.py +0 -0
  345. ptsip/migration/analysis/findings/architecture.py +29 -0
  346. ptsip/migration/analysis/findings/lifecycle.py +28 -0
  347. ptsip/migration/analysis/findings/work_requirements.py +103 -0
  348. ptsip/migration/analysis/issue.py +12 -0
  349. ptsip/migration/analysis/result.py +69 -0
  350. ptsip/migration/analysis/source/__init__.py +0 -0
  351. ptsip/migration/analysis/source/binding_validation.py +43 -0
  352. ptsip/migration/analysis/source/evidence_correlation.py +40 -0
  353. ptsip/migration/analysis/source/projection.py +252 -0
  354. ptsip/migration/analysis/source/repository_resolution.py +96 -0
  355. ptsip/migration/analysis/target/__init__.py +0 -0
  356. ptsip/migration/analysis/target/compatibility.py +142 -0
  357. ptsip/migration/analysis/target/relationship_findings.py +20 -0
  358. ptsip/migration/analysis/target/semantics.py +159 -0
  359. ptsip/migration/execution/__init__.py +5 -0
  360. ptsip/migration/execution/authorization/__init__.py +4 -0
  361. ptsip/migration/execution/authorization/binding.py +160 -0
  362. ptsip/migration/execution/authorization/preconditions.py +71 -0
  363. ptsip/migration/execution/authorization/proof.py +80 -0
  364. ptsip/migration/execution/direct/__init__.py +9 -0
  365. ptsip/migration/execution/direct/identity_plan.py +133 -0
  366. ptsip/migration/execution/direct/identity_rewrite.py +322 -0
  367. ptsip/migration/execution/direct/promotion.py +180 -0
  368. ptsip/migration/execution/direct/verification.py +118 -0
  369. ptsip/migration/execution/error.py +2 -0
  370. ptsip/migration/execution/guard/__init__.py +3 -0
  371. ptsip/migration/execution/guard/mutation.py +104 -0
  372. ptsip/migration/execution/guard/repository_snapshot.py +47 -0
  373. ptsip/migration/execution/ledger/__init__.py +3 -0
  374. ptsip/migration/execution/ledger/record.py +149 -0
  375. ptsip/migration/execution/ledger/store.py +113 -0
  376. ptsip/migration/execution/mutation/__init__.py +5 -0
  377. ptsip/migration/execution/mutation/asynchronous.py +41 -0
  378. ptsip/migration/execution/mutation/delta.py +243 -0
  379. ptsip/migration/execution/mutation/finalization.py +90 -0
  380. ptsip/migration/execution/mutation/source.py +57 -0
  381. ptsip/migration/execution/promotion/__init__.py +4 -0
  382. ptsip/migration/execution/promotion/mutation.py +55 -0
  383. ptsip/migration/execution/promotion/preparation.py +79 -0
  384. ptsip/migration/execution/promotion/verification.py +87 -0
  385. ptsip/migration/execution/recovery.py +93 -0
  386. ptsip/migration/execution/state/__init__.py +3 -0
  387. ptsip/migration/execution/state/phase.py +17 -0
  388. ptsip/migration/execution/state/promotion.py +25 -0
  389. ptsip/migration/execution/state/recovery.py +31 -0
  390. ptsip/migration/execution/state/source_steps.py +78 -0
  391. ptsip/migration/planning/__init__.py +12 -0
  392. ptsip/migration/planning/convergence/__init__.py +0 -0
  393. ptsip/migration/planning/convergence/builder.py +324 -0
  394. ptsip/migration/planning/convergence/final_state.py +132 -0
  395. ptsip/migration/planning/convergence/reconciliation.py +175 -0
  396. ptsip/migration/planning/direct/__init__.py +3 -0
  397. ptsip/migration/planning/direct/builder.py +346 -0
  398. ptsip/migration/planning/direct/reference.py +26 -0
  399. ptsip/migration/planning/proposal_derivation.py +137 -0
  400. ptsip/migration/planning/result.py +106 -0
  401. ptsip/migration/proposal/__init__.py +9 -0
  402. ptsip/migration/proposal/bundle.py +207 -0
  403. ptsip/migration/proposal/semantic_identity.py +40 -0
  404. ptsip/migration/proposal/source_set.py +50 -0
  405. ptsip/migration/proposal/target_delta.py +88 -0
  406. ptsip/model.py +165 -0
  407. ptsip/package-composition.json +266 -0
  408. ptsip/package-distribution.json +16 -0
  409. ptsip/pilot/__init__.py +1 -0
  410. ptsip/pilot/runner.py +170 -0
  411. ptsip/profiles/__init__.py +1 -0
  412. ptsip/profiles/catalog.py +157 -0
  413. ptsip/profiles/compatibility.py +184 -0
  414. ptsip/profiles/contracts.py +100 -0
  415. ptsip/profiles/example.ptsip.yaml +149 -0
  416. ptsip/profiles/hybrid-python-package.ptsip.yaml +40 -0
  417. ptsip/profiles/identity.py +314 -0
  418. ptsip/profiles/index.yaml +22 -0
  419. ptsip/profiles/metadata.py +61 -0
  420. ptsip/profiles/template-python-package.ptsip.yaml +18 -0
  421. ptsip/proposed_component.py +128 -0
  422. ptsip/remediation/__init__.py +77 -0
  423. ptsip/remediation/domain/__init__.py +39 -0
  424. ptsip/remediation/domain/model.py +417 -0
  425. ptsip/remediation/escalation/__init__.py +37 -0
  426. ptsip/remediation/escalation/contract.py +388 -0
  427. ptsip/remediation/escalation/engine.py +212 -0
  428. ptsip/remediation/rules/__init__.py +23 -0
  429. ptsip/remediation/rules/contract.py +276 -0
  430. ptsip/repository/__init__.py +1 -0
  431. ptsip/repository/discover.py +68 -0
  432. ptsip/repository/namespace.py +86 -0
  433. ptsip/repository/profile_convergence.py +409 -0
  434. ptsip/repository/profile_path.py +97 -0
  435. ptsip/repository/profile_transition.py +377 -0
  436. ptsip/repository/remote.py +54 -0
  437. ptsip/repository/schemas/engine-state.schema.json +1 -0
  438. ptsip/repository/schemas/repository-index.schema.json +104 -0
  439. ptsip/repository/snapshot.py +173 -0
  440. ptsip/review_evidence.py +406 -0
  441. ptsip/source_compat/__init__.py +56 -0
  442. ptsip/source_compat/integrity.py +131 -0
  443. ptsip/source_compat/model.py +260 -0
  444. ptsip/source_compat/reader.py +336 -0
  445. ptsip/spec_identity.py +24 -0
  446. ptsip/specdata/project-profile-contracts.yaml +36 -0
  447. ptsip/specdata/ptsip-agent-classification.schema.json +57 -0
  448. ptsip/specdata/ptsip-artifact-evidence.schema.json +68 -0
  449. ptsip/specdata/ptsip-diagnostic.schema.json +58 -0
  450. ptsip/specdata/ptsip-normalized-evidence.schema.json +126 -0
  451. ptsip/specdata/ptsip-profile-pp-1.01.schema.json +309 -0
  452. ptsip/specdata/ptsip-profile-pp-1.02.schema.json +604 -0
  453. ptsip/specdata/ptsip-profile.schema.json +296 -0
  454. ptsip/specdata/ptsip-registry.yaml +333 -0
  455. ptsip/specdata/ptsip-source-profile-0.3.4.schema.json +55 -0
  456. ptsip/specdata/ptsip-source-profile-0.3.6.schema.json +35 -0
  457. ptsip/specification_binding.py +246 -0
  458. ptsip/storage/__init__.py +1 -0
  459. ptsip/storage/local_state.py +40 -0
  460. ptsip/support/policy/ARCH/SFP-ARCH-0001.yaml +131 -0
  461. ptsip/support/policy/ARCH/SFP-ARCH-0002.yaml +46 -0
  462. ptsip/support/policy/ARCH/SFP-ARCH-0003.yaml +39 -0
  463. ptsip/support/policy/ARCH/SFP-ARCH-0004.yaml +1215 -0
  464. ptsip/support/policy/ARCH/SFP-ARCH-0005.yaml +76 -0
  465. ptsip/support/policy/ASSURE/SFP-ASSURE-0001.yaml +45 -0
  466. ptsip/support/policy/ASSURE/SFP-ASSURE-0002.yaml +45 -0
  467. ptsip/support/policy/ASSURE/SFP-ASSURE-0003.yaml +46 -0
  468. ptsip/support/policy/ASSURE/SFP-ASSURE-0004.yaml +148 -0
  469. ptsip/support/policy/ASSURE/SFP-ASSURE-0005.yaml +115 -0
  470. ptsip/support/policy/CHANGE/SFP-CHANGE-0001.yaml +256 -0
  471. ptsip/support/policy/CNTR/SFP-CNTR-0001.yaml +242 -0
  472. ptsip/support/policy/CNTR/SFP-CNTR-0002.yaml +44 -0
  473. ptsip/support/policy/CNTR/SFP-CNTR-0003.yaml +54 -0
  474. ptsip/support/policy/CNTR/SFP-CNTR-0004.yaml +47 -0
  475. ptsip/support/policy/CNTR/SFP-CNTR-0005.yaml +1772 -0
  476. ptsip/support/policy/CNTR/SFP-CNTR-0006.yaml +131 -0
  477. ptsip/support/policy/CNTR/SFP-CNTR-0007.yaml +146 -0
  478. ptsip/support/policy/CNTR/SFP-CNTR-0008.yaml +57 -0
  479. ptsip/support/policy/CTRL/SFP-CTRL-0001.yaml +95 -0
  480. ptsip/support/policy/CTRL/SFP-CTRL-0002.yaml +415 -0
  481. ptsip/support/policy/CTRL/SFP-CTRL-0003.yaml +715 -0
  482. ptsip/support/policy/GOV/SFP-GOV-0001.yaml +55 -0
  483. ptsip/support/policy/GOV/SFP-GOV-0002.yaml +93 -0
  484. ptsip/support/policy/INFO/SFP-INFO-0001.yaml +271 -0
  485. ptsip/support/policy/INFO/SFP-INFO-0002.yaml +42 -0
  486. ptsip/support/policy/INFO/SFP-INFO-0003.yaml +41 -0
  487. ptsip/support/policy/INFO/SFP-INFO-0004.yaml +133 -0
  488. ptsip/support/policy/INFO/SFP-INFO-0005.yaml +135 -0
  489. ptsip/support/policy/INFO/SFP-INFO-0006.yaml +67 -0
  490. ptsip/support/policy/INFO/SFP-INFO-0007.yaml +256 -0
  491. ptsip/support/policy/INFO/SFP-INFO-0008.yaml +99 -0
  492. ptsip/support/policy/INFO/SFP-INFO-0009.yaml +56 -0
  493. ptsip/support/policy/INTENT/SFP-INTENT-0001.yaml +39 -0
  494. ptsip/support/policy/NORM/SFP-NORM-0001.yaml +45 -0
  495. ptsip/support/policy/NORM/SFP-NORM-0002.yaml +38 -0
  496. ptsip/support/policy/OPS/SFP-OPS-0001.yaml +40 -0
  497. ptsip/support/policy/OPS/SFP-OPS-0002.yaml +270 -0
  498. ptsip/support/policy/REAL/SFP-REAL-0001.yaml +47 -0
  499. ptsip/support/policy/RECORD/SFP-RECORD-0001.yaml +39 -0
  500. ptsip/support/policy/RECORD/SFP-RECORD-0002.yaml +40 -0
  501. ptsip/support/policy/RECORD/SFP-RECORD-0003.yaml +403 -0
  502. ptsip/support/policy/RECORD/SFP-RECORD-0004.yaml +186 -0
  503. ptsip/support/policy/RISK/SFP-RISK-0001.yaml +34 -0
  504. ptsip/support/policy/SUPPLY/SFP-SUPPLY-0001.yaml +43 -0
  505. ptsip/support/policy/index.yaml +183 -0
  506. ptsip/support/policy/legacy/SFP-0001.yaml +28 -0
  507. ptsip/support/policy/legacy/SFP-0002.yaml +28 -0
  508. ptsip/support/policy/legacy/SFP-0003.yaml +27 -0
  509. ptsip/support/policy/legacy/SFP-0004.yaml +53 -0
  510. ptsip/support/policy/legacy/SFP-0005.yaml +32 -0
  511. ptsip/support/policy/legacy/SFP-0006.yaml +43 -0
  512. ptsip/support/policy/legacy/SFP-0007.yaml +52 -0
  513. ptsip/support/policy/legacy/SFP-0008.yaml +66 -0
  514. ptsip/support/policy/legacy/SFP-0009.yaml +54 -0
  515. ptsip/support/policy/legacy/SFP-0010.yaml +40 -0
  516. ptsip/support/policy/legacy/SFP-0011.yaml +42 -0
  517. ptsip/support/policy/legacy/SFP-0012.yaml +38 -0
  518. ptsip/support/policy/legacy/SFP-0013.yaml +38 -0
  519. ptsip/support/policy/legacy/SFP-0014.yaml +46 -0
  520. ptsip/support/policy/legacy/SFP-0015.yaml +52 -0
  521. ptsip/support/policy/legacy/SFP-0016.yaml +52 -0
  522. ptsip/support/policy/legacy/SFP-0017.yaml +23 -0
  523. ptsip/support/policy/legacy/SFP-0018.yaml +41 -0
  524. ptsip/support/policy/legacy/SFP-0019.yaml +40 -0
  525. ptsip/support/policy/legacy/SFP-0020.yaml +34 -0
  526. ptsip/support/policy/legacy/SFP-0021.yaml +44 -0
  527. ptsip/support/policy/legacy/SFP-0022.yaml +53 -0
  528. ptsip/support/policy/legacy/SFP-0023.yaml +20 -0
  529. ptsip/support/policy/legacy/SFP-0024.yaml +118 -0
  530. ptsip/support/registries/ptsip-support-authority-role-registry.yaml +319 -0
  531. ptsip/support/registries/ptsip-support-authority-schema-registry.yaml +239 -0
  532. ptsip/support/registries/ptsip-support-authority-subject-registry.yaml +68 -0
  533. ptsip/support/registries/ptsip-support-authorization-registry.yaml +14 -0
  534. ptsip/support/registries/root-family-migration.json +3097 -0
  535. ptsip/support/registries/root-family-projection-module-admission.json +23 -0
  536. ptsip/support/registries/root-family-projection.module.json +201 -0
  537. ptsip/support/schemas/ptsip-support-authority-eligibility-result.schema.json +114 -0
  538. ptsip/support/schemas/ptsip-support-authority-role.schema.json +126 -0
  539. ptsip/support/schemas/ptsip-support-authority-semantics.schema.json +378 -0
  540. ptsip/support/schemas/ptsip-support-feature-policy-index.schema.json +57 -0
  541. ptsip/support/schemas/ptsip-support-feature-policy.schema.json +186 -0
  542. ptsip/support/schemas/ptsip-support-governance-registry.schema.json +28 -0
  543. ptsip/support/schemas/ptsip-support-project-authority-record.schema.json +136 -0
  544. ptsip/support/schemas/ptsip-support-root-family-policy.schema.json +206 -0
  545. ptsip/support/schemas/ptsip-support-subject-binding.schema.json +60 -0
  546. ptsip/support/schemas/root-family-migration.schema.json +240 -0
  547. ptsip/support/schemas/root-family-projection-module.schema.json +276 -0
  548. ptsip/topology.py +747 -0
  549. ptsip/validation/__init__.py +1 -0
  550. ptsip/validation/components.py +357 -0
  551. ptsip/validation/handoff.py +75 -0
  552. ptsip/validation/profile.py +514 -0
  553. ptsip/validation/rules.py +186 -0
  554. ptsip/validation/specification.py +60 -0
  555. ptsip/validation/templates.py +767 -0
  556. ptsip/validation_capture.py +168 -0
  557. ptsip-0.3.8b3.data/scripts/ptsip-engine-context.exe +0 -0
  558. ptsip-0.3.8b3.data/scripts/ptsip-engine-discovery.exe +0 -0
  559. ptsip-0.3.8b3.data/scripts/ptsip-engine-execution.exe +0 -0
  560. ptsip-0.3.8b3.data/scripts/ptsip-engine-planning.exe +0 -0
  561. ptsip-0.3.8b3.data/scripts/ptsip-engine-projection.exe +0 -0
  562. ptsip-0.3.8b3.data/scripts/ptsip-engine-storage.exe +0 -0
  563. ptsip-0.3.8b3.data/scripts/ptsip-engine-validation.exe +0 -0
  564. ptsip-0.3.8b3.dist-info/METADATA +361 -0
  565. ptsip-0.3.8b3.dist-info/RECORD +600 -0
  566. ptsip-0.3.8b3.dist-info/WHEEL +4 -0
  567. ptsip-0.3.8b3.dist-info/entry_points.txt +3 -0
  568. ptsip-0.3.8b3.dist-info/licenses/License-Authority/COMMERCIAL-USE.md +301 -0
  569. ptsip-0.3.8b3.dist-info/licenses/License-Authority/LICENSE-GUIDE.md +159 -0
  570. ptsip-0.3.8b3.dist-info/licenses/License-Authority/LICENSE-HISTORY.md +15 -0
  571. ptsip-0.3.8b3.dist-info/licenses/License-Authority/LICENSE.md +414 -0
  572. ptsip-0.3.8b3.dist-info/licenses/License-Authority/NOTICE.md +22 -0
  573. ptsip-0.3.8b3.dist-info/licenses/License-Authority/license-authority.yaml +133 -0
  574. ptsip-0.3.8b3.dist-info/ptsip-engines.json +78 -0
  575. vpms/__init__.py +70 -0
  576. vpms/contract_runtime.py +124 -0
  577. vpms/contracts/execution-composition.json +1 -0
  578. vpms/contracts/identity-registry.json +69 -0
  579. vpms/contracts/index.json +1 -0
  580. vpms/contracts/invariants.json +27 -0
  581. vpms/contracts/protocol.json +1 -0
  582. vpms/contracts/schemas/catalog.schema.json +80 -0
  583. vpms/contracts/schemas/product-contract.schema.json +572 -0
  584. vpms/contracts/schemas/selection-request.schema.json +47 -0
  585. vpms/contracts/schemas/selection-result.schema.json +79 -0
  586. vpms/contracts/schemas/selection-rule.schema.json +31 -0
  587. vpms/contracts/selection.json +1 -0
  588. vpms/domain/__init__.py +1 -0
  589. vpms/domain/model.py +88 -0
  590. vpms/domain/registry.py +350 -0
  591. vpms/domain/snapshot.py +118 -0
  592. vpms/execution/__init__.py +1 -0
  593. vpms/execution/adapters/__init__.py +1 -0
  594. vpms/execution/adapters/command.py +67 -0
  595. vpms/execution/composition.py +58 -0
  596. vpms/execution/runner.py +69 -0
  597. vpms/integration/__init__.py +1 -0
  598. vpms/integration/ptsip_bridge.py +156 -0
  599. vpms/selection/__init__.py +4 -0
  600. vpms/selection/resolver.py +130 -0
@@ -0,0 +1,174 @@
1
+ """Bounded, read-only evidence packages for unresolved support-contract review."""
2
+ from __future__ import annotations
3
+
4
+ from copy import deepcopy
5
+ import hashlib
6
+ import json
7
+ from pathlib import Path
8
+ import tokenize
9
+
10
+ from .repository.snapshot import capture_snapshot, git_tracked_files
11
+ from .storage.local_state import ptsip_home, repository_fingerprint
12
+
13
+
14
+ def _serialized(value: object) -> str:
15
+ return json.dumps(value, ensure_ascii=False, sort_keys=True, indent=2)
16
+
17
+
18
+ def _size(value: object) -> int:
19
+ return len(_serialized(value).encode("utf-8"))
20
+
21
+
22
+ def _snippet(root: Path, path: str, line: int, kind: str) -> dict[str, object] | None:
23
+ candidate = root / path
24
+ if not candidate.resolve().is_relative_to(root) or candidate.is_symlink():
25
+ return None
26
+ try:
27
+ with tokenize.open(candidate) as source:
28
+ lines = source.read().splitlines()
29
+ except (OSError, UnicodeError, SyntaxError):
30
+ return None
31
+ if not 1 <= line <= len(lines):
32
+ return None
33
+ start = max(1, line - 3)
34
+ end = min(len(lines), start + 6)
35
+ selected = "\n".join(lines[start - 1:end])
36
+ return {
37
+ "path": path, "line": line, "start_line": start, "end_line": end,
38
+ "kind": kind, "text": selected[:1200], "truncated": len(selected) > 1200,
39
+ }
40
+
41
+
42
+ def _callers(report: dict, item: dict, max_files: int) -> list[dict]:
43
+ source = item["dependency"]["source"]
44
+ candidates = item.get("callers")
45
+ if candidates is None:
46
+ candidates = [
47
+ {"path": edge["dependency"]["source"], "line": edge["dependency"].get("line"),
48
+ "evidence_id": edge["evidence_id"], "semantics": "OBSERVED_FILE_DEPENDENCY"}
49
+ for edge in report["items"] if edge["dependency"].get("resolved_path") == source
50
+ ]
51
+ selected, files = [], {source}
52
+ for caller in sorted(candidates, key=lambda value: (
53
+ value.get("path", ""), value.get("line") or 0, value.get("evidence_id", "")
54
+ )):
55
+ path = caller.get("path")
56
+ if not path or (path not in files and len(files) >= max_files):
57
+ continue
58
+ files.add(path)
59
+ selected.append({**{key: caller.get(key) for key in ("path", "line", "evidence_id")},
60
+ "semantics": caller.get("semantics") or caller.get("kind")})
61
+ if len(selected) >= 4:
62
+ break
63
+ return selected
64
+
65
+
66
+ def build_review_pack(
67
+ report: dict, *, max_items: int = 8, max_context_bytes_per_item: int = 12000,
68
+ max_source_files_per_item: int = 4,
69
+ ) -> dict:
70
+ """Select only REVIEW_REQUIRED items; never invoke an AI or assert authority."""
71
+ if max_items < 0 or max_context_bytes_per_item < 1 or max_source_files_per_item < 1:
72
+ raise ValueError("Review budgets require nonnegative max-items and positive byte/file caps")
73
+ if report.get("status") != "RAN":
74
+ raise ValueError("Review Pack requires stable analysis with a valid selected profile")
75
+ root = Path(report["repository"]["root"]).resolve()
76
+ eligible = sorted((item for item in report["items"] if item["actionability"] == "REVIEW_REQUIRED"),
77
+ key=lambda item: item["evidence_id"])
78
+ reviews, deferred = [], []
79
+ for item in eligible:
80
+ if len(reviews) >= max_items:
81
+ deferred.append({"evidence_id": item["evidence_id"], "reason": "ITEM_BUDGET"})
82
+ continue
83
+ usage = deepcopy(item.get("usage", {}))
84
+ fallback = usage.pop("fallback", {"detected": False, "semantics": "NO_SYNTAX_EVIDENCE"})
85
+ callers = _callers(report, item, max_source_files_per_item)
86
+ review = {
87
+ "review_id": "review:" + item["evidence_id"], "evidence_id": item["evidence_id"],
88
+ "actionability": "REVIEW_REQUIRED", "reason": item["reason"],
89
+ "dependency": deepcopy(item["dependency"]), "component": deepcopy(item["component"]),
90
+ "target_component": item.get("target_component"),
91
+ "declaration": deepcopy(item.get("declaration", {})), "usage": usage,
92
+ "fallback": fallback, "callers": callers, "snippets": [],
93
+ "question": "What support contract governs this dependency, and is its fallback sufficient for that contract?",
94
+ "authority": "REVIEW_PROPOSAL_ONLY",
95
+ }
96
+ if _size(review) > max_context_bytes_per_item:
97
+ deferred.append({"evidence_id": item["evidence_id"], "reason": "BASE_EVIDENCE_EXCEEDS_BYTE_BUDGET"})
98
+ continue
99
+ source = item["dependency"]["source"]
100
+ references = [(source, item["dependency"].get("line"), "IMPORT")]
101
+ references += [(source, line, "GUARD") for line in usage.get("guard_lines", [])[:4]]
102
+ references += [(value.get("path", source), value.get("line"), "FALLBACK")
103
+ for value in fallback.get("evidence", [])[:4]]
104
+ references += [(source, line, "USAGE") for line in usage.get("usage_lines", [])[:8]]
105
+ references += [(value["path"], value["line"], "INCOMING_FILE_DEPENDENCY") for value in callers]
106
+ files, seen = set(), set()
107
+ for path, line, kind in references:
108
+ if not isinstance(path, str) or not isinstance(line, int) or (path, line) in seen:
109
+ continue
110
+ if path not in files and len(files) >= max_source_files_per_item:
111
+ continue
112
+ seen.add((path, line))
113
+ snippet = _snippet(root, path, line, kind)
114
+ if snippet is None:
115
+ continue
116
+ review["snippets"].append(snippet)
117
+ if _size(review) > max_context_bytes_per_item:
118
+ review["snippets"].pop()
119
+ else:
120
+ files.add(path)
121
+ if not any(snippet["kind"] == "IMPORT" for snippet in review["snippets"]):
122
+ deferred.append({"evidence_id": item["evidence_id"],
123
+ "reason": "IMPORT_CONTEXT_UNAVAILABLE_WITHIN_BUDGET"})
124
+ continue
125
+ reviews.append(review)
126
+ return {
127
+ "format": "ptsip-dependency-review-pack/v1", "repository": deepcopy(report["repository"]),
128
+ "component": report.get("component"), "snapshot": deepcopy(report.get("snapshot", {})),
129
+ "summary": {"review_required_total": len(eligible), "selected_for_review": len(reviews),
130
+ "deferred": len(deferred), "ai_reviewed_items": 0},
131
+ "budget": {"only": ["REVIEW_REQUIRED"], "max_items": max_items,
132
+ "max_context_bytes_per_item": max_context_bytes_per_item,
133
+ "max_source_files_per_item": max_source_files_per_item,
134
+ "byte_measure": "UTF-8 serialized standalone review object, ensure_ascii=false, indent=2, sort_keys=true"},
135
+ "reviews": reviews, "deferred": deferred,
136
+ "authority": "ADVISORY_ONLY", "conformance_evaluated": False,
137
+ }
138
+
139
+
140
+ def write_review_pack(report: dict, output: str | Path | None = None, **budgets) -> tuple[dict, Path]:
141
+ root = Path(report["repository"]["root"]).resolve()
142
+ pack = build_review_pack(report, **budgets)
143
+ observed = capture_snapshot(root).as_dict()
144
+ expected = report.get("snapshot", {}).get("after", {})
145
+ identity = ("is_git", "head", "status_fingerprint", "tracked_content_fingerprint", "tracked_files")
146
+ if not expected or observed["observation_errors"] or any(observed[key] != expected.get(key) for key in identity):
147
+ raise RuntimeError("Repository changed after dependency analysis; regenerate the Review Pack")
148
+ data = json.dumps(pack, ensure_ascii=False, sort_keys=True, separators=(",", ":")) + "\n"
149
+ if output is None:
150
+ destination = (ptsip_home() / "dependency-reviews" / repository_fingerprint(root)
151
+ / (hashlib.sha256(data.encode("utf-8")).hexdigest()[:20] + ".json")).resolve()
152
+ if destination.is_relative_to(root):
153
+ raise ValueError("Default Review Pack state must stay outside the Consumer Repository")
154
+ else:
155
+ destination = Path(output).expanduser().resolve()
156
+ if destination.suffix.lower() != ".json":
157
+ raise ValueError("Review Pack output must be a JSON report path")
158
+ if destination.is_relative_to(root):
159
+ tracked, errors = git_tracked_files(root)
160
+ if observed["is_git"] and errors:
161
+ raise RuntimeError("Cannot verify Review Pack output against tracked files")
162
+ protected = {str((root / rel).resolve()).casefold() for rel in tracked}
163
+ if str(destination).casefold() in protected:
164
+ raise ValueError("Review Pack output must not overwrite a tracked repository path")
165
+ if any(part in {".git", "src"} for part in destination.relative_to(root).parts):
166
+ raise ValueError("Review Pack output must not be written into source or Git state")
167
+ if destination.exists():
168
+ if destination.read_bytes() != data.encode("utf-8"):
169
+ raise FileExistsError("Review Pack output already exists; choose a new report path")
170
+ return pack, destination
171
+ destination.parent.mkdir(parents=True, exist_ok=True)
172
+ with destination.open("x", encoding="utf-8", newline="\n") as stream:
173
+ stream.write(data)
174
+ return pack, destination
@@ -0,0 +1,106 @@
1
+ """Advisory validation selection from declared ownership and observed imports."""
2
+ from __future__ import annotations
3
+
4
+ from pathlib import Path
5
+ import re
6
+ import tomllib
7
+
8
+ from .validation.components import partition_components, selector_matches_path
9
+ from .validation.profile import validate_profile
10
+ from .repository.snapshot import capture_snapshot, compare_snapshots
11
+
12
+
13
+ def validation_plan(root, report, *, changed_paths=()):
14
+ before = capture_snapshot(root)
15
+ expected = report.get("snapshot", {}).get("after", {})
16
+ if any(before.as_dict()[key] != expected.get(key) for key in (
17
+ "head", "status_fingerprint", "tracked_content_fingerprint")):
18
+ return {"status": "BLOCKED", "reason": "STALE_ANALYSIS", "steps": []}
19
+ result = _validation_plan(root, report, changed_paths=changed_paths)
20
+ if not compare_snapshots(before, capture_snapshot(root)).stable:
21
+ return {"status": "BLOCKED", "reason": "SNAPSHOT_CHANGED", "steps": []}
22
+ return result
23
+
24
+
25
+ def _validation_plan(root, report, *, changed_paths=()):
26
+ root = Path(root).resolve()
27
+ validation = validate_profile(root, report.get("profile_path"))
28
+ if not validation.valid or not validation.resolved_profile or report["status"] != "RAN":
29
+ return {"status": "BLOCKED", "reason": "VALID_PROFILE_AND_SNAPSHOT_REQUIRED", "steps": []}
30
+ payload = validation.resolved_profile.effective_payload
31
+ components = {item["id"]: item for item in payload["components"]}
32
+ partition = partition_components(root, list(components.values()))
33
+ owners = {item.path: item.component_id for item in partition.assignments}
34
+ changed = sorted(set(str(path).replace("\\", "/").removeprefix("./") for path in changed_paths))
35
+ if any(path not in owners for path in changed):
36
+ return {"status": "BLOCKED", "reason": "CHANGED_PATH_OWNERSHIP_REQUIRED", "steps": []}
37
+ affected = {owners[path] for path in changed}
38
+ if not changed:
39
+ affected = {item["component"]["id"] for item in report["items"]
40
+ if item["actionability"] != "AUTO_RESOLVED"}
41
+ links = {(item["from"], item["to"]) for item in payload.get("relationships", [])
42
+ if item.get("type") == "VERIFIES"}
43
+ selected = {key for key, meta in components.items() if "VERIFICATION" in meta.get("roles", [])
44
+ and (key in affected or any((key, target) in links for target in affected)
45
+ or any(selector_matches_path(path, pattern) for path in changed
46
+ for pattern in meta.get("analysis_inputs", [])))}
47
+ tests = sorted(path for path, owner in owners.items() if owner in selected
48
+ and Path(path).name.startswith("test_") and path.endswith(".py"))
49
+ runner_evidence = []
50
+ for item in report["items"]:
51
+ edge = item["dependency"]
52
+ if edge["source"] in tests and edge["target"] == "pytest":
53
+ runner_evidence.append(item["evidence_id"])
54
+ try:
55
+ project = tomllib.loads((root / "pyproject.toml").read_text(encoding="utf-8-sig")).get("project", {})
56
+ if not isinstance(project, dict):
57
+ raise ValueError("Project metadata must be a table")
58
+ requirements = project.get("dependencies", [])
59
+ optional = project.get("optional-dependencies", {})
60
+ if not isinstance(requirements, list) or not isinstance(optional, dict):
61
+ raise ValueError("Dependency metadata has an invalid shape")
62
+ requirements = requirements + [value for group in optional.values() if isinstance(group, list) for value in group]
63
+ if any(isinstance(value, str) and re.match(r"^pytest(?:\s|[<>=!~\[;@]|$)", value.strip())
64
+ for value in requirements):
65
+ runner_evidence.append("manifest:pyproject.toml:pytest")
66
+ except (OSError, ValueError, TypeError):
67
+ pass
68
+ if not tests or not runner_evidence:
69
+ return {"status": "REVIEW_REQUIRED", "reason": "VERIFICATION_EXECUTION_EVIDENCE_REQUIRED",
70
+ "verification_components": sorted(selected), "steps": [], "authority": "ADVISORY_ONLY"}
71
+ graph = {}
72
+ for item in report["items"]:
73
+ edge = item["dependency"]
74
+ if edge.get("resolved_path"):
75
+ graph.setdefault(edge["source"], set()).add(edge["resolved_path"])
76
+ def reaches_change(path):
77
+ visited, pending = set(), [path]
78
+ while pending:
79
+ current = pending.pop()
80
+ if current in changed:
81
+ return True
82
+ if current not in visited:
83
+ visited.add(current)
84
+ pending.extend(graph.get(current, ()))
85
+ return False
86
+ focused = [path for path in tests if reaches_change(path)]
87
+ steps = []
88
+ if focused:
89
+ steps.append({"scope": "focused", "command": ["python", "-m", "pytest", *focused, "-q"],
90
+ "run_when": "focused_change", "basis": "OBSERVED_IMPORT_REACHABILITY_OR_CHANGED_TEST"})
91
+ # Only collapse to a directory if every tracked test in it belongs to the
92
+ # selected verification components; never broaden to unrelated tests.
93
+ parents = sorted({str(Path(path).parent).replace("\\", "/") for path in tests})
94
+ targets = []
95
+ for parent in parents:
96
+ peers = [path for path in owners if path.startswith(parent + "/")
97
+ and Path(path).name.startswith("test_") and path.endswith(".py")]
98
+ targets.extend([parent] if all(path in tests for path in peers)
99
+ else [path for path in tests if str(Path(path).parent).replace("\\", "/") == parent])
100
+ steps.append({"scope": "component", "command": ["python", "-m", "pytest", *targets, "-q", "--maxfail=10"],
101
+ "run_when": "batch_complete", "basis": "DECLARED_VERIFICATION_OWNERSHIP"})
102
+ return {"format": "ptsip-dependency-validation-plan/v1", "status": "PROPOSED",
103
+ "changed_paths": changed, "verification_components": sorted(selected),
104
+ "runner_evidence": sorted(runner_evidence), "steps": steps,
105
+ "authority": "ADVISORY_ONLY", "executed": False,
106
+ "checkpoint": "Run the repository-declared full regression and strict conform at a WU checkpoint"}
ptsip/doctor.py ADDED
@@ -0,0 +1,33 @@
1
+ from __future__ import annotations
2
+
3
+ import shutil
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ from .storage.local_state import ptsip_home
8
+
9
+
10
+ def doctor(path: str | Path = ".") -> dict[str, object]:
11
+ target = Path(path).expanduser().resolve()
12
+ current_minor = (sys.version_info.major, sys.version_info.minor)
13
+ metadata_supported = current_minor >= (3, 11)
14
+ ci_verified = (3, 11) <= current_minor <= (3, 14)
15
+ github_cli = shutil.which("gh")
16
+ return {
17
+ "python": sys.version.split()[0],
18
+ "python_ok": metadata_supported,
19
+ "python_supported_by_metadata": metadata_supported,
20
+ "python_ci_verified": ci_verified,
21
+ "python_ci_verified_range": "3.11-3.14",
22
+ "python_support_note": (
23
+ "This interpreter is within the current CI-verified range."
24
+ if ci_verified
25
+ else "Package metadata allows this interpreter, but this minor version is outside the current CI matrix."
26
+ ),
27
+ "git": shutil.which("git"),
28
+ "github_cli": github_cli,
29
+ "github_issue_publish_available": github_cli is not None,
30
+ "target": str(target),
31
+ "target_exists": target.exists(),
32
+ "ptsip_home": str(ptsip_home()),
33
+ }
@@ -0,0 +1,191 @@
1
+ """Read-only consumer diagnostics for the seven independently packaged engines.
2
+
3
+ Installation is delegated to the authorized package-manager transaction.
4
+ Presence or self-reported wheel provenance never grants runtime authority.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import hashlib
9
+ import json
10
+ import os
11
+ import platform
12
+ import re
13
+ import sys
14
+ import sysconfig
15
+ from importlib import metadata, resources
16
+ from pathlib import Path
17
+ from typing import Any
18
+
19
+ HOST_TARGETS = {
20
+ ("win32", "amd64"): "x86_64-pc-windows-msvc",
21
+ ("win32", "x86_64"): "x86_64-pc-windows-msvc",
22
+ ("linux", "x86_64"): "x86_64-unknown-linux-gnu",
23
+ ("darwin", "arm64"): "aarch64-apple-darwin",
24
+ ("darwin", "aarch64"): "aarch64-apple-darwin",
25
+ }
26
+
27
+
28
+ def _load(relative: str) -> dict[str, Any]:
29
+ resource = resources.files("ptsip").joinpath("engines", *relative.split("/"))
30
+ return json.loads(resource.read_text(encoding="utf-8"))
31
+
32
+
33
+ def _sha256(path: Path) -> str:
34
+ digest = hashlib.sha256()
35
+ with path.open("rb") as stream:
36
+ for chunk in iter(lambda: stream.read(1024 * 1024), b""):
37
+ digest.update(chunk)
38
+ return digest.hexdigest()
39
+
40
+
41
+ def _bundle(items: list[dict[str, Any]]) -> tuple[Any, dict[str, Any]] | None:
42
+ """A broken installed bundle must not fall back to another distribution."""
43
+ try:
44
+ distribution = metadata.distribution("PTSIP")
45
+ except metadata.PackageNotFoundError:
46
+ return None
47
+ text = distribution.read_text("ptsip-engines.json")
48
+ if text is None:
49
+ return None
50
+ try:
51
+ bundle = json.loads(text)
52
+ origins = bundle["engines"]
53
+ if (
54
+ bundle["schema_version"] != "ptsip-bundled-engine-origins/v1"
55
+ or bundle["package_version"] != distribution.version
56
+ or bundle["execution_authorized"] is not False
57
+ or set(origins) != {entry["kind"].lower() for entry in items}
58
+ or len(origins) != len(items)
59
+ ):
60
+ raise ValueError("incomplete or incompatible PTSIP engine bundle")
61
+ for entry in items:
62
+ origin = origins[entry["kind"].lower()]
63
+ if (
64
+ origin["schema_version"] != "ptsip-independent-wheel-origin/v1"
65
+ or origin["engine_id"] != entry["engine_id"]
66
+ or origin["engine_version"] != entry["version"]
67
+ or origin["execution_authorized"] is not False
68
+ or re.fullmatch(r"[0-9a-f]{40}", origin["source_sha"]) is None
69
+ ):
70
+ raise ValueError("PTSIP bundled engine identity differs")
71
+ return distribution, bundle
72
+ except (KeyError, TypeError, json.JSONDecodeError) as exc:
73
+ raise ValueError("PTSIP bundled engine metadata is invalid") from exc
74
+
75
+
76
+ def inspect(
77
+ *,
78
+ index: dict[str, Any] | None = None,
79
+ plan: dict[str, Any] | None = None,
80
+ bindings: dict[str, dict[str, Any]] | None = None,
81
+ scripts: Path | None = None,
82
+ host_target: str | None = None,
83
+ ) -> dict[str, Any]:
84
+ index = index if index is not None else _load("index.json")
85
+ plan = plan if plan is not None else _load("contracts/installation-plan.json")
86
+ items = index["official_engine_entries"]
87
+ packages = {p["engine_id"]: p for p in plan["packages"]}
88
+ if (
89
+ len(items) != 7 or len(packages) != 7
90
+ or len({i["engine_id"] for i in items}) != 7
91
+ or set(packages) != {i["engine_id"] for i in items}
92
+ or set(plan["default_available_engine_ids"]) != set(packages)
93
+ ):
94
+ raise ValueError("official engine index or installation registry incomplete/ambiguous")
95
+ host_target = host_target or HOST_TARGETS.get(
96
+ (sys.platform, platform.machine().lower())
97
+ )
98
+ scripts = scripts or Path(sysconfig.get_path("scripts"))
99
+ bundled = _bundle(items)
100
+ entries: list[dict[str, Any]] = []
101
+ for entry in items:
102
+ kind, engine_id, version = entry["kind"], entry["engine_id"], entry["version"]
103
+ pkg = packages[engine_id]
104
+ package_name = "ptsip-engine-" + kind.lower()
105
+ if (
106
+ pkg["distribution"] != "INDEPENDENT_VERSIONED_NATIVE_WHEEL"
107
+ or pkg["package_distribution"] != package_name
108
+ or pkg["installation"] != "AUTO_INSTALL_AS_PTSIP_PACKAGE_DEPENDENCY"
109
+ or pkg["version"] != version
110
+ or pkg["permission_from_availability"] is not False
111
+ ):
112
+ raise ValueError("outdated or incompatible official engine installation contract")
113
+ b = bindings[kind] if bindings is not None else _load(
114
+ f"{kind.lower()}/execution-bindings.json"
115
+ )
116
+ candidates = b["bindings"]
117
+ if len(candidates) != 1:
118
+ raise ValueError("ambiguous registered engine executable")
119
+ binding = candidates[0]
120
+ if binding["entrypoint"] != package_name or binding["engine_id"] != engine_id or binding["engine_version"] != version:
121
+ raise ValueError("registered engine entrypoint mismatch")
122
+ row: dict[str, Any] = {
123
+ "engine": kind, "engine_id": engine_id, "expected_version": version,
124
+ "package": package_name, "host_target": host_target,
125
+ "status": "NOT_INSTALLED", "execution_authorized": False,
126
+ }
127
+ if host_target not in pkg["build_targets"]:
128
+ row["status"] = "UNSUPPORTED_HOST"
129
+ entries.append(row)
130
+ continue
131
+ if bundled is not None:
132
+ distribution, bundle = bundled
133
+ origin = bundle["engines"][kind.lower()]
134
+ row["package"] = "PTSIP"
135
+ row["package_version"] = distribution.version
136
+ row["installed_version"] = origin["engine_version"]
137
+ else:
138
+ try:
139
+ distribution = metadata.distribution(package_name)
140
+ except metadata.PackageNotFoundError:
141
+ entries.append(row)
142
+ continue
143
+ row["installed_version"] = distribution.version
144
+ if row["installed_version"] != version:
145
+ row["status"] = "VERSION_MISMATCH"
146
+ entries.append(row)
147
+ continue
148
+ extension = ".exe" if host_target and "windows" in host_target else ""
149
+ executable = scripts / (binding["entrypoint"] + extension)
150
+ if executable.is_symlink() or not executable.is_file():
151
+ row["status"] = "EXECUTABLE_MISSING_OR_UNTRUSTED_PATH"
152
+ entries.append(row)
153
+ continue
154
+ try:
155
+ if bundled is None:
156
+ origin_text = distribution.read_text("ptsip-origin.json")
157
+ origin = json.loads(origin_text or "")
158
+ if (
159
+ origin["engine_id"] != engine_id
160
+ or origin["engine_version"] != version
161
+ or origin["host_target"] != host_target
162
+ or origin["entrypoint"] != executable.name
163
+ or not isinstance(origin["source_sha"], str)
164
+ or len(origin["source_sha"]) != 40
165
+ or origin["binary_sha256"] != _sha256(executable)
166
+ ):
167
+ row["status"] = "ARTIFACT_IDENTITY_MISMATCH"
168
+ else:
169
+ row["status"] = "INSTALLED_UNATTESTED_RELEASE"
170
+ row["binary_sha256"] = origin["binary_sha256"]
171
+ row["source_sha"] = origin["source_sha"]
172
+ except (OSError, json.JSONDecodeError, KeyError, TypeError, ValueError):
173
+ row["status"] = "ORIGIN_PROOF_MISSING_OR_INVALID"
174
+ entries.append(row)
175
+ return {
176
+ "schema_version": "ptsip-official-engine-consumer-status/v1",
177
+ "host_target": host_target,
178
+ "distribution": "SINGLE_PROJECT_PLATFORM_BUNDLES" if bundled else "AUTO_INSTALLED_INDEPENDENT_PACKAGES_ONLY",
179
+ "entries": entries,
180
+ "all_installed": all(e["status"] == "INSTALLED_UNATTESTED_RELEASE" for e in entries),
181
+ "release_attested": False,
182
+ "execution_authorized": False,
183
+ "status": "AWAITING_OFFICIAL_RELEASE_ATTESTATION"
184
+ if all(e["status"] == "INSTALLED_UNATTESTED_RELEASE" for e in entries)
185
+ else "INCOMPLETE",
186
+ }
187
+
188
+
189
+ def emit_status() -> tuple[dict[str, Any], int]:
190
+ payload = inspect()
191
+ return payload, 0 if payload["all_installed"] else 4