forge-autonomy 2.7.35__tar.gz → 2.7.37__tar.gz

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 (337) hide show
  1. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/PKG-INFO +1 -1
  2. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/__main__.py +67 -0
  3. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/installed_lifecycle.py +13 -0
  4. forge_autonomy-2.7.37/forge/preserved_lifecycle.py +459 -0
  5. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/qualification/server_runtime.py +125 -0
  6. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge_autonomy.egg-info/PKG-INFO +1 -1
  7. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge_autonomy.egg-info/SOURCES.txt +2 -0
  8. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/product-version.json +1 -1
  9. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_installed_lifecycle.py +29 -0
  10. forge_autonomy-2.7.37/tests/test_preserved_lifecycle.py +239 -0
  11. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/MANIFEST.in +0 -0
  12. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/README.md +0 -0
  13. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/__init__.py +0 -0
  14. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/_version.py +0 -0
  15. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/action_derivation_canary_closure.py +0 -0
  16. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/action_derivation_evidence.py +0 -0
  17. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/action_derivation_qualification.py +0 -0
  18. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/action_derivation_reattempt.py +0 -0
  19. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/ai_architect/__init__.py +0 -0
  20. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/ai_architect/provider_registry.py +0 -0
  21. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/api/installed-health-component-registry-1.0.json +0 -0
  22. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/api/operations-read-openapi-v1.json +0 -0
  23. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/api/operations-read-postman-v1.json +0 -0
  24. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/api/server-openapi-v1.json +0 -0
  25. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/api/server-postman-v1.json +0 -0
  26. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/architecture/__init__.py +0 -0
  27. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/architecture/advisor.py +0 -0
  28. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/architecture/presentation.py +0 -0
  29. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/architecture/workspace.py +0 -0
  30. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/autonomous_orchestrator.py +0 -0
  31. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/business/__init__.py +0 -0
  32. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/business/advisor.py +0 -0
  33. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/business/ingress.py +0 -0
  34. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/business/presentation.py +0 -0
  35. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/business/workspace.py +0 -0
  36. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/canary.py +0 -0
  37. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/capabilities/__init__.py +0 -0
  38. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/capabilities/delegation.py +0 -0
  39. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/completion/__init__.py +0 -0
  40. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/completion/assessor.py +0 -0
  41. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/completion/host_control_observer.py +0 -0
  42. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/completion/mission.py +0 -0
  43. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/completion/repository_observer.py +0 -0
  44. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/constitutional/__init__.py +0 -0
  45. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/constitutional/assessor.py +0 -0
  46. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/core/__init__.py +0 -0
  47. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/core/json_store.py +0 -0
  48. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/decision_evidence/__init__.py +0 -0
  49. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/decision_evidence/repository.py +0 -0
  50. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/dispatcher/__init__.py +0 -0
  51. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/dispatcher/dispatcher.py +0 -0
  52. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/ep_simulator.py +0 -0
  53. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/execution/__init__.py +0 -0
  54. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/execution/loop.py +0 -0
  55. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/execution/presentation.py +0 -0
  56. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/execution_host_configuration.py +0 -0
  57. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/foundation/__init__.py +0 -0
  58. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/foundation/loader.py +0 -0
  59. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/generation_two.py +0 -0
  60. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/generation_two_execution.py +0 -0
  61. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/governance/__init__.py +0 -0
  62. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/governance/execution_policy.py +0 -0
  63. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/governance/profiles.py +0 -0
  64. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/governance_authority.py +0 -0
  65. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/installed_health.py +0 -0
  66. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/intake.py +0 -0
  67. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/integration/__init__.py +0 -0
  68. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/integration/coordinator.py +0 -0
  69. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/integration/repository.py +0 -0
  70. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/knowledge/__init__.py +0 -0
  71. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/knowledge/consumer.py +0 -0
  72. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/knowledge/registry.py +0 -0
  73. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/lifecycle/__init__.py +0 -0
  74. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/lifecycle/store.py +0 -0
  75. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/mission_amendment.py +0 -0
  76. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/mission_cli.py +0 -0
  77. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/mission_scheduler.py +0 -0
  78. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/__init__.py +0 -0
  79. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/action.py +0 -0
  80. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/action_derivation.py +0 -0
  81. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/agent_policy.py +0 -0
  82. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/ai_architect_provider.py +0 -0
  83. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/ai_architect_session.py +0 -0
  84. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/ai_provider_registry.py +0 -0
  85. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/architecture_mission.py +0 -0
  86. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/architecture_reasoning.py +0 -0
  87. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/architecture_review.py +0 -0
  88. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/codex_runtime_prompt.py +0 -0
  89. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/constitutional_validation.py +0 -0
  90. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/criterion_assessment.py +0 -0
  91. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/criterion_observation.py +0 -0
  92. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/decision_evidence.py +0 -0
  93. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/execution_host.py +0 -0
  94. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/foundation.py +0 -0
  95. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/historical_intent.py +0 -0
  96. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/integration.py +0 -0
  97. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/intent.py +0 -0
  98. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/intent_authoring.py +0 -0
  99. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/mission.py +0 -0
  100. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/mission_candidate.py +0 -0
  101. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/mission_completion.py +0 -0
  102. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/mission_planner.py +0 -0
  103. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/mission_recommendation.py +0 -0
  104. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/phase_completion.py +0 -0
  105. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/planning.py +0 -0
  106. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/producer.py +0 -0
  107. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/prompt_artifact.py +0 -0
  108. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/proposal.py +0 -0
  109. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/runtime_prompt.py +0 -0
  110. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/models/solution_template.py +0 -0
  111. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/operations_read_api.py +0 -0
  112. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/operator_identity.py +0 -0
  113. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/planner/__init__.py +0 -0
  114. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/planner/action_derivation.py +0 -0
  115. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/planner/codex_cli_session.py +0 -0
  116. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/planner/durable_derivation.py +0 -0
  117. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/planner/engine.py +0 -0
  118. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/planner/openai_responses.py +0 -0
  119. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/planner/provider_adapter.py +0 -0
  120. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/planning/__init__.py +0 -0
  121. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/planning/loader.py +0 -0
  122. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/planning/registry.py +0 -0
  123. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/portfolio_intelligence.py +0 -0
  124. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/programme_authorization.py +0 -0
  125. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/prompts/__init__.py +0 -0
  126. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/prompts/codex_cli.py +0 -0
  127. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/prompts/generator.py +0 -0
  128. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/proposals/__init__.py +0 -0
  129. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/proposals/generator.py +0 -0
  130. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/provider_context.py +0 -0
  131. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/provider_security.py +0 -0
  132. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/qualification/__init__.py +0 -0
  133. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/qualification/bootstrap_sequence.py +0 -0
  134. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/qualification/criterion_completion.py +0 -0
  135. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/qualification/generation_one.py +0 -0
  136. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/qualification/installed_smoke.py +0 -0
  137. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/recommendations/__init__.py +0 -0
  138. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/recommendations/engine.py +0 -0
  139. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/repository_truth.py +0 -0
  140. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/review/__init__.py +0 -0
  141. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/review/engine.py +0 -0
  142. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/runtime/__init__.py +0 -0
  143. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/runtime/bootstrap.py +0 -0
  144. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/runtime/data_root.py +0 -0
  145. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/runtime/database.py +0 -0
  146. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/runtime/dynamic_mission.py +0 -0
  147. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/runtime/evidence.py +0 -0
  148. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/runtime/execution_context.py +0 -0
  149. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/runtime/health.py +0 -0
  150. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/runtime/mission_controller.py +0 -0
  151. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/runtime/operational_reset.py +0 -0
  152. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/runtime/runner.py +0 -0
  153. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/runtime/service.py +0 -0
  154. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/scheduler/__init__.py +0 -0
  155. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/scheduler/adapter.py +0 -0
  156. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/scheduler/ep_http_adapter.py +0 -0
  157. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/scheduler/ep_v12.py +0 -0
  158. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/scheduler/scheduler.py +0 -0
  159. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/capability-registry-1.0.schema.json +0 -0
  160. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/capability.schema.json +0 -0
  161. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/common.schema.json +0 -0
  162. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/decision-evidence-1.0.schema.json +0 -0
  163. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/engineering-goal-0.5.schema.json +0 -0
  164. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/engineering-increment-proposal-0.5.schema.json +0 -0
  165. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/engineering-mode.schema.json +0 -0
  166. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/engineering-plan-0.5.schema.json +0 -0
  167. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/engineering-platform-peer-configuration-1.0.schema.json +0 -0
  168. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/engineering-platform-peer-configuration-1.1.schema.json +0 -0
  169. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/engineering-prompt-artifact-0.7.schema.json +0 -0
  170. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/engineering-proposal-0.6.schema.json +0 -0
  171. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/evidence-reference-0.5.schema.json +0 -0
  172. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/foundation-document.schema.json +0 -0
  173. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/governance-profile.schema.json +0 -0
  174. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/knowledge-source-0.4.schema.json +0 -0
  175. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/knowledge-source.schema.json +0 -0
  176. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/mission-candidate-1.0.schema.json +0 -0
  177. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/mission-candidate-1.1.schema.json +0 -0
  178. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/phase-completion-1.0.schema.json +0 -0
  179. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/planning-document-0.5.schema.json +0 -0
  180. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/repository-catalog.schema.json +0 -0
  181. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/repository.schema.json +0 -0
  182. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/solution-template-1.0.schema.json +0 -0
  183. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/schemas/workspace.schema.json +0 -0
  184. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/secure_store.py +0 -0
  185. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/server_runtime.py +0 -0
  186. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/solutions/__init__.py +0 -0
  187. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/solutions/catalogue.py +0 -0
  188. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/solutions/generator.py +0 -0
  189. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/state/__init__.py +0 -0
  190. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/state/mission_state.py +0 -0
  191. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge/workspace/__init__.py +0 -0
  192. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge_autonomy.egg-info/dependency_links.txt +0 -0
  193. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge_autonomy.egg-info/entry_points.txt +0 -0
  194. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/forge_autonomy.egg-info/top_level.txt +0 -0
  195. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/pyproject.toml +0 -0
  196. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/capability-registry-1.0.schema.json +0 -0
  197. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/capability.schema.json +0 -0
  198. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/common.schema.json +0 -0
  199. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/decision-evidence-1.0.schema.json +0 -0
  200. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/engineering-goal-0.5.schema.json +0 -0
  201. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/engineering-increment-proposal-0.5.schema.json +0 -0
  202. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/engineering-mode.schema.json +0 -0
  203. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/engineering-plan-0.5.schema.json +0 -0
  204. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/engineering-prompt-artifact-0.7.schema.json +0 -0
  205. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/engineering-proposal-0.6.schema.json +0 -0
  206. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/evidence-reference-0.5.schema.json +0 -0
  207. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/foundation-document.schema.json +0 -0
  208. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/governance-profile.schema.json +0 -0
  209. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/knowledge-source-0.4.schema.json +0 -0
  210. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/knowledge-source.schema.json +0 -0
  211. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/mission-candidate-1.0.schema.json +0 -0
  212. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/mission-candidate-1.1.schema.json +0 -0
  213. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/phase-completion-1.0.schema.json +0 -0
  214. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/planning-document-0.5.schema.json +0 -0
  215. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/repository-catalog.schema.json +0 -0
  216. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/repository.schema.json +0 -0
  217. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/solution-template-1.0.schema.json +0 -0
  218. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/schemas/workspace.schema.json +0 -0
  219. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/setup.cfg +0 -0
  220. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_action_derivation.py +0 -0
  221. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_action_derivation_canary_closure.py +0 -0
  222. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_action_derivation_evidence.py +0 -0
  223. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_action_derivation_qualification.py +0 -0
  224. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_adaptive_action_sizing_design.py +0 -0
  225. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_agent_policy.py +0 -0
  226. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_aggregate_health_roadmap.py +0 -0
  227. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_ai_architect_provider.py +0 -0
  228. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_ai_architect_session.py +0 -0
  229. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_ai_development_projection.py +0 -0
  230. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_ai_provider_registry.py +0 -0
  231. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_architecture_reasoning.py +0 -0
  232. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_architecture_review_engine.py +0 -0
  233. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_architecture_workspace.py +0 -0
  234. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_autonomous_orchestrator.py +0 -0
  235. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_bootstrap_adapter.py +0 -0
  236. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_bootstrap_mission_canary.py +0 -0
  237. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_bootstrap_mission_runner.py +0 -0
  238. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_bootstrap_mission_scheduler.py +0 -0
  239. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_bootstrap_sequence_qualification.py +0 -0
  240. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_business_governance_ingress.py +0 -0
  241. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_business_workspace.py +0 -0
  242. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_canonical_data_root.py +0 -0
  243. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_capability_delegation.py +0 -0
  244. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_codex_cli_runtime_prompt.py +0 -0
  245. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_codex_cli_session.py +0 -0
  246. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_constitutional_validation.py +0 -0
  247. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_criterion_assessment_contract.py +0 -0
  248. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_criterion_ep_http_boundary.py +0 -0
  249. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_criterion_planning_context.py +0 -0
  250. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_decision_evidence.py +0 -0
  251. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_default_project_quality_policy.py +0 -0
  252. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_distribution_identity.py +0 -0
  253. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_durable_action_derivation.py +0 -0
  254. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_dynamic_mission_capability.py +0 -0
  255. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_engineering_action_architecture.py +0 -0
  256. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_engineering_intent_authoring.py +0 -0
  257. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_engineering_intent_lifecycle.py +0 -0
  258. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_engineering_mission.py +0 -0
  259. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_engineering_planning.py +0 -0
  260. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_engineering_prompt_artifacts.py +0 -0
  261. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_engineering_proposals.py +0 -0
  262. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_ep_http_adapter.py +0 -0
  263. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_ep_simulator.py +0 -0
  264. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_ep_simulator_rolling_planning.py +0 -0
  265. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_execution_context.py +0 -0
  266. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_execution_host_configuration.py +0 -0
  267. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_execution_host_contract.py +0 -0
  268. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_execution_loop.py +0 -0
  269. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_existing_release_reconciliation.py +0 -0
  270. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_forge_console_admin_parity_contract.py +0 -0
  271. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_forge_console_configuration_contract.py +0 -0
  272. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_forge_console_hosting_contract.py +0 -0
  273. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_forge_operations_console_roadmap.py +0 -0
  274. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_forge_outer_loop_ci_roadmap.py +0 -0
  275. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_forge_v1_bootstrap_scheduler_contract.py +0 -0
  276. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_forge_v1_implementation_dag.py +0 -0
  277. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_foundation_loader.py +0 -0
  278. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_foundation_models.py +0 -0
  279. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_generation_one_bootstrap_qualification.py +0 -0
  280. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_generation_two_execution.py +0 -0
  281. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_generation_two_mission_intake.py +0 -0
  282. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_governance_authority.py +0 -0
  283. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_governed_coverage.py +0 -0
  284. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_historical_engineering_intent.py +0 -0
  285. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_host_control_completion.py +0 -0
  286. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_inner_loop_ci_contract.py +0 -0
  287. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_installed_dynamic_mission_runtime.py +0 -0
  288. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_installed_forge_update.py +0 -0
  289. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_integration_coordination.py +0 -0
  290. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_knowledge_consumption.py +0 -0
  291. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_live_project_roadmap_contract.py +0 -0
  292. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_mission_completion_evaluator.py +0 -0
  293. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_mission_controller.py +0 -0
  294. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_mission_dispatcher.py +0 -0
  295. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_mission_planner.py +0 -0
  296. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_mission_recommendation_engine.py +0 -0
  297. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_mission_recommendation_lifecycle.py +0 -0
  298. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_mission_runtime_projection.py +0 -0
  299. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_mission_runtime_scheduler.py +0 -0
  300. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_mission_state_store.py +0 -0
  301. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_mission_status.py +0 -0
  302. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_openai_action_derivation.py +0 -0
  303. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_operational_reset.py +0 -0
  304. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_operations_health.py +0 -0
  305. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_operations_read_api.py +0 -0
  306. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_operator_identity.py +0 -0
  307. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_phase_completion.py +0 -0
  308. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_platform_contract_documents.py +0 -0
  309. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_portfolio_intelligence_operation.py +0 -0
  310. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_producer_contract.py +0 -0
  311. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_product_version_operations.py +0 -0
  312. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_production_release_workflow.py +0 -0
  313. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_productization_reconciliation.py +0 -0
  314. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_programme_authorization.py +0 -0
  315. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_provider_context.py +0 -0
  316. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_provider_security.py +0 -0
  317. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_pypi_distribution_readback.py +0 -0
  318. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_recovered_partial_completion.py +0 -0
  319. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_release_operation.py +0 -0
  320. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_repository_truth.py +0 -0
  321. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_role_aware_conversations_contract.py +0 -0
  322. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_runtime_bootstrap.py +0 -0
  323. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_runtime_database.py +0 -0
  324. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_runtime_evidence.py +0 -0
  325. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_runtime_health.py +0 -0
  326. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_runtime_instance_persistence.py +0 -0
  327. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_runtime_prompt_generation.py +0 -0
  328. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_runtime_service.py +0 -0
  329. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_runtime_status_projection.py +0 -0
  330. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_scheduler_dependency_boundary.py +0 -0
  331. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_schemas.py +0 -0
  332. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_secure_store.py +0 -0
  333. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_server_api_contract.py +0 -0
  334. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_server_runtime.py +0 -0
  335. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_solution_templates.py +0 -0
  336. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_substantive_mission_completion.py +0 -0
  337. {forge_autonomy-2.7.35 → forge_autonomy-2.7.37}/tests/test_terminal_continuation_replay.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forge-autonomy
