forge-autonomy 2.7.25__tar.gz → 2.7.26__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 (315) hide show
  1. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/PKG-INFO +1 -1
  2. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/__main__.py +58 -0
  3. forge_autonomy-2.7.26/forge/completion/host_control_observer.py +164 -0
  4. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/completion/mission.py +53 -1
  5. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/execution/loop.py +44 -2
  6. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/governance_authority.py +11 -0
  7. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/intake.py +26 -14
  8. forge_autonomy-2.7.26/forge/mission_cli.py +353 -0
  9. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/criterion_assessment.py +42 -5
  10. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/criterion_observation.py +1 -1
  11. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/execution_host.py +1 -0
  12. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/runtime/dynamic_mission.py +53 -2
  13. forge_autonomy-2.7.26/forge/runtime/mission_controller.py +242 -0
  14. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/runtime/operational_reset.py +6 -2
  15. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/runtime/runner.py +11 -1
  16. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/scheduler/ep_http_adapter.py +91 -12
  17. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/scheduler/ep_v12.py +18 -7
  18. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge_autonomy.egg-info/PKG-INFO +1 -1
  19. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge_autonomy.egg-info/SOURCES.txt +6 -0
  20. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/product-version.json +1 -1
  21. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_bootstrap_mission_runner.py +17 -0
  22. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_criterion_ep_http_boundary.py +4 -3
  23. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_dynamic_mission_capability.py +55 -0
  24. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_ep_http_adapter.py +135 -1
  25. forge_autonomy-2.7.26/tests/test_host_control_completion.py +442 -0
  26. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_installed_dynamic_mission_runtime.py +79 -1
  27. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_installed_forge_update.py +297 -4
  28. forge_autonomy-2.7.26/tests/test_mission_controller.py +218 -0
  29. forge_autonomy-2.7.26/tests/test_mission_status.py +35 -0
  30. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/MANIFEST.in +0 -0
  31. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/README.md +0 -0
  32. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/__init__.py +0 -0
  33. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/_version.py +0 -0
  34. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/action_derivation_canary_closure.py +0 -0
  35. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/action_derivation_evidence.py +0 -0
  36. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/action_derivation_qualification.py +0 -0
  37. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/action_derivation_reattempt.py +0 -0
  38. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/ai_architect/__init__.py +0 -0
  39. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/ai_architect/provider_registry.py +0 -0
  40. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/architecture/__init__.py +0 -0
  41. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/architecture/advisor.py +0 -0
  42. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/architecture/presentation.py +0 -0
  43. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/architecture/workspace.py +0 -0
  44. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/autonomous_orchestrator.py +0 -0
  45. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/business/__init__.py +0 -0
  46. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/business/advisor.py +0 -0
  47. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/business/ingress.py +0 -0
  48. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/business/presentation.py +0 -0
  49. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/business/workspace.py +0 -0
  50. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/canary.py +0 -0
  51. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/capabilities/__init__.py +0 -0
  52. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/capabilities/delegation.py +0 -0
  53. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/completion/__init__.py +0 -0
  54. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/completion/assessor.py +0 -0
  55. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/completion/repository_observer.py +0 -0
  56. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/constitutional/__init__.py +0 -0
  57. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/constitutional/assessor.py +0 -0
  58. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/core/__init__.py +0 -0
  59. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/core/json_store.py +0 -0
  60. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/decision_evidence/__init__.py +0 -0
  61. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/decision_evidence/repository.py +0 -0
  62. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/dispatcher/__init__.py +0 -0
  63. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/dispatcher/dispatcher.py +0 -0
  64. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/execution/__init__.py +0 -0
  65. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/execution/presentation.py +0 -0
  66. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/execution_host_configuration.py +0 -0
  67. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/foundation/__init__.py +0 -0
  68. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/foundation/loader.py +0 -0
  69. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/generation_two.py +0 -0
  70. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/generation_two_execution.py +0 -0
  71. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/governance/__init__.py +0 -0
  72. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/governance/execution_policy.py +0 -0
  73. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/governance/profiles.py +0 -0
  74. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/integration/__init__.py +0 -0
  75. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/integration/coordinator.py +0 -0
  76. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/integration/repository.py +0 -0
  77. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/knowledge/__init__.py +0 -0
  78. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/knowledge/consumer.py +0 -0
  79. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/knowledge/registry.py +0 -0
  80. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/lifecycle/__init__.py +0 -0
  81. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/lifecycle/store.py +0 -0
  82. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/mission_amendment.py +0 -0
  83. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/mission_scheduler.py +0 -0
  84. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/__init__.py +0 -0
  85. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/action.py +0 -0
  86. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/action_derivation.py +0 -0
  87. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/agent_policy.py +0 -0
  88. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/ai_architect_provider.py +0 -0
  89. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/ai_architect_session.py +0 -0
  90. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/ai_provider_registry.py +0 -0
  91. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/architecture_mission.py +0 -0
  92. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/architecture_reasoning.py +0 -0
  93. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/architecture_review.py +0 -0
  94. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/codex_runtime_prompt.py +0 -0
  95. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/constitutional_validation.py +0 -0
  96. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/decision_evidence.py +0 -0
  97. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/foundation.py +0 -0
  98. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/historical_intent.py +0 -0
  99. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/integration.py +0 -0
  100. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/intent.py +0 -0
  101. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/intent_authoring.py +0 -0
  102. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/mission.py +0 -0
  103. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/mission_candidate.py +0 -0
  104. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/mission_completion.py +0 -0
  105. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/mission_planner.py +0 -0
  106. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/mission_recommendation.py +0 -0
  107. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/phase_completion.py +0 -0
  108. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/planning.py +0 -0
  109. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/producer.py +0 -0
  110. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/prompt_artifact.py +0 -0
  111. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/proposal.py +0 -0
  112. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/runtime_prompt.py +0 -0
  113. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/models/solution_template.py +0 -0
  114. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/operator_identity.py +0 -0
  115. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/planner/__init__.py +0 -0
  116. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/planner/action_derivation.py +0 -0
  117. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/planner/codex_cli_session.py +0 -0
  118. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/planner/durable_derivation.py +0 -0
  119. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/planner/engine.py +0 -0
  120. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/planner/openai_responses.py +0 -0
  121. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/planner/provider_adapter.py +0 -0
  122. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/planning/__init__.py +0 -0
  123. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/planning/loader.py +0 -0
  124. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/planning/registry.py +0 -0
  125. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/portfolio_intelligence.py +0 -0
  126. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/programme_authorization.py +0 -0
  127. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/prompts/__init__.py +0 -0
  128. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/prompts/codex_cli.py +0 -0
  129. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/prompts/generator.py +0 -0
  130. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/proposals/__init__.py +0 -0
  131. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/proposals/generator.py +0 -0
  132. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/provider_security.py +0 -0
  133. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/qualification/__init__.py +0 -0
  134. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/qualification/bootstrap_sequence.py +0 -0
  135. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/qualification/criterion_completion.py +0 -0
  136. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/qualification/generation_one.py +0 -0
  137. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/qualification/installed_smoke.py +0 -0
  138. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/recommendations/__init__.py +0 -0
  139. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/recommendations/engine.py +0 -0
  140. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/repository_truth.py +0 -0
  141. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/review/__init__.py +0 -0
  142. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/review/engine.py +0 -0
  143. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/runtime/__init__.py +0 -0
  144. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/runtime/bootstrap.py +0 -0
  145. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/runtime/data_root.py +0 -0
  146. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/runtime/database.py +0 -0
  147. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/runtime/evidence.py +0 -0
  148. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/runtime/execution_context.py +0 -0
  149. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/runtime/health.py +0 -0
  150. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/runtime/service.py +0 -0
  151. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/scheduler/__init__.py +0 -0
  152. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/scheduler/adapter.py +0 -0
  153. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/scheduler/scheduler.py +0 -0
  154. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/capability-registry-1.0.schema.json +0 -0
  155. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/capability.schema.json +0 -0
  156. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/common.schema.json +0 -0
  157. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/decision-evidence-1.0.schema.json +0 -0
  158. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/engineering-goal-0.5.schema.json +0 -0
  159. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/engineering-increment-proposal-0.5.schema.json +0 -0
  160. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/engineering-mode.schema.json +0 -0
  161. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/engineering-plan-0.5.schema.json +0 -0
  162. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/engineering-platform-peer-configuration-1.0.schema.json +0 -0
  163. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/engineering-platform-peer-configuration-1.1.schema.json +0 -0
  164. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/engineering-prompt-artifact-0.7.schema.json +0 -0
  165. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/engineering-proposal-0.6.schema.json +0 -0
  166. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/evidence-reference-0.5.schema.json +0 -0
  167. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/foundation-document.schema.json +0 -0
  168. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/governance-profile.schema.json +0 -0
  169. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/knowledge-source-0.4.schema.json +0 -0
  170. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/knowledge-source.schema.json +0 -0
  171. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/mission-candidate-1.0.schema.json +0 -0
  172. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/mission-candidate-1.1.schema.json +0 -0
  173. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/phase-completion-1.0.schema.json +0 -0
  174. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/planning-document-0.5.schema.json +0 -0
  175. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/repository-catalog.schema.json +0 -0
  176. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/repository.schema.json +0 -0
  177. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/solution-template-1.0.schema.json +0 -0
  178. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/schemas/workspace.schema.json +0 -0
  179. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/secure_store.py +0 -0
  180. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/solutions/__init__.py +0 -0
  181. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/solutions/catalogue.py +0 -0
  182. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/solutions/generator.py +0 -0
  183. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/state/__init__.py +0 -0
  184. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/state/mission_state.py +0 -0
  185. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge/workspace/__init__.py +0 -0
  186. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge_autonomy.egg-info/dependency_links.txt +0 -0
  187. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge_autonomy.egg-info/entry_points.txt +0 -0
  188. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/forge_autonomy.egg-info/top_level.txt +0 -0
  189. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/pyproject.toml +0 -0
  190. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/capability-registry-1.0.schema.json +0 -0
  191. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/capability.schema.json +0 -0
  192. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/common.schema.json +0 -0
  193. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/decision-evidence-1.0.schema.json +0 -0
  194. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/engineering-goal-0.5.schema.json +0 -0
  195. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/engineering-increment-proposal-0.5.schema.json +0 -0
  196. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/engineering-mode.schema.json +0 -0
  197. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/engineering-plan-0.5.schema.json +0 -0
  198. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/engineering-prompt-artifact-0.7.schema.json +0 -0
  199. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/engineering-proposal-0.6.schema.json +0 -0
  200. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/evidence-reference-0.5.schema.json +0 -0
  201. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/foundation-document.schema.json +0 -0
  202. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/governance-profile.schema.json +0 -0
  203. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/knowledge-source-0.4.schema.json +0 -0
  204. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/knowledge-source.schema.json +0 -0
  205. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/mission-candidate-1.0.schema.json +0 -0
  206. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/mission-candidate-1.1.schema.json +0 -0
  207. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/phase-completion-1.0.schema.json +0 -0
  208. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/planning-document-0.5.schema.json +0 -0
  209. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/repository-catalog.schema.json +0 -0
  210. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/repository.schema.json +0 -0
  211. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/solution-template-1.0.schema.json +0 -0
  212. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/schemas/workspace.schema.json +0 -0
  213. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/setup.cfg +0 -0
  214. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_action_derivation.py +0 -0
  215. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_action_derivation_canary_closure.py +0 -0
  216. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_action_derivation_evidence.py +0 -0
  217. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_action_derivation_qualification.py +0 -0
  218. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_adaptive_action_sizing_design.py +0 -0
  219. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_agent_policy.py +0 -0
  220. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_aggregate_health_roadmap.py +0 -0
  221. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_ai_architect_provider.py +0 -0
  222. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_ai_architect_session.py +0 -0
  223. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_ai_development_projection.py +0 -0
  224. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_ai_provider_registry.py +0 -0
  225. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_architecture_reasoning.py +0 -0
  226. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_architecture_review_engine.py +0 -0
  227. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_architecture_workspace.py +0 -0
  228. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_autonomous_orchestrator.py +0 -0
  229. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_bootstrap_adapter.py +0 -0
  230. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_bootstrap_mission_canary.py +0 -0
  231. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_bootstrap_mission_scheduler.py +0 -0
  232. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_bootstrap_sequence_qualification.py +0 -0
  233. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_business_governance_ingress.py +0 -0
  234. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_business_workspace.py +0 -0
  235. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_canonical_data_root.py +0 -0
  236. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_capability_delegation.py +0 -0
  237. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_codex_cli_runtime_prompt.py +0 -0
  238. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_codex_cli_session.py +0 -0
  239. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_constitutional_validation.py +0 -0
  240. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_criterion_assessment_contract.py +0 -0
  241. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_criterion_planning_context.py +0 -0
  242. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_decision_evidence.py +0 -0
  243. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_default_project_quality_policy.py +0 -0
  244. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_distribution_identity.py +0 -0
  245. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_durable_action_derivation.py +0 -0
  246. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_engineering_action_architecture.py +0 -0
  247. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_engineering_intent_authoring.py +0 -0
  248. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_engineering_intent_lifecycle.py +0 -0
  249. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_engineering_mission.py +0 -0
  250. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_engineering_planning.py +0 -0
  251. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_engineering_prompt_artifacts.py +0 -0
  252. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_engineering_proposals.py +0 -0
  253. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_execution_context.py +0 -0
  254. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_execution_host_configuration.py +0 -0
  255. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_execution_host_contract.py +0 -0
  256. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_execution_loop.py +0 -0
  257. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_existing_release_reconciliation.py +0 -0
  258. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_forge_console_admin_parity_contract.py +0 -0
  259. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_forge_console_configuration_contract.py +0 -0
  260. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_forge_console_hosting_contract.py +0 -0
  261. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_forge_operations_console_roadmap.py +0 -0
  262. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_forge_outer_loop_ci_roadmap.py +0 -0
  263. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_forge_v1_bootstrap_scheduler_contract.py +0 -0
  264. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_forge_v1_implementation_dag.py +0 -0
  265. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_foundation_loader.py +0 -0
  266. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_foundation_models.py +0 -0
  267. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_generation_one_bootstrap_qualification.py +0 -0
  268. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_generation_two_execution.py +0 -0
  269. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_generation_two_mission_intake.py +0 -0
  270. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_governance_authority.py +0 -0
  271. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_governed_coverage.py +0 -0
  272. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_historical_engineering_intent.py +0 -0
  273. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_inner_loop_ci_contract.py +0 -0
  274. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_integration_coordination.py +0 -0
  275. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_knowledge_consumption.py +0 -0
  276. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_live_project_roadmap_contract.py +0 -0
  277. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_mission_completion_evaluator.py +0 -0
  278. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_mission_dispatcher.py +0 -0
  279. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_mission_planner.py +0 -0
  280. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_mission_recommendation_engine.py +0 -0
  281. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_mission_recommendation_lifecycle.py +0 -0
  282. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_mission_runtime_projection.py +0 -0
  283. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_mission_runtime_scheduler.py +0 -0
  284. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_mission_state_store.py +0 -0
  285. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_openai_action_derivation.py +0 -0
  286. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_operational_reset.py +0 -0
  287. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_operator_identity.py +0 -0
  288. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_phase_completion.py +0 -0
  289. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_platform_contract_documents.py +0 -0
  290. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_portfolio_intelligence_operation.py +0 -0
  291. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_producer_contract.py +0 -0
  292. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_product_version_operations.py +0 -0
  293. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_production_release_workflow.py +0 -0
  294. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_productization_reconciliation.py +0 -0
  295. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_programme_authorization.py +0 -0
  296. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_provider_security.py +0 -0
  297. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_pypi_distribution_readback.py +0 -0
  298. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_recovered_partial_completion.py +0 -0
  299. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_release_operation.py +0 -0
  300. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_repository_truth.py +0 -0
  301. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_role_aware_conversations_contract.py +0 -0
  302. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_runtime_bootstrap.py +0 -0
  303. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_runtime_database.py +0 -0
  304. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_runtime_evidence.py +0 -0
  305. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_runtime_health.py +0 -0
  306. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_runtime_instance_persistence.py +0 -0
  307. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_runtime_prompt_generation.py +0 -0
  308. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_runtime_service.py +0 -0
  309. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_runtime_status_projection.py +0 -0
  310. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_scheduler_dependency_boundary.py +0 -0
  311. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_schemas.py +0 -0
  312. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_secure_store.py +0 -0
  313. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_solution_templates.py +0 -0
  314. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/tests/test_substantive_mission_completion.py +0 -0
  315. {forge_autonomy-2.7.25 → forge_autonomy-2.7.26}/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.25
