forge-autonomy 2.7.22__tar.gz → 2.7.24__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 (299) hide show
  1. {forge_autonomy-2.7.22/forge_autonomy.egg-info → forge_autonomy-2.7.24}/PKG-INFO +12 -1
  2. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/README.md +11 -0
  3. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/__main__.py +2 -1
  4. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/runtime/operational_reset.py +108 -9
  5. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24/forge_autonomy.egg-info}/PKG-INFO +12 -1
  6. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge_autonomy.egg-info/SOURCES.txt +2 -0
  7. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/product-version.json +1 -1
  8. forge_autonomy-2.7.24/tests/test_existing_release_reconciliation.py +357 -0
  9. forge_autonomy-2.7.24/tests/test_installed_forge_update.py +694 -0
  10. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_operational_reset.py +150 -1
  11. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_production_release_workflow.py +111 -0
  12. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/MANIFEST.in +0 -0
  13. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/__init__.py +0 -0
  14. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/_version.py +0 -0
  15. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/action_derivation_canary_closure.py +0 -0
  16. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/action_derivation_evidence.py +0 -0
  17. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/action_derivation_qualification.py +0 -0
  18. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/action_derivation_reattempt.py +0 -0
  19. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/ai_architect/__init__.py +0 -0
  20. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/ai_architect/provider_registry.py +0 -0
  21. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/architecture/__init__.py +0 -0
  22. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/architecture/advisor.py +0 -0
  23. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/architecture/presentation.py +0 -0
  24. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/architecture/workspace.py +0 -0
  25. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/autonomous_orchestrator.py +0 -0
  26. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/business/__init__.py +0 -0
  27. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/business/advisor.py +0 -0
  28. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/business/ingress.py +0 -0
  29. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/business/presentation.py +0 -0
  30. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/business/workspace.py +0 -0
  31. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/canary.py +0 -0
  32. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/capabilities/__init__.py +0 -0
  33. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/capabilities/delegation.py +0 -0
  34. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/completion/__init__.py +0 -0
  35. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/completion/assessor.py +0 -0
  36. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/completion/mission.py +0 -0
  37. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/constitutional/__init__.py +0 -0
  38. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/constitutional/assessor.py +0 -0
  39. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/core/__init__.py +0 -0
  40. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/core/json_store.py +0 -0
  41. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/decision_evidence/__init__.py +0 -0
  42. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/decision_evidence/repository.py +0 -0
  43. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/dispatcher/__init__.py +0 -0
  44. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/dispatcher/dispatcher.py +0 -0
  45. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/execution/__init__.py +0 -0
  46. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/execution/loop.py +0 -0
  47. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/execution/presentation.py +0 -0
  48. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/execution_host_configuration.py +0 -0
  49. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/foundation/__init__.py +0 -0
  50. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/foundation/loader.py +0 -0
  51. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/generation_two.py +0 -0
  52. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/generation_two_execution.py +0 -0
  53. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/governance/__init__.py +0 -0
  54. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/governance/execution_policy.py +0 -0
  55. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/governance/profiles.py +0 -0
  56. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/governance_authority.py +0 -0
  57. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/intake.py +0 -0
  58. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/integration/__init__.py +0 -0
  59. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/integration/coordinator.py +0 -0
  60. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/integration/repository.py +0 -0
  61. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/knowledge/__init__.py +0 -0
  62. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/knowledge/consumer.py +0 -0
  63. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/knowledge/registry.py +0 -0
  64. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/lifecycle/__init__.py +0 -0
  65. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/lifecycle/store.py +0 -0
  66. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/mission_amendment.py +0 -0
  67. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/mission_scheduler.py +0 -0
  68. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/__init__.py +0 -0
  69. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/action.py +0 -0
  70. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/action_derivation.py +0 -0
  71. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/agent_policy.py +0 -0
  72. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/ai_architect_provider.py +0 -0
  73. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/ai_architect_session.py +0 -0
  74. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/ai_provider_registry.py +0 -0
  75. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/architecture_mission.py +0 -0
  76. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/architecture_reasoning.py +0 -0
  77. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/architecture_review.py +0 -0
  78. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/codex_runtime_prompt.py +0 -0
  79. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/constitutional_validation.py +0 -0
  80. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/decision_evidence.py +0 -0
  81. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/execution_host.py +0 -0
  82. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/foundation.py +0 -0
  83. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/historical_intent.py +0 -0
  84. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/integration.py +0 -0
  85. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/intent.py +0 -0
  86. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/intent_authoring.py +0 -0
  87. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/mission.py +0 -0
  88. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/mission_candidate.py +0 -0
  89. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/mission_completion.py +0 -0
  90. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/mission_planner.py +0 -0
  91. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/mission_recommendation.py +0 -0
  92. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/phase_completion.py +0 -0
  93. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/planning.py +0 -0
  94. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/producer.py +0 -0
  95. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/prompt_artifact.py +0 -0
  96. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/proposal.py +0 -0
  97. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/runtime_prompt.py +0 -0
  98. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/models/solution_template.py +0 -0
  99. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/operator_identity.py +0 -0
  100. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/planner/__init__.py +0 -0
  101. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/planner/action_derivation.py +0 -0
  102. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/planner/codex_cli_session.py +0 -0
  103. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/planner/durable_derivation.py +0 -0
  104. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/planner/engine.py +0 -0
  105. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/planner/openai_responses.py +0 -0
  106. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/planner/provider_adapter.py +0 -0
  107. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/planning/__init__.py +0 -0
  108. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/planning/loader.py +0 -0
  109. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/planning/registry.py +0 -0
  110. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/portfolio_intelligence.py +0 -0
  111. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/programme_authorization.py +0 -0
  112. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/prompts/__init__.py +0 -0
  113. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/prompts/codex_cli.py +0 -0
  114. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/prompts/generator.py +0 -0
  115. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/proposals/__init__.py +0 -0
  116. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/proposals/generator.py +0 -0
  117. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/provider_security.py +0 -0
  118. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/qualification/__init__.py +0 -0
  119. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/qualification/bootstrap_sequence.py +0 -0
  120. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/qualification/generation_one.py +0 -0
  121. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/qualification/installed_smoke.py +0 -0
  122. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/recommendations/__init__.py +0 -0
  123. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/recommendations/engine.py +0 -0
  124. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/repository_truth.py +0 -0
  125. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/review/__init__.py +0 -0
  126. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/review/engine.py +0 -0
  127. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/runtime/__init__.py +0 -0
  128. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/runtime/bootstrap.py +0 -0
  129. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/runtime/data_root.py +0 -0
  130. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/runtime/database.py +0 -0
  131. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/runtime/dynamic_mission.py +0 -0
  132. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/runtime/evidence.py +0 -0
  133. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/runtime/execution_context.py +0 -0
  134. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/runtime/health.py +0 -0
  135. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/runtime/runner.py +0 -0
  136. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/runtime/service.py +0 -0
  137. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/scheduler/__init__.py +0 -0
  138. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/scheduler/adapter.py +0 -0
  139. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/scheduler/ep_http_adapter.py +0 -0
  140. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/scheduler/ep_v12.py +0 -0
  141. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/scheduler/scheduler.py +0 -0
  142. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/capability-registry-1.0.schema.json +0 -0
  143. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/capability.schema.json +0 -0
  144. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/common.schema.json +0 -0
  145. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/decision-evidence-1.0.schema.json +0 -0
  146. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/engineering-goal-0.5.schema.json +0 -0
  147. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/engineering-increment-proposal-0.5.schema.json +0 -0
  148. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/engineering-mode.schema.json +0 -0
  149. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/engineering-plan-0.5.schema.json +0 -0
  150. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/engineering-platform-peer-configuration-1.0.schema.json +0 -0
  151. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/engineering-platform-peer-configuration-1.1.schema.json +0 -0
  152. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/engineering-prompt-artifact-0.7.schema.json +0 -0
  153. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/engineering-proposal-0.6.schema.json +0 -0
  154. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/evidence-reference-0.5.schema.json +0 -0
  155. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/foundation-document.schema.json +0 -0
  156. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/governance-profile.schema.json +0 -0
  157. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/knowledge-source-0.4.schema.json +0 -0
  158. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/knowledge-source.schema.json +0 -0
  159. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/mission-candidate-1.0.schema.json +0 -0
  160. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/mission-candidate-1.1.schema.json +0 -0
  161. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/phase-completion-1.0.schema.json +0 -0
  162. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/planning-document-0.5.schema.json +0 -0
  163. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/repository-catalog.schema.json +0 -0
  164. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/repository.schema.json +0 -0
  165. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/solution-template-1.0.schema.json +0 -0
  166. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/schemas/workspace.schema.json +0 -0
  167. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/secure_store.py +0 -0
  168. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/solutions/__init__.py +0 -0
  169. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/solutions/catalogue.py +0 -0
  170. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/solutions/generator.py +0 -0
  171. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/state/__init__.py +0 -0
  172. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/state/mission_state.py +0 -0
  173. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge/workspace/__init__.py +0 -0
  174. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge_autonomy.egg-info/dependency_links.txt +0 -0
  175. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge_autonomy.egg-info/entry_points.txt +0 -0
  176. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/forge_autonomy.egg-info/top_level.txt +0 -0
  177. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/pyproject.toml +0 -0
  178. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/capability-registry-1.0.schema.json +0 -0
  179. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/capability.schema.json +0 -0
  180. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/common.schema.json +0 -0
  181. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/decision-evidence-1.0.schema.json +0 -0
  182. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/engineering-goal-0.5.schema.json +0 -0
  183. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/engineering-increment-proposal-0.5.schema.json +0 -0
  184. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/engineering-mode.schema.json +0 -0
  185. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/engineering-plan-0.5.schema.json +0 -0
  186. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/engineering-prompt-artifact-0.7.schema.json +0 -0
  187. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/engineering-proposal-0.6.schema.json +0 -0
  188. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/evidence-reference-0.5.schema.json +0 -0
  189. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/foundation-document.schema.json +0 -0
  190. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/governance-profile.schema.json +0 -0
  191. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/knowledge-source-0.4.schema.json +0 -0
  192. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/knowledge-source.schema.json +0 -0
  193. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/mission-candidate-1.0.schema.json +0 -0
  194. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/mission-candidate-1.1.schema.json +0 -0
  195. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/phase-completion-1.0.schema.json +0 -0
  196. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/planning-document-0.5.schema.json +0 -0
  197. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/repository-catalog.schema.json +0 -0
  198. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/repository.schema.json +0 -0
  199. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/solution-template-1.0.schema.json +0 -0
  200. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/schemas/workspace.schema.json +0 -0
  201. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/setup.cfg +0 -0
  202. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_action_derivation.py +0 -0
  203. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_action_derivation_canary_closure.py +0 -0
  204. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_action_derivation_evidence.py +0 -0
  205. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_action_derivation_qualification.py +0 -0
  206. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_adaptive_action_sizing_design.py +0 -0
  207. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_agent_policy.py +0 -0
  208. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_aggregate_health_roadmap.py +0 -0
  209. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_ai_architect_provider.py +0 -0
  210. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_ai_architect_session.py +0 -0
  211. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_ai_development_projection.py +0 -0
  212. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_ai_provider_registry.py +0 -0
  213. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_architecture_reasoning.py +0 -0
  214. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_architecture_review_engine.py +0 -0
  215. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_architecture_workspace.py +0 -0
  216. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_autonomous_orchestrator.py +0 -0
  217. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_bootstrap_adapter.py +0 -0
  218. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_bootstrap_mission_canary.py +0 -0
  219. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_bootstrap_mission_runner.py +0 -0
  220. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_bootstrap_mission_scheduler.py +0 -0
  221. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_bootstrap_sequence_qualification.py +0 -0
  222. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_business_governance_ingress.py +0 -0
  223. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_business_workspace.py +0 -0
  224. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_canonical_data_root.py +0 -0
  225. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_capability_delegation.py +0 -0
  226. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_codex_cli_runtime_prompt.py +0 -0
  227. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_codex_cli_session.py +0 -0
  228. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_constitutional_validation.py +0 -0
  229. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_decision_evidence.py +0 -0
  230. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_default_project_quality_policy.py +0 -0
  231. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_distribution_identity.py +0 -0
  232. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_durable_action_derivation.py +0 -0
  233. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_dynamic_mission_capability.py +0 -0
  234. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_engineering_action_architecture.py +0 -0
  235. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_engineering_intent_authoring.py +0 -0
  236. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_engineering_intent_lifecycle.py +0 -0
  237. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_engineering_mission.py +0 -0
  238. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_engineering_planning.py +0 -0
  239. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_engineering_prompt_artifacts.py +0 -0
  240. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_engineering_proposals.py +0 -0
  241. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_ep_http_adapter.py +0 -0
  242. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_execution_context.py +0 -0
  243. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_execution_host_configuration.py +0 -0
  244. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_execution_host_contract.py +0 -0
  245. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_execution_loop.py +0 -0
  246. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_forge_console_admin_parity_contract.py +0 -0
  247. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_forge_console_configuration_contract.py +0 -0
  248. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_forge_console_hosting_contract.py +0 -0
  249. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_forge_operations_console_roadmap.py +0 -0
  250. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_forge_outer_loop_ci_roadmap.py +0 -0
  251. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_forge_v1_bootstrap_scheduler_contract.py +0 -0
  252. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_forge_v1_implementation_dag.py +0 -0
  253. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_foundation_loader.py +0 -0
  254. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_foundation_models.py +0 -0
  255. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_generation_one_bootstrap_qualification.py +0 -0
  256. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_generation_two_execution.py +0 -0
  257. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_generation_two_mission_intake.py +0 -0
  258. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_governance_authority.py +0 -0
  259. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_governed_coverage.py +0 -0
  260. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_historical_engineering_intent.py +0 -0
  261. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_inner_loop_ci_contract.py +0 -0
  262. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_installed_dynamic_mission_runtime.py +0 -0
  263. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_integration_coordination.py +0 -0
  264. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_knowledge_consumption.py +0 -0
  265. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_live_project_roadmap_contract.py +0 -0
  266. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_mission_completion_evaluator.py +0 -0
  267. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_mission_dispatcher.py +0 -0
  268. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_mission_planner.py +0 -0
  269. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_mission_recommendation_engine.py +0 -0
  270. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_mission_recommendation_lifecycle.py +0 -0
  271. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_mission_runtime_projection.py +0 -0
  272. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_mission_runtime_scheduler.py +0 -0
  273. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_mission_state_store.py +0 -0
  274. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_openai_action_derivation.py +0 -0
  275. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_operator_identity.py +0 -0
  276. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_phase_completion.py +0 -0
  277. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_platform_contract_documents.py +0 -0
  278. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_portfolio_intelligence_operation.py +0 -0
  279. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_producer_contract.py +0 -0
  280. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_product_version_operations.py +0 -0
  281. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_productization_reconciliation.py +0 -0
  282. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_programme_authorization.py +0 -0
  283. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_provider_security.py +0 -0
  284. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_pypi_distribution_readback.py +0 -0
  285. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_release_operation.py +0 -0
  286. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_repository_truth.py +0 -0
  287. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_role_aware_conversations_contract.py +0 -0
  288. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_runtime_bootstrap.py +0 -0
  289. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_runtime_database.py +0 -0
  290. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_runtime_evidence.py +0 -0
  291. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_runtime_health.py +0 -0
  292. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_runtime_instance_persistence.py +0 -0
  293. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_runtime_prompt_generation.py +0 -0
  294. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_runtime_service.py +0 -0
  295. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_runtime_status_projection.py +0 -0
  296. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_scheduler_dependency_boundary.py +0 -0
  297. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_schemas.py +0 -0
  298. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_secure_store.py +0 -0
  299. {forge_autonomy-2.7.22 → forge_autonomy-2.7.24}/tests/test_solution_templates.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forge-autonomy