3
- Version: 2.7.35
3
+ Version: 2.7.37
4
4
  Summary: Forge mission planning, producer contracts, and evidence interpretation runtime.
5
5
  Author: Forge maintainers
6
6
  License-Expression: Apache-2.0
@@ -141,6 +141,28 @@ def main(argv: list[str] | None = None) -> int:
141
141
  uninstall_status.add_argument("--operation-id", required=True)
142
142
  uninstall_status.add_argument("--instances-root", required=True)
143
143
  uninstall_status.add_argument("--instance-id", required=True)
144
+ for lifecycle_name, lifecycle_help in (
145
+ ("preserve", "preserve one exact quiescent Forge instance data root"),
146
+ ("purge", "permanently purge one exact Forge instance"),
147
+ ("restore", "validate one preserved Forge instance for same-identity restore"),
148
+ ):
149
+ lifecycle = server_commands.add_parser(lifecycle_name, help=lifecycle_help)
150
+ lifecycle.add_argument("--operation-id", required=True)
151
+ lifecycle.add_argument("--instances-root", required=True)
152
+ lifecycle.add_argument("--instance-id", required=True)
153
+ lifecycle.add_argument("--runtime-id", required=True)
154
+ lifecycle.add_argument("--installation-id", required=True)
155
+ lifecycle.add_argument("--installed-version", required=True)
156
+ lifecycle.add_argument("--installed-source", required=True)
157
+ lifecycle.add_argument("--installed-artifact-digest", required=True)
158
+ if lifecycle_name == "restore":
159
+ lifecycle.add_argument("--preserve-operation-id", required=True)
160
+ lifecycle_status_parser = server_commands.add_parser(
161
+ "lifecycle-status", help="read one preserve/purge/restore lifecycle operation"
162
+ )
163
+ lifecycle_status_parser.add_argument("--operation-id", required=True)
164
+ lifecycle_status_parser.add_argument("--instances-root", required=True)
165
+ lifecycle_status_parser.add_argument("--instance-id", required=True)
144
166
  reset = server_commands.add_parser("reset", help="operate the Forge-owned operational-history reset")