3
+ Version: 2.7.26
4
4
  Summary: Forge mission planning, producer contracts, and evidence interpretation runtime.
5
5
  Author: Forge maintainers
6
6
  License-Expression: Apache-2.0
@@ -122,6 +122,29 @@ def main(argv: list[str] | None = None) -> int:
122
122
  finish_reset.add_argument("--verification-digest")
123
123
  finish_reset.add_argument("--cancel-before-apply", action="store_true")
124
124
  subparsers.add_parser("status", help="print read-only runtime status as JSON")
125
+ mission = subparsers.add_parser("mission", help="govern and run one selected Mission")
126
+ mission_commands = mission.add_subparsers(dest="mission_command", required=True)
127
+ for name, description in (
128
+ ("inspect", "validate Mission input and evidence fit without allocation"),
129
+ ("approve-business", "record canonical Business approval"),
130
+ ("approve-architecture", "record canonical Architecture approval"),
131
+ ("admit", "allocate and admit the canonically approved Mission"),
132
+ ):
133
+ command = mission_commands.add_parser(name, help=description)
134
+ command.add_argument("--input", required=True)
135
+ for name, description in (
136
+ ("run", "start once and control the selected Mission to a terminal or declared stop"),
137
+ ("reopen", "continue the same durable Mission execution"),
138
+ ("status", "read the exact Mission status and result"),
139
+ ("stop", "request a controlled foreground stop"),
140
+ ):
141
+ command = mission_commands.add_parser(name, help=description)
142
+ command.add_argument("--mission-id", required=True)
143
+ if name == "run":
144
+ command.add_argument("--repository-truth", required=True)
145
+ if name in {"run", "reopen"}:
146
+ command.add_argument("--poll-seconds", type=float, default=1.0)
147
+ command.add_argument("--maximum-wait-seconds", type=float, default=3600.0)
125
148
  execution_host = subparsers.add_parser("execution-host", help="manage the selected Execution Host peer")