3
- Version: 2.7.22
3
+ Version: 2.7.24
4
4
  Summary: Forge mission planning, producer contracts, and evidence interpretation runtime.
5
5
  Author: Forge maintainers
6
6
  License-Expression: Apache-2.0
@@ -123,6 +123,17 @@ The exact instance-ID comparison is consistency evidence, not a newly invented
123
123
  cryptographic peer identity; preflight reports the latter as `NOT_ASSERTED`.
124
124
  `CONFIGURED` is therefore never presented as `LIVE_READY`.
125
125
 
126
+ ## Bounded installed maintenance
127
+
128
+ The product-owned external controller for the selected Forge 2.7.21 to 2.7.22
129
+ installation transition is documented in the
130
+ [installed update runbook](docs/operations/FORGE_INSTALLED_UPDATE_RUNBOOK.md) and
131
+ [sanitized Forge 2.7.22 release/installation completion record](docs/operations/FORGE_2_7_22_RELEASE_INSTALLATION_COMPLETION.md).
132
+ It stages an exact qualified wheel, backs up and migrates the selected runtime,
133
+ and atomically activates a versioned slot. It is not packaged as a Forge
134
+ Runtime command and does not replace Forge Platform's normal installer/update
135
+ composition boundary.
136
+
126
137
  ## Operational-history reset