145
167
  reset_commands = reset.add_subparsers(dest="reset_command", required=True)
146
168
  reset_commands.add_parser("preview", help="inspect a read-only reset plan")
@@ -316,6 +338,51 @@ def main(argv: list[str] | None = None) -> int:
316
338
  return 0
317
339
  except (InstalledLifecycleError, OSError, PermissionError, ValueError) as error:
318
340
  return _failure("server uninstall-status", error)
341
+ if args.command == "server" and args.server_command in {"preserve", "purge", "restore"}:
342
+ if args.data_root is None:
343
+ return _failure(
344
+ "server " + args.server_command,
345
+ ValueError("--data-root is required for instance lifecycle mutation"),
346
+ )
347
+ try:
348
+ from .preserved_lifecycle import (
349
+ InstalledPreserveDispatcher,
350
+ InstalledPurgeDispatcher,
351
+ InstalledRestoreDispatcher,
352
+ InstanceLifecycleRequest,
353
+ RestoreRequest,
354
+ )
355
+ common = {
356
+ "operation_id": args.operation_id,
357
+ "instance_id": args.instance_id,
358
+ "runtime_id": args.runtime_id,
359
+ "installation_id": args.installation_id,
360
+ "installed_version": args.installed_version,
361
+ "installed_source": args.installed_source,
362
+ "installed_artifact_digest": args.installed_artifact_digest,
363
+ "data_root": args.data_root,
364
+ "instances_root": args.instances_root,
365
+ }
366
+ if args.server_command == "preserve":
367
+ result = InstalledPreserveDispatcher(InstanceLifecycleRequest(**common)).run()
368
+ elif args.server_command == "purge":
369
+ result = InstalledPurgeDispatcher(InstanceLifecycleRequest(**common)).run()
370
+ else:
371
+ result = InstalledRestoreDispatcher(RestoreRequest(
372
+ **common, preserve_operation_id=args.preserve_operation_id,
373
+ )).run()
374
+ print(json.dumps(result, sort_keys=True))
375
+ return 0
376
+ except (InstalledLifecycleError, OSError, sqlite3.Error, PermissionError, ValueError) as error:
377
+ return _failure("server " + args.server_command, error)
378
+ if args.command == "server" and args.server_command == "lifecycle-status":
379
+ try:
380
+ from .preserved_lifecycle import InstalledLifecycleError, lifecycle_status
381
+ result = lifecycle_status(args.instances_root, args.instance_id, args.operation_id)
382
+ print(json.dumps(result, sort_keys=True))
383
+ return 0
384
+ except (InstalledLifecycleError, OSError, PermissionError, ValueError) as error:
385
+ return _failure("server lifecycle-status", error)
319
386
  if args.command == "health":