126
149
  execution_host_commands = execution_host.add_subparsers(dest="execution_host_command", required=True)
127
150
  configure = execution_host_commands.add_parser("configure", help="persist one explicit EP peer binding")
@@ -159,6 +182,41 @@ def main(argv: list[str] | None = None) -> int:
159
182
  help="bounded interactive setup timeout in seconds (maximum: 120)",
160
183
  )
161
184
  args = parser.parse_args(argv)
185
+ if args.command == "mission":
186
+ from . import mission_cli
187
+ try:
188
+ command = args.mission_command
189
+ if command == "inspect":
190
+ result = mission_cli.inspect(args.input)
191
+ elif command in {"approve-business", "approve-architecture"}:
192
+ if args.data_root is None:
193
+ raise ValueError("--data-root is required for Mission governance")
194
+ result = mission_cli.approve(args.data_root, args.input,
195
+ "business" if command == "approve-business" else "architecture")
196
+ elif command == "admit":
197
+ if args.data_root is None:
198
+ raise ValueError("--data-root is required for Mission admission")
199
+ result = mission_cli.admit(args.data_root, args.input)
200
+ elif command == "status":
201
+ result = mission_cli.status(args.data_root, args.mission_id)
202
+ elif command == "stop":
203
+ result = mission_cli.stop(args.data_root, args.mission_id)
204
+ else:
205
+ if args.data_root is None:
206
+ raise ValueError("--data-root is required for Mission execution")
207
+ result = mission_cli.run(args.data_root, args.mission_id,
208
+ truth_path=args.repository_truth if command == "run" else None,
209
+ poll_seconds=args.poll_seconds, maximum_wait_seconds=args.maximum_wait_seconds)
210
+ print(json.dumps(result, sort_keys=True))
211
+ if command == "inspect":
212
+ return 0 if result["status"] == "VALID" else 2
213
+ if command == "stop":
214
+ return 0 if result["stop_requested"] else 2
215
+ if command in {"run", "reopen"}:
216
+ return 0 if result["status"] == "COMPLETED" else 2
217
+ return 0
218
+ except (OSError, ValueError, KeyError, sqlite3.Error, RuntimeError, PermissionError) as error:
219
+ return _failure("mission " + args.mission_command, error)
162
220
  if args.command == "server" and args.server_command == "init":