127
138
 
128
139
  Forge schema 38 provides a bounded, product-owned maintenance service under
@@ -106,6 +106,17 @@ The exact instance-ID comparison is consistency evidence, not a newly invented
106
106
  cryptographic peer identity; preflight reports the latter as `NOT_ASSERTED`.
107
107
  `CONFIGURED` is therefore never presented as `LIVE_READY`.
108
108
 
109
+ ## Bounded installed maintenance
110
+
111
+ The product-owned external controller for the selected Forge 2.7.21 to 2.7.22
112
+ installation transition is documented in the
113
+ [installed update runbook](docs/operations/FORGE_INSTALLED_UPDATE_RUNBOOK.md) and
114
+ [sanitized Forge 2.7.22 release/installation completion record](docs/operations/FORGE_2_7_22_RELEASE_INSTALLATION_COMPLETION.md).
115
+ It stages an exact qualified wheel, backs up and migrates the selected runtime,
116
+ and atomically activates a versioned slot. It is not packaged as a Forge
117
+ Runtime command and does not replace Forge Platform's normal installer/update
118
+ composition boundary.
119
+
109
120
  ## Operational-history reset
110
121
 
111
122
  Forge schema 38 provides a bounded, product-owned maintenance service under
@@ -101,6 +101,7 @@ def main(argv: list[str] | None = None) -> int:
101
101
  prepare_reset.add_argument("--plan-digest", required=True)
102
102
  prepare_reset.add_argument("--acknowledge-operational-fk", action="append", default=[])
103
103
  for name, help_text in (
104
+ ("revalidate", "revalidate the exact prepared operation without changing it"),
104
105
  ("apply", "apply the authorized destructive reset"),
105
106
  ("verify", "verify reset integrity and preserved bindings"),
106
107
  ):
@@ -179,7 +180,7 @@ def main(argv: list[str] | None = None) -> int:
179
180
  expected_plan_digest=args.plan_digest,
180
181
  acknowledge_operational_fk=args.acknowledge_operational_fk,
181
182
  )