320
387
  if args.data_root is None:
321
388
  return _failure("health snapshot", ValueError("--data-root is required for health snapshot"))
@@ -498,6 +498,13 @@ def _exclusive_locks(paths: tuple[Path, ...]) -> Iterator[None]:
498
498
 
499
499
 
500
500
  def _assert_tree_has_no_links(root: Path) -> str:
501
+ """Prove one mutable instance tree is exclusively product-owned and safe."""
502
+ _assert_no_symlink_components(root)
503
+ root_info = root.lstat()
504
+ if not stat.S_ISDIR(root_info.st_mode):
505
+ raise InstalledLifecycleError("instance root is not a directory")
506
+ if stat.S_IMODE(root_info.st_mode) & 0o022:
507
+ raise InstalledLifecycleError("instance root is group/world writable")
501
508
  entries: list[dict[str, object]] = []
502
509
  for directory, names, files in os.walk(root, topdown=True, followlinks=False):
503
510
  parent = Path(directory)
@@ -508,8 +515,14 @@ def _assert_tree_has_no_links(root: Path) -> str:
508
515
  raise InstalledLifecycleError(f"instance tree contains an unsafe symbolic link: {path}")
509
516
  if not (stat.S_ISDIR(info.st_mode) or stat.S_ISREG(info.st_mode)):