163
221
  from .runtime import RuntimeBootstrap
164
222
  database = RuntimeBootstrap(data_root=args.data_root, forge_version=canonical_version()).open()
@@ -0,0 +1,164 @@
1
+ """Interpret EP-owned validation records without executing a command."""
2
+ from __future__ import annotations
3
+
4
+ from hashlib import sha256
5
+ import json
6
+ import re
7
+ from typing import Any, Mapping
8
+
9
+ from forge.models.criterion_observation import CriterionObservation, canonical_digest
10
+ from forge.models.mission_completion import mission_criterion_id
11
+
12
+
13
+ _DIGEST = re.compile(r"sha256:[0-9a-f]{64}\Z")
14
+
15
+
16
+ def _canonical(value: object) -> str:
17
+ return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False, allow_nan=False)
18
+
19
+
20
+ def _valid_digest(value: object) -> bool:
21
+ return isinstance(value, str) and _DIGEST.fullmatch(value) is not None
22
+
23
+
24
+ def approved_observation_selector(requirement) -> str | None:
25
+ """Identify an EP unittest observation from its exact approved binding."""
26
+ try:
27
+ command = json.loads(requirement.command)
28
+ except (TypeError, ValueError):
29
+ return None
30
+ if (not isinstance(command, list) or len(command) != 4
31
+ or command[:3] != ["{python}", "-m", "unittest"]
32
+ or not isinstance(command[3], str)):
33
+ return None
34
+ selector = command[3]
35
+ if (len(selector) > 100 or re.fullmatch(
36
+ r"[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)+", selector) is None):
37
+ return None
38
+ validation_id = "unittest_selector_" + sha256(selector.encode("utf-8")).hexdigest()[:16]
39
+ if (requirement.validation_id != validation_id
40
+ or requirement.control_identity != "python3 -m unittest " + selector
41
+ or requirement.control_category != "repository"
42
+ or requirement.validation_profile_version != "1.0"
43
+ or requirement.profile_reference != "validation-profile-registry:FULL@1.0"):
44
+ return None
45
+ return selector
46
+
47
+
48
+ def _control_matches(requirement, context: Mapping[str, Any], run_id: str,
49
+ validated_revision: str | None) -> tuple[bool, str, dict[str, object] | None]:
50
+ if not requirement.control_definition_digest:
51
+ return False, "APPROVED_CONTROL_DEFINITION_MISSING", None
52
+ version = context.get("contract_version")
53
+ if version not in {"1.0", "1.1"} or context.get("status") != "AVAILABLE":
54
+ return False, "HOST_CONTROL_EVIDENCE_UNAVAILABLE", None
55
+ currentness = context.get("currentness")
56
+ if (validated_revision is None or context.get("candidate_sha") != validated_revision
57
+ or context.get("profile_currentness_conflict") is not False
58
+ or not isinstance(currentness, int) or isinstance(currentness, bool) or currentness < 0
59
+ or not _valid_digest(context.get("profile_digest"))):
60
+ return False, "HOST_CONTROL_CANDIDATE_OR_PROFILE_MISMATCH", None
61
+ if (context.get("validation_profile_version") != requirement.validation_profile_version
62
+ or context.get("profile_reference") != requirement.profile_reference):
63
+ return False, "HOST_CONTROL_PROFILE_VERSION_MISMATCH", None
64
+ controls = context.get("controls")
65
+ required = context.get("required_validation_controls")
66
+ if not isinstance(controls, Mapping) or not isinstance(required, list):
67
+ return False, "HOST_CONTROL_BINDINGS_INVALID", None
68
+ observation_controls = context.get("observation_validation_controls", []) if version == "1.1" else []
69
+ if not isinstance(observation_controls, list):
70
+ return False, "HOST_CONTROL_BINDINGS_INVALID", None
71
+ required_binding = required.count(requirement.validation_id) == 1
72
+ observation_matches = [item for item in observation_controls
73
+ if isinstance(item, Mapping)
74
+ and item.get("validation_id") == requirement.validation_id]
75
+ observation_binding = (version == "1.1" and len(observation_matches) == 1
76
+ and approved_observation_selector(requirement) is not None)
77
+ if required_binding == observation_binding:
78
+ return False, "HOST_CONTROL_BINDING_MISSING_OR_DUPLICATE", None
79
+ command_identity = json.loads(requirement.command)
80
+ definition = {"validation_profile_version": context["validation_profile_version"],
81
+ "profile_reference": context["profile_reference"],
82
+ "validation_id": requirement.validation_id,
83
+ "category": requirement.control_category,
84
+ "control_identity": requirement.control_identity,
85
+ "command_identity": command_identity}
86
+ digest = "sha256:" + sha256(_canonical(definition).encode("utf-8")).hexdigest()
87
+ if digest != requirement.control_definition_digest:
88
+ return False, "HOST_CONTROL_DEFINITION_MISMATCH", None
89
+ control = (controls.get(requirement.validation_id) if required_binding
90
+ else observation_matches[0])
91
+ if not isinstance(control, Mapping):
92
+ return False, "HOST_CONTROL_RECORD_MISSING", None
93
+ if (control.get("validation_id") != requirement.validation_id
94
+ or control.get("category") != requirement.control_category
95
+ or control.get("control_identity") != requirement.control_identity
96
+ or control.get("control_definition_digest") != digest
97
+ or control.get("required_for_profile") is not required_binding
98
+ or control.get("currentness") != currentness):
99
+ return False, "HOST_CONTROL_RECORD_CONFLICT", None
100
+ if (control.get("execution_status") != "EXECUTED" or control.get("result") != "PASS"
101
+ or control.get("evidence_authority") != "command_terminal"
102
+ or control.get("evidence_ref") != "command_terminal"
103
+ or control.get("exit_code") != 0
104
+ or not isinstance(control.get("command_id"), str) or not control["command_id"]):
105
+ return False, "HOST_CONTROL_NOT_EXECUTED_AND_PASSED", None
106
+ detail = control.get("result_detail")
107
+ if not isinstance(detail, Mapping):
108
+ return False, "HOST_CONTROL_RESULT_DETAIL_MISSING", None
109
+ if (detail.get("status") != "AVAILABLE" or detail.get("capture_status") != "AVAILABLE"
110
+ or detail.get("schema") != "deterministic-validation-result-detail-v1"
111
+ or detail.get("run_id") != run_id
112
+ or detail.get("command_id") != control["command_id"]
113
+ or detail.get("validation_id") != requirement.validation_id
114
+ or detail.get("exit_code") != 0
115
+ or not isinstance(detail.get("artifact_id"), str) or not detail["artifact_id"]
116
+ or not _valid_digest(detail.get("digest")) or not _valid_digest(detail.get("output_digest"))):
117
+ return False, "HOST_CONTROL_RESULT_DETAIL_INVALID", None
118
+ count = detail.get("test_count")
119
+ minimum_test_count = (max(1, requirement.minimum_test_count) if observation_binding
120
+ else requirement.minimum_test_count)
121
+ if minimum_test_count and (not isinstance(count, int) or isinstance(count, bool)
122
+ or count < minimum_test_count
123
+ or detail.get("test_count_source") != "unittest_terminal_summary"):
124
+ return False, "HOST_CONTROL_TEST_COUNT_INSUFFICIENT", None
125
+ return True, "APPROVED_HOST_CONTROL_EXECUTED_AND_PASSED", {
126
+ "validation_id": requirement.validation_id, "control_identity": requirement.control_identity,
127
+ "control_definition_digest": digest, "validation_profile_version": requirement.validation_profile_version,
128
+ "profile_reference": requirement.profile_reference, "category": requirement.control_category,
129
+ "candidate_sha": validated_revision, "run_id": run_id, "command_id": control["command_id"],
130
+ "result_detail_digest": detail["digest"], "output_digest": detail["output_digest"],
131
+ "test_count": count, "execution_status": "EXECUTED", "result": "PASS", "exit_code": 0,
132
+ }
133
+
134
+
135
+ class HostControlCriterionObserver:
136
+ def observe(self, mission, reference, evidence) -> tuple[CriterionObservation, ...]:
137
+ context = evidence.validation_controls
138
+ observations = []
139
+ for contract in mission.criterion_assessment_contracts:
140
+ for requirement in contract.requirements:
141
+ if requirement.kind != "host_control":
142
+ continue
143
+ control_context = context if isinstance(context, Mapping) else {}
144
+ delivered = reference.repository_revision
145
+ candidate = reference.candidate_revision
146
+ if (contract.validity_policy == "current_revision" and candidate != delivered
147
+ and control_context.get("candidate_sha") == candidate):
148
+ valid, reason, measured = False, "HOST_CONTROL_DELIVERY_REVISION_NOT_VALIDATED", None
149
+ else:
150
+ expected_revision = (delivered if contract.validity_policy == "current_revision" else
151
+ delivered if control_context.get("candidate_sha") == delivered else candidate)
152
+ valid, reason, measured = _control_matches(
153
+ requirement, control_context, evidence.host_run_id, expected_revision)
154
+ observations.append(CriterionObservation(
155
+ mission.id, canonical_digest(mission.to_dict()), mission_criterion_id(mission.id, contract.criterion),
156
+ contract.digest, requirement.requirement_id, reference.receipt_id, reference.action_id,
157
+ reference.report_id, reference.repository_revision, reference.repository_evidence_digest,
158
+ "host_control", requirement.control_identity, "ep-terminal-artifact",
159
+ reference.repository_evidence_digest if valid else None,
160
+ _canonical(measured) if valid else None, "PASS" if valid else "UNAVAILABLE", reason,
161
+ requirement_digest=requirement.digest, candidate_revision=reference.candidate_revision,
162
+ schema_version="1.1",
163
+ ))
164
+ return tuple(observations)
@@ -67,6 +67,34 @@ def _canonical_execution_references(
67
67
  return references
68
68
 
69
69
 
70
+ def _host_control_observation_matches(observation, requirement, validity_policy: str) -> bool:
71
+ if (observation.schema_version != "1.1" or observation.result != "PASS"
72
+ or observation.reason != "APPROVED_HOST_CONTROL_EXECUTED_AND_PASSED"
73
+ or observation.content_digest != observation.repository_evidence_digest
74
+ or observation.observed_json is None):
75
+ return False
76
+ try:
77
+ record = json.loads(observation.observed_json)
78
+ except (TypeError, ValueError):
79
+ return False
80
+ return (isinstance(record, dict)
81
+ and record.get("validation_id") == requirement.validation_id
82
+ and record.get("control_identity") == requirement.control_identity
83
+ and record.get("control_definition_digest") == requirement.control_definition_digest
84
+ and record.get("validation_profile_version") == requirement.validation_profile_version
85
+ and record.get("profile_reference") == requirement.profile_reference
86
+ and record.get("category") == requirement.control_category
87
+ and (record.get("candidate_sha") == observation.repository_revision
88
+ if validity_policy == "current_revision" else
89
+ record.get("candidate_sha") in {observation.candidate_revision, observation.repository_revision})
90
+ and record.get("execution_status") == "EXECUTED"
91
+ and record.get("result") == "PASS" and record.get("exit_code") == 0
92
+ and (requirement.minimum_test_count == 0 or
93
+ isinstance(record.get("test_count"), int)
94
+ and not isinstance(record["test_count"], bool)
95
+ and record["test_count"] >= requirement.minimum_test_count))
96
+
97
+
70
98
  class MissionCompletionEvaluator:
71
99
  """Interpret approved predicates over Forge-observed facts; never provider PASS."""
72
100
 
@@ -132,7 +160,31 @@ class MissionCompletionEvaluator:
132
160
  if contract.validity_policy == "current_revision":
133
161
  candidates = [obs for obs in candidates if obs.repository_revision == truth.revision]
134
162
  source = mission.repository_evidence_source
135
- if requirement.kind != "repository_json":
163
+ if requirement.kind == "host_control":
164
+ if not requirement.control_definition_digest:
165
+ result, why, matched = "UNSATISFIED", "UNSUPPORTED_AUTHORITATIVE_EVIDENCE_SOURCE", ()
166
+ elif any(obs.source_kind != "host_control" or obs.source_identity != requirement.control_identity
167
+ or obs.artifact_path != "ep-terminal-artifact"
168
+ or obs.requirement_digest != requirement.digest or obs.json_pointer != ""
169
+ for obs in candidates):
170
+ result, why, matched = "UNSATISFIED", "OBSERVATION_SOURCE_MISMATCH", ()
171
+ elif not candidates:
172
+ result, why, matched = "UNSATISFIED", "CURRENT_OBSERVATION_MISSING", ()
173
+ else:
174
+ if contract.validity_policy == "historical_delivery":
175
+ passing = [obs for index, obs in enumerate(candidates)
176
+ if _host_control_observation_matches(obs, requirement, contract.validity_policy)
177
+ and not any(later.repository_revision == obs.repository_revision
178
+ and not _host_control_observation_matches(later, requirement, contract.validity_policy)
179
+ for later in candidates[index + 1:])]
180
+ selected = passing[-1] if passing else candidates[-1]
181
+ else:
182
+ selected = candidates[-1]
183
+ proven = _host_control_observation_matches(selected, requirement, contract.validity_policy)
184
+ result = "PROVEN" if proven else "UNSATISFIED"
185
+ why = "APPROVED_HOST_CONTROL_EXECUTED_AND_PASSED" if proven else selected.reason
186
+ matched = (selected.id,)
187
+ elif requirement.kind != "repository_json":
136
188
  result, why, matched = "UNSATISFIED", "UNSUPPORTED_AUTHORITATIVE_EVIDENCE_SOURCE", ()
137
189
  elif source is None:
138
190
  result, why, matched = "UNSATISFIED", "APPROVED_REPOSITORY_SOURCE_MISSING", ()
@@ -19,7 +19,7 @@ from forge.dispatcher import MissionDispatcher
19
19
  from forge.governance import ApprovalRecord, ExecutionPolicy, ExecutionPolicyKind, PauseBoundary, execution_policy_for_profile
20
20
  from forge.models.action import EngineeringAction, EngineeringActionStatus
21
21
  from forge.models.architecture_mission import ArchitectureMission
22
- from forge.models.execution_host import ExecutionHost, ExecutionHostEvidence
22
+ from forge.models.execution_host import ExecutionEvidenceOutcome, ExecutionHost, ExecutionHostEvidence
23
23
  from forge.models.producer import RepositoryRevisionBinding
24
24
  from forge.models.mission_completion import (MissionCompletionEvidence,
25
25
  MissionCriterionEvaluationStatus)
@@ -134,6 +134,7 @@ class ExecutionLoop:
134
134
  completion_evaluator: MissionCompletionEvaluator | None = None,
135
135
  runtime_database: RuntimeDatabase | None = None,
136
136
  repository_revision_binding_factory: RepositoryRevisionBindingFactory | None = None,
137
+ keep_running: Callable[[], bool] | None = None,
137
138
  ) -> None:
138
139
  if not all((host_id, workspace_id, repository_id)):
139
140
  raise ExecutionLoopError("execution host, workspace, and repository identities are required")
@@ -150,14 +151,19 @@ class ExecutionLoop:
150
151
  self._completion_evaluator = completion_evaluator or MissionCompletionEvaluator()
151
152
  self._runtime_database = runtime_database
152
153
  self._repository_revision_binding_factory = repository_revision_binding_factory
154
+ self._keep_running = keep_running or (lambda: True)
153
155
 
154
156
  def run(self) -> MissionExecutionState | None:
155
157
  """Run the one dispatched Mission until terminal or awaiting host evidence."""
158
+ if not self._keep_running():
159
+ return None
156
160
  record = self._dispatcher.dispatch()
157
161
  if record is None:
158
162
  return None
159
163
  state = self._states.get(record.mission_id)
160
164
  state = self._states.set_execution_policy(state.mission_id, self._execution_policy.to_dict(), occurred_at=self._clock())
165
+ if not self._keep_running():
166
+ return state
161
167
  if state.status is MissionExecutionStatus.CREATED:
162
168
  try:
163
169
  state = self._plan(state)
@@ -209,6 +215,8 @@ class ExecutionLoop:
209
215
  approval: ApprovalRecord | None = None) -> MissionExecutionState:
210
216
  """Resume durable work; a blocked or failed Action needs explicit authority."""
211
217
  state = self._states.get(mission_id)
218
+ if not self._keep_running():
219
+ return state
212
220
  if state.status is MissionExecutionStatus.AWAITING_APPROVAL:
213
221
  if approval is None:
214
222
  raise ExecutionLoopError("governance-paused Mission requires an approval record")
@@ -241,6 +249,27 @@ class ExecutionLoop:
241
249
  raise ExecutionLoopError("recovery authorization is valid only for blocked or failed Missions")
242
250
  return self.run() if self._dispatcher.resume() else self._runner().run(mission_id)
243
251
 
252
+ def resume_existing_successor(self, mission_id: str) -> MissionExecutionState:
253
+ """Retry an assessed continuation whose approved next Action already exists."""
254
+ state = self._states.get(mission_id)
255
+ marker = state.resume.get("terminal_continuation")
256
+ remaining = tuple(item for item in state.actions if item["status"] != EngineeringActionStatus.COMPLETE.value)
257
+ if (state.status is not MissionExecutionStatus.BLOCKED
258
+ or state.waiting_reason != "mission_criteria_unmet_no_valid_successor"
259
+ or not isinstance(marker, Mapping) or marker.get("phase") != "ASSESSED"
260
+ or marker.get("mission_complete") is not False
261
+ or not remaining
262
+ or any(item["status"] != EngineeringActionStatus.READY.value for item in remaining)
263
+ or state.execution_evidence is None
264
+ or state.execution_evidence.get("outcome") != ExecutionEvidenceOutcome.COMPLETE.value):
265
+ raise ExecutionLoopError("existing-successor continuation is unavailable for this Mission")
266
+ state = self._states.transition(
267
+ mission_id, MissionExecutionStatus.ACTIVE, occurred_at=self._clock(),
268
+ reason="existing_successor_continuation_requested",
269
+ )
270
+ self._dispatcher.recover(mission_id)
271
+ return self._runner().run(mission_id)
272
+
244
273
  def approve_delegation(self, mission_id: str, delegation_id: str, approval: ApprovalRecord) -> MissionExecutionState:
245
274
  """Record governance approval for one exact delegation without invoking a provider."""
246
275
  state = self._states.get(mission_id)
@@ -289,6 +318,8 @@ class ExecutionLoop:
289
318
  if input_value.mission.id != state.mission_id:
290
319
  raise ExecutionLoopError("planner input must belong to the active Mission")
291
320
  plan, derivation = self._select_plan(input_value, state)
321
+ if not self._keep_running():
322
+ return state
292
323
  return self._persist_planned_result(state, plan, derivation)
293
324
 
294
325
  def resume_authorized_next_derivation(
@@ -333,6 +364,8 @@ class ExecutionLoop:
333
364
 
334
365
  def _persist_planned_result(self, state: MissionExecutionState, plan: MissionPlan,
335
366
  derivation: Mapping[str, Any] | None) -> MissionExecutionState:
367
+ if not self._keep_running():
368
+ return state
336
369
  actions = tuple(action for intent in plan.intents for action in intent.actions)
337
370
  if not actions:
338
371
  raise ExecutionLoopError("approved Mission planning produced no executable Engineering Actions")
@@ -540,7 +573,8 @@ class ExecutionLoop:
540
573
  clock=self._clock, correlation_id_factory=self._correlation_id_factory,
541
574
  completion_context=completion, replan_after_evidence=self._replan_after_evidence,
542
575
  evidence_progression_gate=self._pause_after_evidence,
543
- repository_revision_binding_factory=self._repository_revision_binding_factory)
576
+ repository_revision_binding_factory=self._repository_revision_binding_factory,
577
+ keep_running=self._keep_running)
544
578
 
545
579
  def _pause_after_evidence(self, state: MissionExecutionState, actions: tuple[EngineeringAction, ...],
546
580
  evidence: ExecutionHostEvidence, mission_complete: bool) -> MissionExecutionState | None:
@@ -596,8 +630,14 @@ class ExecutionLoop:
596
630
  return state
597
631
 
598
632
  self._assert_current_replan_evidence(state, replanning, evidence)
633
+ # An approved successor can already be part of the initial dynamic
634
+ # plan. Continue that immutable Action instead of deriving a duplicate.
635
+ if remaining:
636
+ return state
599
637
  self._assert_successor_bounds(state, replanning.mission)
600
638
  plan, derivation = self._select_plan(replanning, state)
639
+ if not self._keep_running():
640
+ return state
601
641
  assert derivation is not None