182
- elif args.reset_command in {"apply", "verify"}:
183
+ elif args.reset_command in {"revalidate", "apply", "verify"}:
183
184
  result = getattr(service, args.reset_command)(
184
185
  operation_id=args.operation_id, plan_digest=args.plan_digest,
185
186
  request_digest=args.request_digest, backup_digest=args.backup_digest,
@@ -31,7 +31,8 @@ from .service import RuntimeServiceLock
31
31
 
32
32
 
33
33
  RESET_PROFILE = "forge-operational-history-v1"
34
- RESET_POLICY_VERSION = "1"
34
+ RESET_POLICY_VERSION = "2"
35
+ RESET_PLAN_CONTRACT = "forge-operational-reset-plan-1.1"
35
36
  _OPERATION_ID = re.compile(r"[a-zA-Z0-9][a-zA-Z0-9._-]{7,127}\Z")
36
37
  _DIGEST = re.compile(r"sha256:[0-9a-f]{64}\Z")
37
38
 
@@ -511,10 +512,18 @@ class ForgeOperationalResetService:
511
512
  ).fetchone()
512
513
  if state is not None:
513
514
  generation, active_operation = int(state[0]), state[1]
515
+ database_stat = self.database_path.stat()
516
+ database_binding = {
517
+ "path": str(self.database_path.resolve()),
518
+ "device": int(database_stat.st_dev),
519
+ "inode": int(database_stat.st_ino),
520
+ }
514
521
  target = {
515
522
  "product": "forge", "runtime_id": runtime_id,
516
523
  "data_root": str(self.data_root), "database": str(self.database_path),
517
- "database_binding_digest": _digest(str(self.database_path.resolve())),
524
+ "database_binding_digest": _digest(database_binding),
525
+ "database_device": database_binding["device"],
526
+ "database_inode": database_binding["inode"],
518
527
  "marker_digest": _file_digest(self.marker_path), "schema_version": schema,
519
528
  "dataset_generation": generation,
520
529
  }
@@ -580,8 +589,12 @@ class ForgeOperationalResetService:
580
589
  and active_permits == 0
581
590
  )
582
591
  source_revision = self._source_revision()
592
+ semantic_classifications = {
593
+ category: entries for category, entries in classifications.items()
594
+ if category != "MAINTENANCE_AUDIT"
595
+ }
583
596
  plan_core = {
584
- "contract": "forge-operational-reset-plan-1.0", "profile": RESET_PROFILE,
597
+ "contract": RESET_PLAN_CONTRACT, "profile": RESET_PROFILE,
585
598
  "policy_version": RESET_POLICY_VERSION, "product_version": canonical_version(),
586
599
  "source_revision": source_revision,
587
600
  "source_revision_kind": (
@@ -590,7 +603,8 @@ class ForgeOperationalResetService:
590
603
  ),
591
604
  "implementation_digest": self._implementation_digest(),
592
605
  "target": target,
593
- "relevant_revision": relevant_revision, "classifications": classifications,
606
+ "relevant_revision": relevant_revision,
607
+ "classifications": semantic_classifications,
594
608
  "preserved": preserved, "effect_set": effect_set,
595
609
  "effect_set_digest": _digest(effect_set), "foreign_key_issues": foreign_keys,
596
610
  "required_fk_acknowledgements": [item["issue_id"] for item in operational_fk],
@@ -601,7 +615,7 @@ class ForgeOperationalResetService:
601
615
  "preserved_external_digest": _digest(preserved_external),
602
616
  "unknown_external": unknown_external,
603
617
  "unknown_tables": unknown_tables, "missing_tables": missing_tables,
604
- "blockers": blockers, "no_op": no_op,
618
+ "no_op": no_op,
605
619
  "namespace": {
606
620
  "repository_document": "missions/MISSION-0003.md",
607
621
  "repository_document_kind": "HISTORICAL_REPOSITORY_DOCUMENT",
@@ -610,7 +624,17 @@ class ForgeOperationalResetService:
610
624
  },
611
625
  }
612
626
  return {
613
- **plan_core, "plan_digest": _digest(plan_core),
627
+ **plan_core,
628
+ # Availability findings remain visible and fail closed, but are
629
+ # not part of the approved semantic effect plan. In particular,
630
+ # the owning prepare transition necessarily makes maintenance
631
+ # active without redefining the reset that was authorized.
632
+ "blockers": blockers,
633
+ "plan_digest": _digest(plan_core),
634
+ "maintenance_observation": {
635
+ "active_operation_id": active_operation,
636
+ "tables": classifications["MAINTENANCE_AUDIT"],
637
+ },
614
638
  "external_control_entries": external_controls,
615
639
  }
616
640
 
@@ -999,8 +1023,79 @@ class ForgeOperationalResetService:
999
1023
  raise OperationalResetError("source revision changed after authorization")
1000
1024
  if row["implementation_digest"] != self._implementation_digest():
1001
1025
  raise OperationalResetError("maintenance service revision changed after authorization")
1026
+ actor_reference, authority_digest = self._operator_authority(connection)
1027
+ if (
1028
+ row["actor_reference"] != actor_reference
1029
+ or row["authority_digest"] != authority_digest
1030
+ ):
1031
+ raise OperationalResetError("operator or reset authority changed after authorization")
1002
1032
  return row
1003
1033
 