510
517
  raise InstalledLifecycleError(f"instance tree contains an unsupported filesystem entry: {path}")
518
+ if stat.S_IMODE(info.st_mode) & 0o022:
519
+ raise InstalledLifecycleError(f"instance tree contains a group/world writable entry: {path}")
520
+ if stat.S_ISREG(info.st_mode) and info.st_nlink != 1:
521
+ raise InstalledLifecycleError(f"instance tree contains a non-exclusive hardlinked file: {path}")
511
522
  entries.append({
512
523
  "path": str(path.relative_to(root)), "mode": stat.S_IFMT(info.st_mode),
524
+ "permissions": stat.S_IMODE(info.st_mode),
525
+ "links": info.st_nlink if stat.S_ISREG(info.st_mode) else None,
513
526
  "size": info.st_size if stat.S_ISREG(info.st_mode) else None,
514
527
  })
515
528
  return _digest_bytes(_json_bytes(entries))
@@ -0,0 +1,459 @@
1
+ """Product-owned preserve/purge/restore semantics for Forge Server Runtime.
2
+
3
+ This contract extends, but never changes, the destructive
4
+ ``forge-server-runtime-lifecycle/v1`` uninstall boundary. Deployment-owned
5
+ service definitions and immutable runtime slots remain outside Forge's mutable
6
+ instance-data ownership.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import asdict, dataclass
11
+ from hashlib import sha256
12
+ import os
13
+ from pathlib import Path
14
+ import stat
15
+ from typing import Any, Callable, Mapping
16
+
17
+ from .installed_lifecycle import (
18
+ CONTROL_DIRECTORY,
19
+ InstalledLifecycleError,
20
+ InstalledUninstallDispatcher,
21
+ UninstallRequest,
22
+ _assert_identity,
23
+ _assert_no_symlink_components,
24
+ _assert_quiescent,
25
+ _assert_tree_has_no_links,
26
+ _atomic_json,
27
+ _digest_bytes,
28
+ _exclusive_locks,
29
+ _json_bytes,
30
+ _read_json,
31
+ _read_regular_bytes,
32
+ _runtime_snapshot,
33
+ _validate_identifier,
34
+ _DIGEST,
35
+ _SOURCE,
36
+ )
37
+
38
+
39
+ INSTANCE_LIFECYCLE_CONTRACT = "forge-server-instance-lifecycle/v1"
40
+ LIFECYCLE_DIRECTORY = "instance-lifecycle-v1"
41
+ PROVIDER_AUTH_PRESERVED = "PRESERVED_REQUIRES_REVERIFICATION"
42
+
43
+
44
+ def _content_tree_digest(root: Path) -> str:
45
+ """Bind every regular byte after shared exclusive-ownership admission."""
46
+ _assert_tree_has_no_links(root)
47
+ entries: list[dict[str, object]] = []
48
+ for directory, names, files in os.walk(root, topdown=True, followlinks=False):
49
+ parent = Path(directory)
50
+ for name in sorted(names):
51
+ path = parent / name
52
+ info = path.lstat()
53
+ if stat.S_ISLNK(info.st_mode) or not stat.S_ISDIR(info.st_mode):
54
+ raise InstalledLifecycleError(f"instance tree contains an unsafe directory entry: {path}")
55
+ entries.append({
56
+ "path": str(path.relative_to(root)),
57
+ "kind": "DIRECTORY",
58
+ "mode": stat.S_IMODE(info.st_mode),
59
+ })
60
+ for name in sorted(files):
61
+ path = parent / name
62
+ info = path.lstat()
63
+ if stat.S_ISLNK(info.st_mode) or not stat.S_ISREG(info.st_mode):
64
+ raise InstalledLifecycleError(f"instance tree contains an unsafe file entry: {path}")
65
+ payload = _read_regular_bytes(path)
66
+ entries.append({
67
+ "path": str(path.relative_to(root)),
68
+ "kind": "FILE",
69
+ "mode": stat.S_IMODE(info.st_mode),
70
+ "size": len(payload),
71
+ "digest": _digest_bytes(payload),
72
+ })
73
+ return _digest_bytes(_json_bytes(entries))
74
+
75
+
76
+ @dataclass(frozen=True)
77
+ class InstanceLifecycleRequest:
78
+ operation_id: str
79
+ instance_id: str
80
+ runtime_id: str
81
+ installation_id: str
82
+ installed_version: str
83
+ installed_source: str
84
+ installed_artifact_digest: str
85
+ data_root: str
86
+ instances_root: str
87
+
88
+ def validate(self) -> None:
89
+ for label, value in (
90
+ ("operation identity", self.operation_id),
91
+ ("instance identity", self.instance_id),
92
+ ("runtime identity", self.runtime_id),
93
+ ("installation identity", self.installation_id),
94
+ ):
95
+ _validate_identifier(label, value)
96
+ if self.instance_id != self.runtime_id:
97
+ raise InstalledLifecycleError("deployment instance does not bind the selected Forge runtime")
98
+ if _SOURCE.fullmatch(self.installed_source) is None or _DIGEST.fullmatch(self.installed_artifact_digest) is None:
99
+ raise InstalledLifecycleError("installed Forge artifact identity is invalid")
100
+ if not isinstance(self.installed_version, str) or not self.installed_version:
101
+ raise InstalledLifecycleError("installed Forge version is invalid")
102
+ data_root, instances_root = Path(self.data_root), Path(self.instances_root)
103
+ if not data_root.is_absolute() or not instances_root.is_absolute():
104
+ raise InstalledLifecycleError("lifecycle paths must be absolute")
105
+ _assert_no_symlink_components(instances_root)
106
+ _assert_no_symlink_components(data_root, allow_missing=True)
107
+ if data_root.parent != instances_root or data_root.name == CONTROL_DIRECTORY:
108
+ raise InstalledLifecycleError("data root is not one direct managed instance root")
109
+
110
+ @property
111
+ def digest(self) -> str:
112
+ return _digest_bytes(_json_bytes(asdict(self)))
113
+
114
+
115
+ @dataclass(frozen=True)
116
+ class RestoreRequest(InstanceLifecycleRequest):
117
+ preserve_operation_id: str
118
+
119
+ def validate(self) -> None:
120
+ super().validate()
121
+ _validate_identifier("preserve operation identity", self.preserve_operation_id)
122
+
123
+
124
+ class _LifecycleOperation:
125
+ operation: str
126
+
127
+ def __init__(
128
+ self,
129
+ request: InstanceLifecycleRequest,
130
+ *,
131
+ interrupt_after: str | None = None,
132
+ clock: Callable[[], str] | None = None,
133
+ ) -> None:
134
+ request.validate()
135
+ self.request = request
136
+ self.data_root = Path(request.data_root)
137
+ self.instances_root = Path(request.instances_root)
138
+ identity = sha256(request.instance_id.encode()).hexdigest()
139
+ self.control_root = self.instances_root / CONTROL_DIRECTORY / identity
140
+ self.lifecycle_root = self.control_root / LIFECYCLE_DIRECTORY
141
+ self.operation_root = self.lifecycle_root / "operations" / request.operation_id
142
+ self.state_path = self.operation_root / "state.json"
143
+ self.receipt_path = self.operation_root / "receipt.json"
144
+ self.purge_tombstone = self.lifecycle_root / "purged.json"
145
+ self.lifecycle_lock = self.control_root / "lifecycle.lock"
146
+ self.interrupt_after = interrupt_after
147
+ if clock is None:
148
+ from .installed_lifecycle import _now
149
+ clock = _now
150
+ self.clock = clock
151
+
152
+ def _interrupt(self, phase: str) -> None:
153
+ if self.interrupt_after == phase:
154
+ raise InterruptedError(f"interrupted after {phase}")
155
+
156
+ def _state(self) -> dict[str, Any]:
157
+ if self.state_path.exists():
158
+ state = _read_json(self.state_path)
159
+ if (
160
+ state.get("contract") != INSTANCE_LIFECYCLE_CONTRACT
161
+ or state.get("operation") != self.operation
162
+ or state.get("request") != asdict(self.request)
163
+ or state.get("request_digest") != self.request.digest
164
+ ):
165
+ raise InstalledLifecycleError("operation identity already belongs to another lifecycle request")
166
+ return state
167
+ state = {
168
+ "contract": INSTANCE_LIFECYCLE_CONTRACT,
169
+ "operation": self.operation,
170
+ "request": asdict(self.request),
171
+ "request_digest": self.request.digest,
172
+ "phase": "PREPARED",
173
+ "prepared_at": self.clock(),
174
+ }
175
+ _atomic_json(self.state_path, state)
176
+ return state
177
+
178
+ def _save(self, state: Mapping[str, Any], phase: str, **evidence: object) -> dict[str, Any]:
179
+ updated = {**state, **evidence, "phase": phase, "updated_at": self.clock()}
180
+ _atomic_json(self.state_path, updated)
181
+ return updated
182
+
183
+ def _write_receipt(self, state: Mapping[str, Any], payload: Mapping[str, object]) -> dict[str, Any]:
184
+ receipt = {
185
+ "contract": INSTANCE_LIFECYCLE_CONTRACT,
186
+ "operation": self.operation,
187
+ "operation_id": self.request.operation_id,
188
+ "instance_id": self.request.instance_id,
189
+ "runtime_id": self.request.runtime_id,
190
+ "installation_id": self.request.installation_id,
191
+ "request_digest": self.request.digest,
192
+ "selected_artifact": {
193
+ "version": self.request.installed_version,
194
+ "source_revision": self.request.installed_source,
195
+ "artifact_digest": self.request.installed_artifact_digest,
196
+ },
197
+ "state": "COMPLETE",
198
+ **payload,
199
+ "completed_at": self.clock(),
200
+ }
201
+ receipt["receipt_digest"] = _digest_bytes(_json_bytes(receipt))
202
+ if self.receipt_path.exists():
203
+ existing = _read_json(self.receipt_path)
204
+ unsigned = {key: value for key, value in existing.items() if key != "receipt_digest"}
205
+ if (
206
+ existing.get("contract") != INSTANCE_LIFECYCLE_CONTRACT
207
+ or existing.get("operation") != self.operation
208
+ or existing.get("request_digest") != self.request.digest
209
+ or existing.get("receipt_digest") != _digest_bytes(_json_bytes(unsigned))
210
+ ):
211
+ raise InstalledLifecycleError("terminal lifecycle receipt conflicts with existing evidence")
212
+ receipt = existing
213
+ else:
214
+ _atomic_json(self.receipt_path, receipt)
215
+ self._save(state, "COMPLETE", receipt_digest=receipt["receipt_digest"])
216
+ return receipt
217
+
218
+ def _terminal_receipt(self, state: Mapping[str, Any]) -> dict[str, Any]:
219
+ receipt = _read_json(self.receipt_path)
220
+ unsigned = {key: value for key, value in receipt.items() if key != "receipt_digest"}
221
+ if (
222
+ receipt.get("contract") != INSTANCE_LIFECYCLE_CONTRACT
223
+ or receipt.get("operation") != self.operation
224
+ or receipt.get("request_digest") != self.request.digest
225
+ or receipt.get("selected_artifact") != {
226
+ "version": self.request.installed_version,
227
+ "source_revision": self.request.installed_source,
228
+ "artifact_digest": self.request.installed_artifact_digest,
229
+ }
230
+ or receipt.get("state") != "COMPLETE"
231
+ or receipt.get("receipt_digest") != _digest_bytes(_json_bytes(unsigned))
232
+ or state.get("receipt_digest") != receipt.get("receipt_digest")
233
+ ):
234
+ raise InstalledLifecycleError("terminal lifecycle receipt is inconsistent")
235
+ return receipt
236
+
237
+ def _runtime_locks(self) -> tuple[Path, ...]:
238
+ return (
239
+ self.data_root / "locks" / "forge-server-runtime.lock",
240
+ self.data_root / "forge-mission-controller.lock",
241
+ self.data_root / "forge-runtime-mutation.lock",
242
+ self.data_root / "locks" / "runtime.lock",
243
+ )
244
+
245
+ def _verified_snapshot(self) -> tuple[dict[str, Any], str]:
246
+ if not self.data_root.is_dir():
247
+ raise InstalledLifecycleError("selected preserved instance data is unavailable")
248
+ with _exclusive_locks(self._runtime_locks()):
249
+ snapshot = _runtime_snapshot(self.data_root)
250
+ _assert_identity(
251
+ snapshot,
252
+ runtime_id=self.request.runtime_id,
253
+ installation_id=self.request.installation_id,
254
+ installed_version=self.request.installed_version,
255
+ )
256
+ _assert_quiescent(snapshot)
257
+ tree_digest = _content_tree_digest(self.data_root)
258
+ return snapshot, tree_digest
259
+
260
+
261
+ class InstalledPreserveDispatcher(_LifecycleOperation):
262
+ """Durably attest one quiescent instance as data-preserved."""
263
+
264
+ operation = "PRESERVE"
265
+
266
+ def run(self) -> dict[str, Any]:
267
+ self.lifecycle_root.mkdir(parents=True, exist_ok=True)
268
+ os.chmod(self.lifecycle_root, 0o700)
269
+ with _exclusive_locks((self.lifecycle_lock,)):
270
+ if self.purge_tombstone.exists():
271
+ raise InstalledLifecycleError("instance identity was permanently purged")
272
+ state = self._state()
273
+ if state.get("phase") == "COMPLETE":
274
+ receipt = self._terminal_receipt(state)
275
+ _snapshot, current = self._verified_snapshot()
276
+ if current != receipt.get("mutable_instance_data_digest"):
277
+ raise InstalledLifecycleError("preserved instance data changed after terminal preserve")
278
+ return receipt
279
+ snapshot, tree_digest = self._verified_snapshot()
280
+ state = self._save(
281
+ state,
282
+ "VERIFIED",
283
+ runtime_snapshot_digest=snapshot["digest"],
284
+ mutable_instance_data_digest=tree_digest,
285
+ )
286
+ self._interrupt("verified")
287
+ return self._write_receipt(state, {
288
+ "lifecycle_state": "UNINSTALLED_DATA_PRESERVED",
289
+ "instance_identity": "PRESERVED",
290
+ "mutable_instance_data": "PRESERVED",
291
+ "mutable_instance_data_digest": tree_digest,
292
+ "restorable": True,
293
+ "service_state": "REMOVED_OR_INACTIVE",
294
+ "service_definition": "DEPLOYMENT_OWNER",
295
+ "immutable_runtime_slots": "PRESERVED",
296
+ "provider_auth_state": PROVIDER_AUTH_PRESERVED,
297
+ })
298
+
299
+
300
+ class InstalledRestoreDispatcher(_LifecycleOperation):
301
+ """Validate preserved Forge data for same-identity deployment-owner restore."""
302
+
303
+ operation = "RESTORE"
304
+
305
+ @property
306
+ def restore_request(self) -> RestoreRequest:
307
+ if not isinstance(self.request, RestoreRequest):
308
+ raise InstalledLifecycleError("restore request type is invalid")
309
+ return self.request
310
+
311
+ def _preserve_receipt(self) -> dict[str, Any]:
312
+ preserve_root = self.lifecycle_root / "operations" / self.restore_request.preserve_operation_id
313
+ receipt = _read_json(preserve_root / "receipt.json")
314
+ unsigned = {key: value for key, value in receipt.items() if key != "receipt_digest"}
315
+ if (
316
+ receipt.get("contract") != INSTANCE_LIFECYCLE_CONTRACT
317
+ or receipt.get("operation") != "PRESERVE"
318
+ or receipt.get("instance_id") != self.request.instance_id
319
+ or receipt.get("runtime_id") != self.request.runtime_id
320
+ or receipt.get("installation_id") != self.request.installation_id
321
+ or receipt.get("selected_artifact") != {
322
+ "version": self.request.installed_version,
323
+ "source_revision": self.request.installed_source,
324
+ "artifact_digest": self.request.installed_artifact_digest,
325
+ }
326
+ or receipt.get("lifecycle_state") != "UNINSTALLED_DATA_PRESERVED"
327
+ or receipt.get("restorable") is not True
328
+ or receipt.get("receipt_digest") != _digest_bytes(_json_bytes(unsigned))
329
+ ):
330
+ raise InstalledLifecycleError("preserve evidence does not authorize same-identity restore")
331
+ return receipt
332
+
333
+ def run(self) -> dict[str, Any]:
334
+ self.lifecycle_root.mkdir(parents=True, exist_ok=True)
335
+ os.chmod(self.lifecycle_root, 0o700)
336
+ with _exclusive_locks((self.lifecycle_lock,)):
337
+ if self.purge_tombstone.exists():
338
+ raise InstalledLifecycleError("purged instance identity cannot be restored")
339
+ state = self._state()
340
+ if state.get("phase") == "COMPLETE":
341
+ return self._terminal_receipt(state)
342
+ preserve = self._preserve_receipt()
343
+ snapshot, tree_digest = self._verified_snapshot()
344
+ if tree_digest != preserve.get("mutable_instance_data_digest"):
345
+ raise InstalledLifecycleError("preserved instance data no longer matches restore evidence")
346
+ state = self._save(
347
+ state,
348
+ "VERIFIED",
349
+ runtime_snapshot_digest=snapshot["digest"],
350
+ preserve_operation_id=self.restore_request.preserve_operation_id,
351
+ preserve_receipt_digest=preserve["receipt_digest"],
352
+ mutable_instance_data_digest=tree_digest,
353
+ )
354
+ self._interrupt("verified")
355
+ return self._write_receipt(state, {
356
+ "lifecycle_state": "RESTORE_VALIDATED",
357
+ "instance_identity": "PRESERVED",
358
+ "mutable_instance_data": "PRESERVED",
359
+ "mutable_instance_data_digest": tree_digest,
360
+ "restored_from_preserve_operation": self.restore_request.preserve_operation_id,
361
+ "restored": True,
362
+ "service_state": "DEPLOYMENT_OWNER_REINSTALL_REQUIRED",
363
+ "service_definition": "DEPLOYMENT_OWNER",
364
+ "immutable_runtime_slots": "PRESERVED",
365
+ "provider_auth_state": PROVIDER_AUTH_PRESERVED,
366
+ "ready": False,
367
+ })
368
+
369
+
370
+ class InstalledPurgeDispatcher(_LifecycleOperation):
371
+ """Explicit permanent purge projection over the legacy destructive uninstall."""
372
+
373
+ operation = "PURGE"
374
+
375
+ def run(self) -> dict[str, Any]:
376
+ self.lifecycle_root.mkdir(parents=True, exist_ok=True)
377
+ os.chmod(self.lifecycle_root, 0o700)
378
+ with _exclusive_locks((self.lifecycle_lock,)):
379
+ state = self._state()
380
+ if state.get("phase") == "COMPLETE":
381
+ receipt = self._terminal_receipt(state)
382
+ if self.data_root.exists() or self.data_root.is_symlink():
383
+ raise InstalledLifecycleError("purged instance target was recreated")
384
+ return receipt
385
+
386
+ uninstall = InstalledUninstallDispatcher(UninstallRequest(
387
+ operation_id=self.request.operation_id,
388
+ instance_id=self.request.instance_id,
389
+ runtime_id=self.request.runtime_id,
390
+ installation_id=self.request.installation_id,
391
+ data_root=self.request.data_root,
392
+ instances_root=self.request.instances_root,
393
+ )).run()
394
+ self._interrupt("uninstalled")
395
+
396
+ with _exclusive_locks((self.lifecycle_lock,)):
397
+ state = self._state()
398
+ if self.data_root.exists() or self.data_root.is_symlink():
399
+ raise InstalledLifecycleError("destructive uninstall did not remove selected instance data")
400
+ tombstone = {
401
+ "contract": INSTANCE_LIFECYCLE_CONTRACT,
402
+ "instance_id": self.request.instance_id,
403
+ "lifecycle_state": "PURGED",
404
+ "restorable": False,
405
+ "purge_operation_id": self.request.operation_id,
406
+ "uninstall_receipt_digest": uninstall["receipt_digest"],
407
+ }
408
+ tombstone["tombstone_digest"] = _digest_bytes(_json_bytes(tombstone))
409
+ if self.purge_tombstone.exists():
410
+ if _read_json(self.purge_tombstone) != tombstone:
411
+ raise InstalledLifecycleError("instance purge tombstone conflicts with prior evidence")
412
+ else:
413
+ _atomic_json(self.purge_tombstone, tombstone)
414
+ return self._write_receipt(state, {
415
+ "lifecycle_state": "PURGED",
416
+ "instance_identity": "RETIRED",
417
+ "mutable_instance_data": "REMOVED",
418
+ "restorable": False,
419
+ "service_state": "DEPLOYMENT_OWNER_REMOVAL_REQUIRED_OR_COMPLETE",
420
+ "service_definition": "DEPLOYMENT_OWNER",
421
+ "immutable_runtime_slots": "PRESERVED",
422
+ "provider_auth_state": "REMOVED_WITH_INSTANCE_DATA",
423
+ "legacy_uninstall_receipt_digest": uninstall["receipt_digest"],
424
+ "purge_tombstone_digest": tombstone["tombstone_digest"],
425
+ })
426
+
427
+
428
+ def lifecycle_status(instances_root: str, instance_id: str, operation_id: str) -> dict[str, Any]:
429
+ """Read one exact product-owned preserve/purge/restore operation."""
430
+ _validate_identifier("instance identity", instance_id)
431
+ _validate_identifier("operation identity", operation_id)
432
+ root = Path(instances_root)
433
+ if not root.is_absolute():
434
+ raise InstalledLifecycleError("instances root must be absolute")
435
+ _assert_no_symlink_components(root)
436
+ identity = sha256(instance_id.encode()).hexdigest()
437
+ operation_root = root / CONTROL_DIRECTORY / identity / LIFECYCLE_DIRECTORY / "operations" / operation_id
438
+ state = _read_json(operation_root / "state.json")
439
+ result = {
440
+ "contract": state.get("contract"),
441
+ "operation": state.get("operation"),
442
+ "operation_id": operation_id,
443
+ "instance_id": instance_id,
444
+ "phase": state.get("phase"),
445
+ "request_digest": state.get("request_digest"),
446
+ "state": "IN_PROGRESS",
447
+ }
448
+ if state.get("phase") == "COMPLETE":
449
+ receipt = _read_json(operation_root / "receipt.json")
450
+ unsigned = {key: value for key, value in receipt.items() if key != "receipt_digest"}
451
+ if receipt.get("receipt_digest") != _digest_bytes(_json_bytes(unsigned)):
452
+ raise InstalledLifecycleError("terminal lifecycle receipt is inconsistent")
453
+ result.update({
454
+ "state": "COMPLETE",
455
+ "lifecycle_state": receipt.get("lifecycle_state"),
456
+ "receipt_digest": receipt.get("receipt_digest"),
457
+ "restorable": receipt.get("restorable"),
458
+ })
459
+ return result