602
642
  previous_fingerprints = {
603
643
  item["work_fingerprint"] for record in state.planning_history
@@ -631,6 +671,8 @@ class ExecutionLoop:
631
671
  if isinstance(continuation, Mapping):
632
672
  resume["terminal_continuation"] = {**continuation, "phase": "SUCCESSOR_READY"}
633
673
  try:
674
+ if not self._keep_running():
675
+ return state
634
676
  return self._states.transition(
635
677
  state.mission_id, MissionExecutionStatus.ACTIVE, occurred_at=self._clock(),
636
678
  reason="dynamic_successor_materialized", intents=(*state.intents, *new_intents),
@@ -201,6 +201,7 @@ class ArchitecturePlanningEvidence:
201
201
  maximum_actions: int | None = None
202
202
  maximum_consecutive_no_progress_actions: int | None = None
203
203
  repository_evidence_source: ApprovedRepositoryEvidenceSource | None = None
204
+ mission_spec_digest: str | None = None
204
205
 
205
206
  def __post_init__(self) -> None:
206
207
  if not all((self.scope, self.write_scopes, self.non_goals, self.risk_inputs, self.human_gates,
@@ -211,6 +212,10 @@ class ArchitecturePlanningEvidence:
211
212
  self.criterion_assessment_contracts, self.maximum_actions,
212
213
  self.maximum_consecutive_no_progress_actions, self.repository_evidence_source,
213
214
  ))
215
+ if self.mission_spec_digest is not None and (not isinstance(self.mission_spec_digest, str)
216
+ or len(self.mission_spec_digest) != 71 or not self.mission_spec_digest.startswith("sha256:")
217
+ or any(character not in "0123456789abcdef" for character in self.mission_spec_digest[7:])):
218
+ raise ValueError("Mission specification digest is invalid")
214
219
 
215
220
  def to_dict(self) -> dict[str, object]:
216
221
  value = asdict(self)
@@ -227,6 +232,8 @@ class ArchitecturePlanningEvidence:
227
232
  value.pop("repository_evidence_source")
228
233
  else:
229
234
  value["repository_evidence_source"] = self.repository_evidence_source.to_dict()
235
+ if self.mission_spec_digest is None:
236
+ value.pop("mission_spec_digest")
230
237
  return value
231
238
 
232
239
  @property
@@ -271,6 +278,10 @@ class MissionPlanningEvidenceEnvelope:
271
278
  raise ValueError("approval envelope has invalid, stale, conflicting, or cross-installation lineage")
272
279
  if planning.provenance_revision != subject_revision:
273
280
  raise ValueError("planning evidence revision is stale")
281
+ if planning.mission_spec_digest is not None and (
282
+ tuple(sorted(business.get("scope", ()))) != tuple(sorted(planning.scope))
283
+ or tuple(sorted(business.get("gates", ()))) != tuple(sorted(planning.human_gates))):
284
+ raise ValueError("Architecture planning exceeds or changes the exact Business-approved scope and gates")
274
285
  evidence = architecture.get("evidence")
275
286
  approved_digest = evidence.get("planning_digest") if isinstance(evidence, dict) else None
276
287
  if (planning.criterion_assessment_contracts and approved_digest is None
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from dataclasses import dataclass
5
+ from dataclasses import dataclass, replace
6
6
  import json
7
7
  from typing import Callable, Sequence
8
8
 
@@ -12,6 +12,7 @@ from forge.models.mission import EngineeringMission, MissionStatus
12
12
  from forge.models.architecture_mission import ArchitectureMission, ArchitectureMissionStatus
13
13
  from forge.state import MissionExecutionState, MissionExecutionStatus, MissionStateStore, MissionStateStoreError
14
14
  from forge.governance_authority import CanonicalGovernanceRepository, MissionPlanningEvidenceEnvelope
15
+ from forge.models.criterion_observation import canonical_digest
15
16
  from forge.runtime.database import RuntimeDatabaseError
16
17
 
17
18
 
@@ -50,19 +51,7 @@ class MissionIntake:
50
51
  This is the canonical governance bridge. It preserves the legacy
51
52
  bootstrap admission method below for its separately-versioned contract.
52
53
  """
53
- self.validate_approved_evidence(envelope, repository)
54
- if mission.status is not ArchitectureMissionStatus.APPROVED_FOR_ENGINEERING:
55
- raise MissionIntakeError("Mission Intake requires an engineering-approved Architecture Mission")
56
- planning = envelope.planning
57
- if mission.criterion_assessment_contracts or planning.criterion_assessment_contracts:
58
- if (mission.candidate_id != envelope.subject_id
59
- or tuple(sorted(mission.scope)) != tuple(sorted(planning.scope))
60
- or mission.architecture_review_reference != envelope.architecture_decision_id
61
- or mission.criterion_assessment_contracts != planning.criterion_assessment_contracts
62
- or mission.maximum_actions != planning.maximum_actions
63
- or mission.maximum_consecutive_no_progress_actions != planning.maximum_consecutive_no_progress_actions
64
- or mission.repository_evidence_source != planning.repository_evidence_source):
65
- raise MissionIntakeError("Mission Intake criterion contract differs from canonical Architecture approval")
54
+ self.validate_canonical_mission_contract(mission, envelope, repository)
66
55
  source = "canonical-governance-envelope:" + envelope.digest
67
56
  allocation = repository.database._connection.execute(
68
57
  "SELECT mission_id FROM mission_id_allocations WHERE source = ?", (source,)
@@ -119,6 +108,29 @@ class MissionIntake:
119
108
  raise MissionIntakeError("Mission Intake could not create canonical Mission state") from error
120
109
  return MissionStateStore._decode(json.dumps(document, sort_keys=True, separators=(",", ":")))
121
110
 
111
+ def validate_canonical_mission_contract(
112
+ self, mission: ArchitectureMission, envelope: MissionPlanningEvidenceEnvelope,
113
+ repository: CanonicalGovernanceRepository,
114
+ ) -> None:
115
+ """Check all approval and Mission content before any allocation."""
116
+ self.validate_approved_evidence(envelope, repository)
117
+ if mission.status is not ArchitectureMissionStatus.APPROVED_FOR_ENGINEERING:
118
+ raise MissionIntakeError("Mission Intake requires an engineering-approved Architecture Mission")
119
+ planning = envelope.planning
120
+ if (planning.mission_spec_digest is not None
121
+ and planning.mission_spec_digest != canonical_digest(
122
+ replace(mission, id="MISSION-PREVIEW").to_dict())):
123
+ raise MissionIntakeError("Mission content differs from the exact Architecture-approved specification")
124
+ if mission.criterion_assessment_contracts or planning.criterion_assessment_contracts:
125
+ if (mission.candidate_id != envelope.subject_id
126
+ or tuple(sorted(mission.scope)) != tuple(sorted(planning.scope))
127
+ or mission.architecture_review_reference != envelope.architecture_decision_id
128
+ or mission.criterion_assessment_contracts != planning.criterion_assessment_contracts
129
+ or mission.maximum_actions != planning.maximum_actions
130
+ or mission.maximum_consecutive_no_progress_actions != planning.maximum_consecutive_no_progress_actions
131
+ or mission.repository_evidence_source != planning.repository_evidence_source):
132
+ raise MissionIntakeError("Mission Intake criterion contract differs from canonical Architecture approval")
133
+
122
134
  def admit(
123
135
  self,
124
136
  mission: EngineeringMission,