1034
+ def _pre_apply_revalidation(
1035
+ self, connection: sqlite3.Connection, operation: sqlite3.Row,
1036
+ ) -> dict[str, Any]:
1037
+ """Re-prove one prepared operation without redefining its approved plan."""
1038
+ if operation["state"] != "BACKUP_VERIFIED":
1039
+ raise OperationalResetError("operation is not in the prepared revalidation phase")
1040
+ operation_id = str(operation["operation_id"])
1041
+ document = json.loads(operation["document"])
1042
+ plan = document["plan"]
1043
+ current = self._inventory(connection)
1044
+ expected_maintenance = [{
1045
+ "code": "MAINTENANCE_ALREADY_ACTIVE", "operation_id": operation_id,
1046
+ }]
1047
+ if current["blockers"] != expected_maintenance:
1048
+ raise OperationalResetError("prepared operation has new or missing blocking findings")
1049
+ if current["target"] != plan["target"]:
1050
+ raise OperationalResetError("prepared operation target or database binding changed")
1051
+ if current["relevant_revision"] != operation["relevant_revision"]:
1052
+ raise OperationalResetError("meaningful source data changed after the approved preview")
1053
+ for key in (
1054
+ "effect_set_digest", "external_inventory_digest", "preserved_external_digest",
1055
+ "required_fk_acknowledgements", "foreign_key_issues", "policy_version",
1056
+ "source_revision", "implementation_digest",
1057
+ ):
1058
+ if current[key] != plan[key]:
1059
+ raise OperationalResetError(f"prepared operation binding changed: {key}")
1060
+ if int(current["target"]["dataset_generation"]) != int(operation["generation_before"]):
1061
+ raise OperationalResetError("dataset generation changed after authorization")
1062
+ evidence = {
1063
+ "operation_id": operation_id,
1064
+ "state": str(operation["state"]),
1065
+ "writer_fence_owner": operation_id,
1066
+ "actor_reference": str(operation["actor_reference"]),
1067
+ "authority_digest": str(operation["authority_digest"]),
1068
+ "target_binding_digest": str(plan["target"]["database_binding_digest"]),
1069
+ "plan_digest": str(operation["plan_digest"]),
1070
+ "request_digest": str(operation["request_digest"]),
1071
+ "effect_set_digest": str(plan["effect_set_digest"]),
1072
+ "relevant_revision_digest": str(operation["relevant_revision"]),
1073
+ "backup_digest": str(operation["backup_digest"]),
1074
+ "dataset_generation": int(operation["generation_before"]),
1075
+ "source_revision": str(operation["source_revision"]),
1076
+ "implementation_digest": str(operation["implementation_digest"]),
1077
+ }
1078
+ evidence["revalidation_digest"] = _digest(evidence)
1079
+ return evidence
1080
+
1081
+ def revalidate(
1082
+ self, *, operation_id: str, plan_digest: str, request_digest: str,
1083
+ backup_digest: str,
1084
+ ) -> dict[str, Any]:
1085
+ """Read-only proof for the already prepared, exact owning operation."""
1086
+ with self._lock.acquire():
1087
+ with self._connect(read_only=True) as connection:
1088
+ row = self._bound_operation(
1089
+ connection, operation_id, plan_digest=plan_digest,
1090
+ request_digest=request_digest, backup_digest=backup_digest,
1091
+ )
1092
+ evidence = self._pre_apply_revalidation(connection, row)
1093
+ self._verify_operation_backup(row)
1094
+ receipt = self._operation_receipt(operation_id)
1095
+ receipt["revalidation"] = evidence
1096
+ receipt["execution_allowed"] = True
1097
+ return receipt
1098
+
1004
1099
  @staticmethod
1005
1100
  def _tombstone_values(connection: sqlite3.Connection) -> list[tuple[str, str]]:
1006
1101
  queries: tuple[tuple[str, str], ...] = (
@@ -1036,6 +1131,7 @@ class ForgeOperationalResetService:
1036
1131
  # and every DELETE. A stale BACKUP_VERIFIED label can never
1037
1132
  # authorize a purge after recovery bytes were changed.
1038
1133
  self._verify_operation_backup(row)
1134
+ self._pre_apply_revalidation(connection, row)
1039
1135
  current = self._relevant_revision(connection)
1040
1136
  if current != row["relevant_revision"]:
1041
1137
  raise OperationalResetError("meaningful source data changed after the approved preview")
@@ -1487,6 +1583,11 @@ class ForgeOperationalResetService:
1487
1583
  state = str(payload.get("state") or payload.get("status") or "UNKNOWN")
1488
1584
  backup_digest = payload.get("backup_digest")
1489
1585
  backup_reference = payload.get("backup_reference")
1586
+ observed_generation = payload.get("generation_after")
1587
+ if observed_generation is None:
1588
+ observed_generation = payload.get("dataset_generation")
1589
+ if observed_generation is None:
1590
+ observed_generation = target.get("dataset_generation")
1490
1591
  return {
1491
1592
  "contract_version": "operational-reset-v1",
1492
1593
  "product": "forge",
@@ -1503,9 +1604,7 @@ class ForgeOperationalResetService:
1503
1604
  "schema_version": target.get("schema_version", payload.get("schema_version")),
1504
1605
  },
1505
1606
  "profile": payload.get("profile", RESET_PROFILE),
1506
- "dataset_generation": target.get(
1507
- "dataset_generation", payload.get("generation_after", payload.get("dataset_generation"))
1508
- ),
1607
+ "dataset_generation": observed_generation,
1509
1608
  "plan_digest": payload.get("plan_digest"),
1510
1609
  "relevant_revision_digest": (
1511
1610
  None if plan is None else plan.get("relevant_revision")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forge-autonomy
3
- Version: 2.7.22
3
+ Version: 2.7.24
4
4
  Summary: Forge mission planning, producer contracts, and evidence interpretation runtime.
5
5
  Author: Forge maintainers
6
6
  License-Expression: Apache-2.0
@@ -123,6 +123,17 @@ The exact instance-ID comparison is consistency evidence, not a newly invented
123
123
  cryptographic peer identity; preflight reports the latter as `NOT_ASSERTED`.
124
124
  `CONFIGURED` is therefore never presented as `LIVE_READY`.
125
125
 
126
+ ## Bounded installed maintenance
127
+
128
+ The product-owned external controller for the selected Forge 2.7.21 to 2.7.22
129
+ installation transition is documented in the
130
+ [installed update runbook](docs/operations/FORGE_INSTALLED_UPDATE_RUNBOOK.md) and
131
+ [sanitized Forge 2.7.22 release/installation completion record](docs/operations/FORGE_2_7_22_RELEASE_INSTALLATION_COMPLETION.md).
132
+ It stages an exact qualified wheel, backs up and migrates the selected runtime,
133
+ and atomically activates a versioned slot. It is not packaged as a Forge
134
+ Runtime command and does not replace Forge Platform's normal installer/update
135
+ composition boundary.
136
+
126
137
  ## Operational-history reset
127
138
 
128
139
  Forge schema 38 provides a bounded, product-owned maintenance service under
@@ -237,6 +237,7 @@ tests/test_execution_context.py
237
237
  tests/test_execution_host_configuration.py
238
238
  tests/test_execution_host_contract.py
239
239
  tests/test_execution_loop.py
240
+ tests/test_existing_release_reconciliation.py
240
241
  tests/test_forge_console_admin_parity_contract.py
241
242
  tests/test_forge_console_configuration_contract.py
242
243
  tests/test_forge_console_hosting_contract.py
@@ -254,6 +255,7 @@ tests/test_governed_coverage.py
254
255
  tests/test_historical_engineering_intent.py
255
256
  tests/test_inner_loop_ci_contract.py
256
257
  tests/test_installed_dynamic_mission_runtime.py
258
+ tests/test_installed_forge_update.py
257
259
  tests/test_integration_coordination.py
258
260
  tests/test_knowledge_consumption.py
259
261
  tests/test_live_project_roadmap_contract.py
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "product": "forge",
3
3
  "schema_version": 1,
4
- "version": "2.7.22"
4
+ "version": "2.7.24"
5
5
  }
@@ -0,0 +1,357 @@
1
+ """Behavioral coverage for terminal reconciliation of an existing Forge release."""
2
+
3
+ import importlib.util
4
+ from hashlib import sha256
5
+ import io
6
+ import json
7
+ from pathlib import Path
8
+ import sys
9
+ import tarfile
10
+ import tempfile
11
+ import unittest
12
+ import zipfile
13
+
14
+
15
+ SCRIPT = Path(__file__).parents[1] / "scripts" / "reconcile_existing_release.py"
16
+ SPEC = importlib.util.spec_from_file_location("forge_existing_release_reconciliation", SCRIPT)
17
+ assert SPEC and SPEC.loader
18
+ reconcile = importlib.util.module_from_spec(SPEC)
19
+ sys.modules[SPEC.name] = reconcile
20
+ SPEC.loader.exec_module(reconcile)
21
+
22
+
23
+ class ExistingReleaseReconciliationTests(unittest.TestCase):
24
+ version = "2.7.22"
25
+ source = "a" * 40
26
+ original_run_id = "35254063298"
27
+ controller = "b" * 40
28
+ reconciliation_run = "40000000000"
29
+
30
+ def setUp(self) -> None:
31
+ self.temporary = tempfile.TemporaryDirectory()
32
+ self.addCleanup(self.temporary.cleanup)
33
+ self.root = Path(self.temporary.name)
34
+ self.wheel = self.root / f"forge_autonomy-{self.version}-py3-none-any.whl"
35
+ self.sdist = self.root / f"forge_autonomy-{self.version}.tar.gz"
36
+ metadata = f"Metadata-Version: 2.4\nName: forge-autonomy\nVersion: {self.version}\n\n"
37
+ with zipfile.ZipFile(self.wheel, "w") as archive:
38
+ archive.writestr(f"forge_autonomy-{self.version}.dist-info/METADATA", metadata)
39
+ with tarfile.open(self.sdist, "w:gz") as archive:
40
+ data = metadata.encode()
41
+ member = tarfile.TarInfo(f"forge_autonomy-{self.version}/PKG-INFO")
42
+ member.size = len(data)
43
+ archive.addfile(member, io.BytesIO(data))
44
+ self.wheel_digest = sha256(self.wheel.read_bytes()).hexdigest()
45
+ self.sdist_digest = sha256(self.sdist.read_bytes()).hexdigest()
46
+ self.hashes = self.root / "SHA256SUMS"
47
+ self.hashes.write_text(
48
+ f"{self.wheel_digest} dist/{self.wheel.name}\n"
49
+ f"{self.sdist_digest} dist/{self.sdist.name}\n",
50
+ encoding="utf-8",
51
+ )
52
+ self.operation = reconcile.ReleaseOperation.create(
53
+ operation_id=f"forge-release-{self.version}-{self.source}",
54
+ version=self.version,
55
+ policy_revision="forge-bootstrap-release-cadence-v2",
56
+ source_revision=self.source,
57
+ artifacts={
58
+ "wheel": "sha256:" + self.wheel_digest,
59
+ "sdist": "sha256:" + self.sdist_digest,
60
+ },
61
+ ).transition("QUALIFIED", evidence={
62
+ "exact_main_sha": self.source,
63
+ "qualification": "forge-production-distribution",
64
+ "artifact_digests": {
65
+ f"dist/{self.wheel.name}": "sha256:" + self.wheel_digest,
66
+ f"dist/{self.sdist.name}": "sha256:" + self.sdist_digest,
67
+ },
68
+ })
69
+ self.qualified_bytes = self.operation_bytes(self.operation)
70
+ self.qualified_name = f"forge-release-qualified-{self.version}-{self.source}.json"
71
+ self.assets = self.root / "assets"
72
+ self.assets.mkdir()
73
+ (self.assets / self.qualified_name).write_bytes(self.qualified_bytes)
74
+ self.release = self.release_document([self.qualified_name])
75
+ self.run = {
76
+ "databaseId": int(self.original_run_id),
77
+ "status": "completed",
78
+ "conclusion": "failure",
79
+ "headSha": self.source,
80
+ "event": "workflow_dispatch",
81
+ "workflowName": "Forge production release",
82
+ "jobs": [
83
+ {"name": "build-and-qualify", "conclusion": "success"},
84
+ {"name": "publish-pypi", "conclusion": "success"},
85
+ {"name": "registry-readback-and-published-evidence", "conclusion": "failure"},
86
+ {"name": "record-release-complete", "conclusion": "skipped"},
87
+ ],
88
+ }
89
+
90
+ @staticmethod
91
+ def operation_bytes(operation: object) -> bytes:
92
+ return (json.dumps(reconcile.asdict(operation), sort_keys=True, separators=(",", ":")) + "\n").encode()
93
+
94
+ def release_document(self, names: list[str], *, draft: bool = True, database_id: int = 123) -> dict[str, object]:
95
+ assets = []
96
+ for name in names:
97
+ content = (self.assets / name).read_bytes()
98
+ assets.append({
99
+ "name": name,
100
+ "state": "uploaded",
101
+ "size": len(content),
102
+ "digest": "sha256:" + sha256(content).hexdigest(),
103
+ })
104
+ return {
105
+ "tagName": f"forge-v{self.version}",
106
+ "targetCommitish": self.source,
107
+ "databaseId": database_id,
108
+ "id": "RE_kwDOReleaseNode",
109
+ "apiUrl": "https://api.github.com/repos/example/forge/releases/123",
110
+ "isDraft": draft,
111
+ "isPrerelease": False,
112
+ "isImmutable": False,
113
+ "assets": assets,
114
+ }
115
+
116
+ @property
117
+ def release_identity(self) -> dict[str, object]:
118
+ return reconcile.validate_release_inventory(
119
+ self.release, version=self.version, source_revision=self.source,
120
+ )["release"]
121
+
122
+ def validate(self, **changes: object) -> dict[str, object]:
123
+ values = {
124
+ "run_document": self.run,
125
+ "qualified_action": self.qualified_bytes,
126
+ "qualified_release": self.qualified_bytes,
127
+ "hashes": self.hashes,
128
+ "wheel": self.wheel,
129
+ "sdist": self.sdist,
130
+ "version": self.version,
131
+ "source_revision": self.source,
132
+ "original_run_id": self.original_run_id,
133
+ "controller_source": self.controller,
134
+ "reconciliation_run_id": self.reconciliation_run,
135
+ "release_document": self.release,
136
+ }
137
+ values.update(changes)
138
+ return reconcile.validate_reconciliation(**values)
139
+
140
+ def published_operation(self) -> object:
141
+ return self.operation.transition("PUBLISHED", evidence=self.validate())
142
+
143
+ def test_exact_historical_release_and_fresh_registry_bytes_are_accepted(self) -> None:
144
+ evidence = self.validate()
145
+ self.assertEqual(evidence["readback"], "PASS")
146
+ self.assertEqual(evidence["product_source_revision"], self.source)
147
+ self.assertEqual(evidence["release_controller_source"], self.controller)
148
+ self.assertEqual(evidence["github_release"], self.release_identity)
149
+
150
+ def test_wrong_artifact_or_unexpected_original_failure_is_rejected(self) -> None:
151
+ self.wheel.write_bytes(b"different registry bytes")
152
+ with self.assertRaisesRegex(reconcile.ExistingReleaseReconciliationError, "registry artifacts"):
153
+ self.validate()
154
+ changed_run = {**self.run, "conclusion": "success"}
155
+ with self.assertRaisesRegex(reconcile.ExistingReleaseReconciliationError, "conclusion"):
156
+ self.validate(run_document=changed_run)
157
+
158
+ def test_changed_release_receipt_and_release_object_are_rejected(self) -> None:
159
+ with self.assertRaisesRegex(reconcile.ExistingReleaseReconciliationError, "differs"):
160
+ self.validate(qualified_release=self.qualified_bytes + b" ")
161
+ changed = self.release_document([self.qualified_name], database_id=124)
162
+ with self.assertRaisesRegex(reconcile.ExistingReleaseReconciliationError, "identity changed"):
163
+ reconcile.validate_same_release(
164
+ self.release, changed, version=self.version, source_revision=self.source,
165
+ )
166
+
167
+ def test_inventory_rejects_unknown_duplicate_and_incomplete_lineage(self) -> None:
168
+ unknown = {**self.release, "assets": [*self.release["assets"], {
169
+ "name": "forge_autonomy-2.7.22-py3-none-any.whl", "state": "uploaded",
170
+ "size": 1, "digest": "sha256:" + "0" * 64,
171
+ }]}
172
+ with self.assertRaisesRegex(reconcile.ExistingReleaseReconciliationError, "unexpected asset"):
173
+ reconcile.validate_release_inventory(unknown, version=self.version, source_revision=self.source)
174
+ duplicate = {**self.release, "assets": [*self.release["assets"], self.release["assets"][0]]}
175
+ with self.assertRaisesRegex(reconcile.ExistingReleaseReconciliationError, "duplicate"):
176
+ reconcile.validate_release_inventory(duplicate, version=self.version, source_revision=self.source)
177
+ complete = f"forge-release-complete-{self.version}-{self.source}.json"
178
+ (self.assets / complete).write_text("{}", encoding="utf-8")
179
+ incomplete = self.release_document([self.qualified_name, complete])
180
+ with self.assertRaisesRegex(reconcile.ExistingReleaseReconciliationError, "published predecessor"):
181
+ reconcile.validate_release_inventory(incomplete, version=self.version, source_revision=self.source)
182
+ prerelease = {**self.release, "isPrerelease": True}
183
+ with self.assertRaisesRegex(reconcile.ExistingReleaseReconciliationError, "must not be a prerelease"):
184
+ reconcile.validate_release_inventory(prerelease, version=self.version, source_revision=self.source)
185
+
186
+ def test_release_asset_bytes_and_symlinks_are_fail_closed(self) -> None:
187
+ self.assertEqual(
188
+ reconcile.validate_release_assets(
189
+ self.release, self.assets, version=self.version, source_revision=self.source,
190
+ )["assets"],
191
+ [self.qualified_name],
192
+ )
193
+ (self.assets / self.qualified_name).write_bytes(b"changed")
194
+ with self.assertRaisesRegex(reconcile.ExistingReleaseReconciliationError, "differs"):
195
+ reconcile.validate_release_assets(
196
+ self.release, self.assets, version=self.version, source_revision=self.source,
197
+ )
198
+ (self.assets / self.qualified_name).unlink()
199
+ (self.assets / self.qualified_name).symlink_to(self.hashes)
200
+ with self.assertRaisesRegex(reconcile.ExistingReleaseReconciliationError, "unsafe"):
201
+ reconcile.validate_release_assets(
202
+ self.release, self.assets, version=self.version, source_revision=self.source,
203
+ )
204
+
205
+ def test_followup_lineage_is_stable_across_resume_controller_runs(self) -> None:
206
+ published = self.published_operation()
207
+ published_path = self.root / "published.json"
208
+ published_path.write_bytes(self.operation_bytes(published))
209
+ complete_evidence = reconcile.completion_evidence(
210
+ version=self.version, original_run_id=self.original_run_id,
211
+ controller_source="c" * 40, reconciliation_run_id="50000000000",
212
+ source_revision=self.source, release_identity=self.release_identity,
213
+ )
214
+ complete = published.transition("RELEASE_COMPLETE", evidence=complete_evidence)
215
+ complete_path = self.root / "complete.json"
216
+ complete_path.write_bytes(self.operation_bytes(complete))
217
+ result = reconcile.validate_followup_receipts(
218
+ published_path=published_path, pending_path=None, complete_path=complete_path,
219
+ original_run_id=self.original_run_id, release_identity=self.release_identity,
220
+ )
221
+ self.assertEqual(result["state"], "RELEASE_COMPLETE")
222
+ self.assertEqual(result["controller_runs"], [
223
+ {"phase": "publication", "run_id": self.reconciliation_run,
224
+ "controller_source": self.controller},
225
+ {"phase": "completion", "run_id": "50000000000",
226
+ "controller_source": "c" * 40},
227
+ ])
228
+ self.assertEqual(
229
+ reconcile.validate_followup_receipts(
230
+ published_path=published_path, pending_path=None, complete_path=complete_path,
231
+ original_run_id=self.original_run_id, release_identity=self.release_identity,
232
+ ), result,
233
+ )
234
+
235
+ def test_cleanup_pending_is_durable_and_may_advance_to_complete(self) -> None:
236
+ published = self.published_operation()
237
+ published_path = self.root / "published.json"
238
+ published_path.write_bytes(self.operation_bytes(published))
239
+ pending_evidence = reconcile.cleanup_pending_evidence(
240
+ original_run_id=self.original_run_id, controller_source="c" * 40,
241
+ reconciliation_run_id="50000000000", source_revision=self.source,
242
+ release_identity=self.release_identity, failed_targets=["published-assets"],
243
+ )
244
+ pending = published.transition("CLEANUP_PENDING", evidence=pending_evidence)
245
+ pending_path = self.root / "pending.json"
246
+ pending_path.write_bytes(self.operation_bytes(pending))
247
+ result = reconcile.validate_followup_receipts(
248
+ published_path=published_path, pending_path=pending_path, complete_path=None,
249
+ original_run_id=self.original_run_id, release_identity=self.release_identity,
250
+ )
251
+ self.assertEqual(result["state"], "CLEANUP_PENDING")
252
+ complete = pending.transition("RELEASE_COMPLETE", evidence=reconcile.completion_evidence(
253
+ version=self.version, original_run_id=self.original_run_id,
254
+ controller_source="d" * 40, reconciliation_run_id="60000000000",
255
+ source_revision=self.source, release_identity=self.release_identity,
256
+ ))
257
+ complete_path = self.root / "complete.json"
258
+ complete_path.write_bytes(self.operation_bytes(complete))
259
+ result = reconcile.validate_followup_receipts(
260
+ published_path=published_path, pending_path=pending_path, complete_path=complete_path,
261
+ original_run_id=self.original_run_id, release_identity=self.release_identity,
262
+ )
263
+ self.assertEqual(result["state"], "RELEASE_COMPLETE")
264
+
265
+ def test_new_publication_and_controller_run_bind_exact_current_provenance(self) -> None:
266
+ qualified = self.root / "qualified.json"
267
+ qualified.write_bytes(self.qualified_bytes)
268
+ publication = self.root / "publication.json"
269
+ publication.write_text(json.dumps(self.validate()), encoding="utf-8")
270
+ result = reconcile.validate_new_publication_evidence(
271
+ qualified_path=qualified, publication_path=publication,
272
+ original_run_id=self.original_run_id, release_identity=self.release_identity,
273
+ controller_source=self.controller, reconciliation_run_id=self.reconciliation_run,
274
+ )
275
+ self.assertEqual(result["controller_runs"][0]["run_id"], self.reconciliation_run)
276
+ with self.assertRaisesRegex(reconcile.ExistingReleaseReconciliationError, "this controller run"):
277
+ reconcile.validate_new_publication_evidence(
278
+ qualified_path=qualified, publication_path=publication,
279
+ original_run_id=self.original_run_id, release_identity=self.release_identity,
280
+ controller_source="f" * 40, reconciliation_run_id=self.reconciliation_run,
281
+ )
282
+ run = {
283
+ "databaseId": int(self.reconciliation_run), "headSha": self.controller,
284
+ "event": "workflow_dispatch", "workflowName": "Forge existing release reconciliation",
285
+ "status": "completed", "conclusion": "success",
286
+ }
287
+ self.assertEqual(
288
+ reconcile.validate_controller_run(
289
+ run, run_id=self.reconciliation_run, controller_source=self.controller,
290
+ )["controller_source"], self.controller,
291
+ )
292
+ with self.assertRaisesRegex(reconcile.ExistingReleaseReconciliationError, "headSha"):
293
+ reconcile.validate_controller_run(
294
+ run, run_id=self.reconciliation_run, controller_source="f" * 40,
295
+ )
296
+
297
+ def test_followup_receipt_hydration_is_monotonic_and_idempotent(self) -> None:
298
+ evidence_root = self.root / "evidence"
299
+ store = reconcile.ReleaseOperationStore(evidence_root)
300
+ store.acquire(self.operation.operation_id)
301
+ try:
302
+ store.save(self.operation)
303
+ finally:
304
+ store.release(self.operation.operation_id)
305
+ published = self.published_operation()
306
+ receipt = self.root / "published.json"
307
+ receipt.write_bytes(self.operation_bytes(published))
308
+ self.assertEqual(
309
+ reconcile.hydrate_followup_receipt(evidence_root, self.operation.operation_id, receipt), published,
310
+ )
311
+ self.assertEqual(
312
+ reconcile.hydrate_followup_receipt(evidence_root, self.operation.operation_id, receipt), published,
313
+ )
314
+ receipt.write_bytes(self.qualified_bytes)
315
+ with self.assertRaisesRegex(reconcile.ExistingReleaseReconciliationError, "invalid operation or state"):
316
+ reconcile.hydrate_followup_receipt(evidence_root, self.operation.operation_id, receipt)
317
+
318
+ def test_workflow_separates_artifact_execution_from_release_mutation(self) -> None:
319
+ workflow = Path(".github/workflows/forge-existing-release-reconcile.yml").read_text(encoding="utf-8")
320
+ validation = workflow.split(" validate-existing-release:", 1)[1].split(
321
+ " smoke-existing-release:", 1,
322
+ )[0]
323
+ smoke = workflow.split(" smoke-existing-release:", 1)[1].split(
324
+ " reconcile-existing-release:", 1,
325
+ )[0]
326
+ mutation = workflow.split(" reconcile-existing-release:", 1)[1]
327
+ self.assertIn("persist-credentials: false", workflow)
328
+ self.assertIn("contents: read", validation)
329
+ self.assertNotIn("pip install", validation)
330
+ self.assertNotIn("installed_smoke", validation)
331
+ self.assertNotIn("gh release edit", validation)
332
+ self.assertIn("forge-registry-wheel", smoke)
333
+ self.assertIn("env -i HOME=", smoke)
334
+ self.assertIn("--no-index --no-deps", smoke)
335
+ self.assertNotIn("upload-artifact", smoke)
336
+ self.assertNotIn("publication.json", smoke)
337
+ self.assertIn("contents: write", mutation)
338
+ self.assertNotIn("pip install", mutation)
339
+ self.assertNotIn("registry-readback", mutation)
340
+ self.assertNotIn("forge-release-smoke-", mutation)
341
+ self.assertIn("validate-publication", mutation)
342
+ self.assertIn("validate-controller-run", mutation)
343
+ self.assertIn("verify_controller_runs", mutation)
344
+ self.assertIn("same-release", mutation)
345
+ self.assertIn("validate-assets", mutation)
346
+ self.assertIn("$tag^{commit}", mutation)
347
+ self.assertIn("--mark-cleanup-pending", mutation)
348
+ self.assertIn("--wheel-digest", mutation)
349
+ self.assertIn("--complete", mutation)
350
+ self.assertNotIn("gh-action-pypi-publish", workflow)
351
+ self.assertNotIn("twine upload", workflow)
352
+ self.assertNotIn("python3 -m build", workflow)
353
+ self.assertNotIn("git tag", workflow)
354
+
355
+
356
+ if __name__ == "__main__":
357
+ unittest.main()