engineering-platform 2.3.70__tar.gz → 2.3.71__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 (163) hide show
  1. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/PKG-INFO +1 -1
  2. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/pyproject.toml +2 -1
  3. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/ENGINEERING_PLATFORM_CONFIG.json +1 -1
  4. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/ENGINEERING_PLATFORM_VERSION.json +5 -5
  5. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/contracts/projection.py +7 -0
  6. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/execution_finalization.py +4 -1
  7. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/execution_host.py +136 -46
  8. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/execution_lifecycle.py +3 -2
  9. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/execution_reporting.py +4 -1
  10. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/execution_repository.py +13 -3
  11. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/managed_autonomy.py +21 -4
  12. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/platform_version.py +1 -1
  13. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/qualification_runtime.py +71 -17
  14. engineering_platform-2.3.71/src/engineering_platform/reconciliation_adoption.py +186 -0
  15. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/server.py +21 -1
  16. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/storage.py +54 -1
  17. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/templates/workspace-config.json +1 -1
  18. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform.egg-info/PKG-INFO +1 -1
  19. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform.egg-info/SOURCES.txt +1 -0
  20. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform.egg-info/entry_points.txt +1 -0
  21. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/README.md +0 -0
  22. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/setup.cfg +0 -0
  23. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/__init__.py +0 -0
  24. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/__main__.py +0 -0
  25. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/agent_state.py +0 -0
  26. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/agent_trust.py +0 -0
  27. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/assets/dashboard.css +0 -0
  28. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/assets/dashboard.js +0 -0
  29. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/assets/dashboard_locales.mjs +0 -0
  30. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/assets/dashboard_status_store.mjs +0 -0
  31. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/assets/dashboard_translation.mjs +0 -0
  32. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/assets/operations-console/apple-touch-icon-dark.png +0 -0
  33. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/assets/operations-console/apple-touch-icon-light.png +0 -0
  34. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/assets/operations-console/icon-dark.png +0 -0
  35. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/assets/operations-console/icon-light.png +0 -0
  36. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/assets/operations-console/icon-transparent.png +0 -0
  37. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/assets/operations-console/manifest.webmanifest +0 -0
  38. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/capability_preflight.py +0 -0
  39. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/capability_review.py +0 -0
  40. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/central_data_transfer.py +0 -0
  41. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/central_database.py +0 -0
  42. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/central_store_migration.py +0 -0
  43. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/codex_capacity.py +0 -0
  44. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/codex_chat.py +0 -0
  45. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/codex_observability.py +0 -0
  46. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/component_lock.py +0 -0
  47. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/component_logging.py +0 -0
  48. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/console_presentation.py +0 -0
  49. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/console_route_ownership.py +0 -0
  50. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/contracts/__init__.py +0 -0
  51. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/contracts/ep_consumer.py +0 -0
  52. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/contracts/models.py +0 -0
  53. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/dashboard_browser_validation.py +0 -0
  54. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/dashboard_state.py +0 -0
  55. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/dashboard_supervisor.swift +0 -0
  56. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/dashboard_translation.py +0 -0
  57. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/dependabot_producer.py +0 -0
  58. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/development_profile.py +0 -0
  59. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/drift_diagnostics.py +0 -0
  60. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/emergency_recovery.py +0 -0
  61. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/engineering_memory.py +0 -0
  62. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/ep_consumer_credentials.py +0 -0
  63. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/evidence_projection.py +0 -0
  64. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/execution_activity.py +0 -0
  65. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/execution_context.py +0 -0
  66. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/execution_errors.py +0 -0
  67. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/execution_evidence.py +0 -0
  68. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/execution_executor.py +0 -0
  69. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/execution_host_evidence.py +0 -0
  70. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/execution_lease.py +0 -0
  71. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/execution_models.py +0 -0
  72. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/execution_readiness.py +0 -0
  73. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/execution_timeout_policy.py +0 -0
  74. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/execution_timing.py +0 -0
  75. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/execution_transaction.py +0 -0
  76. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/external_producer_binding.py +0 -0
  77. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/file_inbox.py +0 -0
  78. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/forensic_attribution.py +0 -0
  79. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/forensic_attribution_v2.py +0 -0
  80. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/forensic_delta.py +0 -0
  81. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/golden_scenario.py +0 -0
  82. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/historical_dashboard_configuration.py +0 -0
  83. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/host_admin.py +0 -0
  84. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/host_preflight.py +0 -0
  85. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/installation_relocation.py +0 -0
  86. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_activation.py +0 -0
  87. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_admission.py +0 -0
  88. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_backup.py +0 -0
  89. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_composition.py +0 -0
  90. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_executor.py +0 -0
  91. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_migration.py +0 -0
  92. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_operation.py +0 -0
  93. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_plan.py +0 -0
  94. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_preparation.py +0 -0
  95. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/investigation_ledger.py +0 -0
  96. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/legacy_inbox_migration.py +0 -0
  97. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/legacy_installation_adoption.py +0 -0
  98. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/lifecycle_worker.py +0 -0
  99. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/live_status.py +0 -0
  100. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/local_api.py +0 -0
  101. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/local_api_keychain.py +0 -0
  102. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/local_repository_binding.py +0 -0
  103. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/managed_codex_runtime.py +0 -0
  104. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/operational_installation.py +0 -0
  105. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/operational_installation_lock.py +0 -0
  106. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/operational_installation_record.py +0 -0
  107. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/owner_credential_recovery.py +0 -0
  108. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/parity_context.py +0 -0
  109. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/parity_lifecycle_dispatcher.py +0 -0
  110. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/platform_admin.py +0 -0
  111. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/platform_api.py +0 -0
  112. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/platform_bootstrap.py +0 -0
  113. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/platform_components.py +0 -0
  114. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/pr_check_repair.py +0 -0
  115. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/pr_evidence_backfill.py +0 -0
  116. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/producer.py +0 -0
  117. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/product_installation_readback.py +0 -0
  118. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/project_agent.py +0 -0
  119. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/project_agent_service.py +0 -0
  120. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/project_topology.py +0 -0
  121. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/prompt_history.py +0 -0
  122. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/provider_context.py +0 -0
  123. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/provider_context_benchmark.py +0 -0
  124. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/provider_context_scope.py +0 -0
  125. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/provider_interruption.py +0 -0
  126. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/provider_process_identity.py +0 -0
  127. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/provider_readiness.py +0 -0
  128. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/provider_recovery.py +0 -0
  129. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/provider_usage.py +0 -0
  130. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/providers.py +0 -0
  131. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/qualification.py +0 -0
  132. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/recommendation_handoff.py +0 -0
  133. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/release_operation.py +0 -0
  134. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/report_analysis.py +0 -0
  135. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/repository_attachment.py +0 -0
  136. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/repository_handoff.py +0 -0
  137. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/resources.py +0 -0
  138. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/reviewer_evidence.py +0 -0
  139. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/revision_binding.py +0 -0
  140. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/schemas/producer-readback-v1.2.schema.json +0 -0
  141. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/schemas/producer-readback-v1.schema.json +0 -0
  142. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/schemas/repository-attachment.schema.json +0 -0
  143. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/server_console_services.py +0 -0
  144. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/server_relay.py +0 -0
  145. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/server_service.py +0 -0
  146. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/status_model.py +0 -0
  147. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/status_reconciliation.py +0 -0
  148. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/submission_cli.py +0 -0
  149. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/submission_intake.py +0 -0
  150. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/submission_service.py +0 -0
  151. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/system_installation_topology.py +0 -0
  152. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/system_server_service.py +0 -0
  153. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/telemetry.py +0 -0
  154. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/validation_identity.py +0 -0
  155. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/validation_profile.py +0 -0
  156. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/version_preparation_delivery.py +0 -0
  157. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/workspace_preflight.py +0 -0
  158. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/worktree_provenance.py +0 -0
  159. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform/worktree_tooling.py +0 -0
  160. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform.egg-info/dependency_links.txt +0 -0
  161. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/src/engineering_platform.egg-info/top_level.txt +0 -0
  162. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/tests/test_adaptive_action_sizing_design.py +0 -0
  163. {engineering_platform-2.3.70 → engineering_platform-2.3.71}/tests/test_role_task_model_policy_design.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: engineering-platform
3
- Version: 2.3.70
3
+ Version: 2.3.71
4
4
  Summary: Local-first Engineering Platform execution operations runtime
5
5
  Author: Peter van Tol
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "engineering-platform"
7
- version = "2.3.70"
7
+ version = "2.3.71"
8
8
  description = "Local-first Engineering Platform execution operations runtime"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.14,<3.15"
@@ -18,6 +18,7 @@ engineering-platform-host = "engineering_platform.__main__:main"
18
18
  engineering-platform-server = "engineering_platform.server:main"
19
19
  engineering-platform = "engineering_platform.submission_cli:main"
20
20
  engineering-project-agent = "engineering_platform.project_agent:main"
21
+ engineering-reconciliation-adopt = "engineering_platform.reconciliation_adoption:main"
21
22
 
22
23
  [tool.setuptools]
23
24
  package-dir = {"" = "src"}
@@ -3,7 +3,7 @@
3
3
  "platform": {
4
4
  "id": "engineering-platform",
5
5
  "name": "Engineering Platform",
6
- "version": "2.3.70",
6
+ "version": "2.3.71",
7
7
  "generation": 2,
8
8
  "documentation_namespace": "engineering-platform",
9
9
  "capability_registry_version": 1
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "bootstrap_contract": "2026.12",
3
3
  "checkpoint_format": 1,
4
- "dashboard_version": "2.3.70",
4
+ "dashboard_version": "2.3.71",
5
5
  "handoff_protocol": 1,
6
6
  "memory_format": 2,
7
7
  "minimum_codex_cli": "0.146.0",
8
8
  "inbox_protocol": 1,
9
- "platform_version": "2.3.70",
9
+ "platform_version": "2.3.71",
10
10
  "report_format": 2,
11
- "runner_version": "2.3.70",
11
+ "runner_version": "2.3.71",
12
12
  "status_model": 1,
13
- "storage_schema": 44,
14
- "watcher_version": "2.3.70"
13
+ "storage_schema": 45,
14
+ "watcher_version": "2.3.71"
15
15
  }
@@ -306,6 +306,7 @@ def get_run_context(root: Path, run_id: str) -> dict[str, object]:
306
306
  snapshot_checks = qualification_source.get("pr_checks", {}) if qualification_source else {}
307
307
  implementation = snapshot_checks.get("IMPLEMENTATION", {}) if qualification_source and isinstance(snapshot_checks, dict) else checks.get("IMPLEMENTATION", {})
308
308
  finalization = snapshot_checks.get("FINALIZATION", {}) if qualification_source and isinstance(snapshot_checks, dict) else checks.get("FINALIZATION", {})
309
+ reconciliation = snapshot_checks.get("RECONCILIATION", {}) if qualification_source and isinstance(snapshot_checks, dict) else checks.get("RECONCILIATION", {})
309
310
  objective = _safe_objective(submission[3] if submission else {})
310
311
  validation_only = action_intent == "VALIDATION_ONLY"
311
312
  persisted_required_validation = qualification_source.get("validation_profile", UNAVAILABLE) if qualification_source else qualification_validation or UNAVAILABLE
@@ -337,8 +338,14 @@ def get_run_context(root: Path, run_id: str) -> dict[str, object]:
337
338
  "finalization_pr": "NOT_REQUIRED" if validation_only else _value(qualification_source.get("finalization_pr") if qualification_source else checkpoint.get("finalization_pull_request") or finalization.get("pr_number")), "finalization_pr_current_state": "NOT_REQUIRED" if validation_only else _value(finalization.get("pr_state")),
338
339
  "finalization_merge_state": _value(finalization.get("merge_state")), "finalization_merge_commit": _value(finalization.get("merge_commit") if qualification_source else checkpoint.get("finalization_merge_commit") or finalization.get("merge_commit")),
339
340
  "finalization_required_checks_state": _value(finalization.get("required_checks_state")), "finalization_merge_gate": "EXPECTED_OPERATOR_GATE" if phase == "WAIT_FOR_FINALIZATION_MERGE" else UNAVAILABLE,
341
+ "reconciliation_pr": "NOT_REQUIRED" if validation_only else _value(qualification_source.get("reconciliation_pr") if qualification_source else checkpoint.get("reconciliation_pull_request") or reconciliation.get("pr_number")),
342
+ "reconciliation_pr_current_state": "NOT_REQUIRED" if validation_only else _value(reconciliation.get("pr_state")),
343
+ "reconciliation_merge_state": _value(reconciliation.get("merge_state")),
344
+ "reconciliation_required_checks_state": _value(reconciliation.get("required_checks_state")),
345
+ "reconciliation_merge_gate": "EXPECTED_OPERATOR_GATE" if phase == "WAIT_FOR_RECONCILIATION_MERGE" else UNAVAILABLE,
340
346
  "implementation_delivery": _value(qualification_source.get("implementation_delivery")),
341
347
  "finalization_delivery": _value(qualification_source.get("finalization_delivery")),
348
+ "reconciliation_delivery": _value(qualification_source.get("reconciliation_delivery")),
342
349
  "run_delivery_commit": _value(checkpoint.get("implementation_head_sha") or checkpoint.get("last_verified_sha")), "current_repository_head": _value(checkpoint.get("last_verified_sha")), "delivery_commit_head_relationship": UNAVAILABLE},
343
350
  "validation": {"engineering_platform_qualification": UNAVAILABLE, "controls": validation_controls,
344
351
  "required_validation": persisted_required_validation,
@@ -33,7 +33,10 @@ class FinalizationCoordinator:
33
33
  if not callable(operation):
34
34
  return save_terminal(cleanup, "BLOCKED", "cleanup_unavailable", "Cleanup client is unavailable; resume with repository cleanup evidence.")
35
35
  try:
36
- result = operation(root, (cleanup.implementation_branch, cleanup.finalization_branch))
36
+ branches = (cleanup.implementation_branch, cleanup.finalization_branch)
37
+ if cleanup.transaction_kind == "RECONCILIATION":
38
+ branches += (cleanup.branch,)
39
+ result = operation(root, branches)
37
40
  except RunnerError as error:
38
41
  return save_terminal(cleanup, "BLOCKED", "repository_cleanup_required", str(error))
39
42
  return save_terminal(
@@ -82,7 +82,7 @@ from .reviewer_evidence import ReviewerEvidence
82
82
  from .investigation_ledger import InvocationInvestigationLedger
83
83
  from .execution_errors import CodexHandoffTimeout, CodexInvocationError, RunnerError
84
84
  from .execution_errors import ProviderReadinessBlocked
85
- from .execution_timeout_policy import FINALIZATION, REPAIR, agent_timeout
85
+ from .execution_timeout_policy import END_RECONCILIATION, FINALIZATION, REPAIR, agent_timeout
86
86
  from .provider_readiness import failures as provider_readiness_failures
87
87
  from .execution_repository import GitHubClient as ProviderGitHubClient, RepositoryClient as ProviderRepositoryClient
88
88
  from .execution_repository import GhCliClient as ProviderGhCliClient, SubprocessRepositoryClient as ProviderRepositoryClientImpl
@@ -125,6 +125,7 @@ from .component_logging import component_logger, record_technical_diagnostic, sh
125
125
 
126
126
  # Compatibility exports for integrations that already import these names.
127
127
  FINALIZATION_PR_HANDOFF_MAX_SECONDS = FINALIZATION.seconds
128
+ RECONCILIATION_PR_HANDOFF_MAX_SECONDS = END_RECONCILIATION.seconds
128
129
  REPAIR_AGENT_MAX_SECONDS = REPAIR.seconds
129
130
 
130
131
  # A repair remains scoped to its original PR, but it must also have a finite
@@ -305,11 +306,12 @@ def assemble_prompt(
305
306
  )
306
307
  authority = (
307
308
  """This is the sole automatic post-Finalization reconciliation. You may only update
308
- the four canonical rolling records, commit them directly to the already synchronized `main`,
309
- and push that one commit. Do not create a branch or pull request. Do not change runtime code,
310
- authority, lifecycle, retry, validation, provider, Forge, queue, or delivery semantics. Return
311
- `COMPLETE`, `repository_reconciled`, and the pushed main commit SHA only after verifying a clean
312
- workspace and that `main` contains that exact commit."""
309
+ the four canonical rolling records on the exact checkpointed reconciliation branch, commit and
310
+ push that branch, and create one draft pull request to `main`. Do not change runtime code,
311
+ authority, lifecycle, retry, validation, provider, Forge, queue, or delivery semantics. Do not
312
+ merge the pull request or push directly to `main`. Return `COMPLETE`, `repository_reconciled`,
313
+ the branch, pull-request number, and exact candidate commit SHA only after verifying a clean
314
+ workspace."""
313
315
  if state and state.transaction_kind == "RECONCILIATION"
314
316
  else
315
317
  """The runner holds explicit owner authorization for this exact bounded transaction. You may create, commit and push one bounded branch and draft pull request, or repair that same pull request. The runner may mark that pull request ready for review, but only the human operator may merge it. Do not merge, release, deploy, tag, publish, upload, change repository settings, bypass protection, or expand the objective."""
@@ -1090,7 +1092,7 @@ class EngineeringRunner:
1090
1092
  def _managed_pr_check(self, state: TransactionState, pr: PullRequestEvidence) -> None:
1091
1093
  """Persist current GitHub required-check evidence separately from historical waits."""
1092
1094
  role = state.transaction_kind
1093
- if role not in {"IMPLEMENTATION", "FINALIZATION"}:
1095
+ if role not in {"IMPLEMENTATION", "FINALIZATION", "RECONCILIATION"}:
1094
1096
  return
1095
1097
  check_state = "PASS" if pr.checks_terminal and pr.checks_passed else "FAIL" if pr.checks_terminal else "WAITING"
1096
1098
  try:
@@ -2731,6 +2733,8 @@ First implementation pull-request publication gate:
2731
2733
  return self._advance_after_repair_agent_result(state, result)
2732
2734
  if lifecycle_phase == "FINALIZE_AGENT":
2733
2735
  return self._advance_after_finalization_agent_result(state, result)
2736
+ if lifecycle_phase == "RECONCILE_AGENT":
2737
+ return self._advance_after_reconciliation_agent_result(state, result)
2734
2738
  return self._save_terminal(state, "BLOCKED", "recovered_provider_phase_invalid", "Recovered provider result has an unsupported lifecycle phase.")
2735
2739
 
2736
2740
  def run(
@@ -2898,7 +2902,7 @@ First implementation pull-request publication gate:
2898
2902
  recovered_resume = (
2899
2903
  isinstance(recovery_snapshot, dict)
2900
2904
  and recovery_snapshot.get("state") == "RECOVERED"
2901
- and recovery_snapshot.get("lifecycle_phase") in {"EXECUTE_AGENT", "QUALITY_CONTROL_AGENT", "REPAIR_AGENT", "FINALIZE_AGENT"}
2905
+ and recovery_snapshot.get("lifecycle_phase") in {"EXECUTE_AGENT", "QUALITY_CONTROL_AGENT", "REPAIR_AGENT", "FINALIZE_AGENT", "RECONCILE_AGENT"}
2902
2906
  )
2903
2907
  try:
2904
2908
  persisted_submission = load_submission_for_run(self.root, state.run_id, central_database=self.store.central_database)
@@ -3281,6 +3285,8 @@ First implementation pull-request publication gate:
3281
3285
  state = self._record_agent_execution_time(state)
3282
3286
  self.console_detail = error.console_detail
3283
3287
  return self._terminalize_provider_invocation_error(state, error)
3288
+ if state.transaction_kind == "RECONCILIATION":
3289
+ return self._advance_after_reconciliation_agent_result(state, result)
3284
3290
  return self._advance_after_primary_agent_result(state, result, evidence)
3285
3291
 
3286
3292
  def _active_genesis_transaction(self, target: Path, run_id: str) -> str | None:
@@ -3397,10 +3403,7 @@ First implementation pull-request publication gate:
3397
3403
  next_action="create_finalization",
3398
3404
  )
3399
3405
  if state.transaction_kind == "RECONCILIATION" and not state.reconciliation_pull_request:
3400
- return replace(
3401
- state, phase="RECONCILE_AGENT", last_verified_sha=evidence.head_sha,
3402
- next_action="create_reconciliation",
3403
- )
3406
+ return self._recover_reconciliation_pull_request(state, evidence)
3404
3407
  if (
3405
3408
  state.transaction_kind == "FINALIZATION"
3406
3409
  and state.finalization_merge_commit
@@ -3478,6 +3481,54 @@ First implementation pull-request publication gate:
3478
3481
  write_live_status(self.root, recovered, recovered.next_action)
3479
3482
  return self._poll(recovered)
3480
3483
 
3484
+ def _recover_reconciliation_pull_request(
3485
+ self, state: TransactionState, evidence: RepositoryEvidence,
3486
+ ) -> TransactionState:
3487
+ """Recover only the PR bound to the durable reconciliation branch."""
3488
+ if state.reconciliation_pull_request:
3489
+ return replace(
3490
+ state, pull_request=state.reconciliation_pull_request,
3491
+ phase="WAIT_FOR_TERMINAL_EVIDENCE", next_action="poll_required_checks",
3492
+ )
3493
+ if (
3494
+ not state.branch
3495
+ or state.branch == "main"
3496
+ or not state.finalization_merge_commit
3497
+ or evidence.branch not in {"main", state.branch}
3498
+ or not evidence.clean
3499
+ or not self.repository.main_contains(self.root, state.finalization_merge_commit)
3500
+ ):
3501
+ return self._save_terminal(
3502
+ state, "BLOCKED", "reconciliation_recovery_evidence_required",
3503
+ "Reconciliation recovery requires its checkpointed branch and a verified Finalization merge.",
3504
+ )
3505
+ candidate = self.github.pull_request_for_head_branch(state.branch)
3506
+ if candidate is None:
3507
+ return self._save_terminal(
3508
+ state, "BLOCKED", "reconciliation_recovery_evidence_required",
3509
+ "No reconciliation pull request matches the checkpointed branch; no replacement was created.",
3510
+ )
3511
+ if (
3512
+ candidate.head_branch != state.branch
3513
+ or candidate.base_branch != "main"
3514
+ or candidate.state not in {"OPEN", "MERGED"}
3515
+ ):
3516
+ return self._save_terminal(
3517
+ state, "BLOCKED", "reconciliation_recovery_evidence_invalid",
3518
+ "Reconciliation pull request evidence does not match the checkpointed branch and main base.",
3519
+ )
3520
+ recovered = replace(
3521
+ state, pull_request=candidate.number,
3522
+ reconciliation_pull_request=candidate.number,
3523
+ phase="WAIT_FOR_TERMINAL_EVIDENCE", next_action="poll_required_checks",
3524
+ last_verified_sha=evidence.head_sha,
3525
+ latest_repository_evidence=_repository_summary(evidence),
3526
+ latest_github_evidence=_pull_request_summary(candidate),
3527
+ )
3528
+ self.store.save(recovered)
3529
+ write_live_status(self.root, recovered, recovered.next_action)
3530
+ return self._poll(recovered)
3531
+
3481
3532
  def _poll(self, state: TransactionState, result: AgentResult | None = None) -> TransactionState:
3482
3533
  if state.pull_request:
3483
3534
  state = self._provider_readiness_gate(
@@ -3497,32 +3548,10 @@ First implementation pull-request publication gate:
3497
3548
  "Agent result referenced a pull request without a transaction branch; the current main branch cannot be reused as execution evidence.",
3498
3549
  )
3499
3550
  if not state.pull_request:
3500
- if state.transaction_kind == "RECONCILIATION" and result and result.pull_request:
3551
+ if state.transaction_kind == "RECONCILIATION" and result:
3501
3552
  return self._save_terminal(
3502
- state, "BLOCKED", "reconciliation_pull_request_forbidden",
3503
- "Automatic reconciliation must commit directly to main and must not create a pull request.",
3504
- )
3505
- if state.transaction_kind == "RECONCILIATION" and result and result.terminal_state == "COMPLETE":
3506
- evidence = self.repository.inspect(self.root)
3507
- if (
3508
- result.terminal_condition == "repository_reconciled"
3509
- and result.commit_sha
3510
- and evidence.clean
3511
- and evidence.branch == "main"
3512
- and evidence.head_sha == result.commit_sha
3513
- and evidence.main_contains_head
3514
- ):
3515
- reconciled = replace(state, last_verified_sha=result.commit_sha)
3516
- reconciled = self._append_verified_commit_evidence(
3517
- reconciled,
3518
- phase="RECONCILE_AGENT",
3519
- commit_sha=result.commit_sha,
3520
- description="end_reconciliation_commit_verified",
3521
- )
3522
- return self._cleanup(reconciled)
3523
- return self._save_terminal(
3524
- state, "BLOCKED", "automatic_reconciliation_evidence_required",
3525
- "Automatic reconciliation requires a clean main checkout containing its reported commit.",
3553
+ state, "BLOCKED", "reconciliation_pr_required",
3554
+ "Automatic reconciliation requires its checkpointed pull request before cleanup.",
3526
3555
  )
3527
3556
  if result and result.terminal_state == "COMPLETE":
3528
3557
  evidence = self.repository.inspect(self.root)
@@ -3885,12 +3914,7 @@ First implementation pull-request publication gate:
3885
3914
  return self._poll(finalization, result)
3886
3915
 
3887
3916
  def _start_automatic_reconciliation(self, state: TransactionState) -> TransactionState:
3888
- """Apply the bounded rolling-record update after Finalization merges.
3889
-
3890
- This is deliberately a direct, post-merge `main` commit: it has no PR,
3891
- review, approval, or operator merge boundary. Its scope is enforced by
3892
- the supplied reconciliation prompt and the exact commit evidence below.
3893
- """
3917
+ """Apply the bounded rolling-record update through a protected PR."""
3894
3918
  synchronize = getattr(self.repository, "synchronize_main", None)
3895
3919
  if callable(synchronize):
3896
3920
  synchronize(self.root)
@@ -3900,22 +3924,37 @@ First implementation pull-request publication gate:
3900
3924
  state,
3901
3925
  "End reconciliation is waiting for a clean, synchronized main checkout.",
3902
3926
  )
3927
+ expected_branch = (
3928
+ state.branch
3929
+ if state.transaction_kind == "RECONCILIATION" and state.branch != "main"
3930
+ else None
3931
+ ) or f"codex/reconcile-{state.run_id}"
3903
3932
  reconciliation = replace(
3904
3933
  state, phase="RECONCILE_AGENT", transaction_kind="RECONCILIATION",
3905
- branch=None, pull_request=None, reconciliation_pull_request=None,
3906
- next_action="reconcile_rolling_records_on_main", waiting_for_merge_since=None,
3934
+ branch=expected_branch, pull_request=None, reconciliation_pull_request=None,
3935
+ next_action="create_reconciliation_pull_request", waiting_for_merge_since=None,
3907
3936
  latest_repository_evidence=_repository_summary(evidence),
3908
3937
  )
3909
3938
  self._managed_action(reconciliation, "AUTOMATIC_RECONCILIATION")
3910
3939
  self.store.save(reconciliation)
3911
3940
  write_live_status(self.root, reconciliation, reconciliation.next_action)
3941
+ reconciliation_span = self._start_phase(state.run_id, "RECONCILIATION")
3942
+ handoff_started = time.monotonic()
3943
+ set_handoff_deadline = getattr(self.agent, "set_handoff_deadline_callback", None)
3944
+ if callable(set_handoff_deadline):
3945
+ set_handoff_deadline(
3946
+ lambda: time.monotonic() - handoff_started
3947
+ >= RECONCILIATION_PR_HANDOFF_MAX_SECONDS
3948
+ )
3912
3949
  try:
3913
3950
  result = self._invoke_agent_with_timing(
3914
3951
  reconciliation,
3915
3952
  assemble_prompt(
3916
3953
  Path(reconciliation.prompt_path), reconciliation,
3917
3954
  managed_target=self.root if reconciliation.execution_mode == "MANAGED" else None,
3918
- ) + "\n\nReconcile only the four canonical rolling current-state records after the verified Finalization merge. Preserve immutable Prompt History.",
3955
+ )
3956
+ + "\n\nReconcile only the four canonical rolling current-state records after the verified Finalization merge. "
3957
+ f"Preserve immutable Prompt History and create one draft pull request on exactly `{expected_branch}`.",
3919
3958
  )
3920
3959
  reconciliation = self._record_agent_execution_time(reconciliation)
3921
3960
  reconciliation = self._record_validation_evidence(reconciliation, result)
@@ -3926,12 +3965,63 @@ First implementation pull-request publication gate:
3926
3965
  description="end_reconciliation_commit_verified",
3927
3966
  )
3928
3967
  self._persist_agent_usage(reconciliation.run_id)
3968
+ except CodexHandoffTimeout:
3969
+ complete_phase(self.root, reconciliation_span, outcome="FAILED")
3970
+ reconciliation = self._record_agent_execution_time(reconciliation)
3971
+ return self._recover_reconciliation_pull_request(
3972
+ reconciliation, self.repository.inspect(self.root)
3973
+ )
3929
3974
  except ProviderReadinessBlocked as blocked:
3975
+ complete_phase(self.root, reconciliation_span, outcome="BLOCKED")
3930
3976
  return blocked.state
3931
3977
  except CodexInvocationError as error:
3978
+ complete_phase(
3979
+ self.root, reconciliation_span,
3980
+ outcome="INTERRUPTED" if error.provider_turn_interrupted else "FAILED",
3981
+ )
3932
3982
  reconciliation = self._record_agent_execution_time(reconciliation)
3933
3983
  self.console_detail = error.console_detail
3934
3984
  return self._terminalize_provider_invocation_error(reconciliation, error)
3985
+ finally:
3986
+ if callable(set_handoff_deadline):
3987
+ set_handoff_deadline(None)
3988
+ complete_phase(self.root, reconciliation_span)
3989
+ return self._advance_after_reconciliation_agent_result(reconciliation, result)
3990
+
3991
+ def _advance_after_reconciliation_agent_result(
3992
+ self, reconciliation: TransactionState, result: AgentResult,
3993
+ ) -> TransactionState:
3994
+ """Bind a live or recovered reconciliation result to its one PR."""
3995
+ if result.terminal_state in {"BLOCKED", "FAILED"} or not result.pull_request:
3996
+ return self._save_terminal(
3997
+ reconciliation,
3998
+ result.terminal_state
3999
+ if result.terminal_state in {"BLOCKED", "FAILED"}
4000
+ else "BLOCKED",
4001
+ "reconciliation_pr_required",
4002
+ result.diagnostic or "Reconciliation pull request was not created.",
4003
+ )
4004
+ expected_branch = reconciliation.branch
4005
+ if not expected_branch or expected_branch == "main" or result.branch != expected_branch:
4006
+ return self._save_terminal(
4007
+ reconciliation, "BLOCKED", "reconciliation_branch_mismatch",
4008
+ "Reconciliation returned a pull request outside the durable reconciliation branch.",
4009
+ )
4010
+ reconciliation = replace(
4011
+ reconciliation,
4012
+ phase="WAIT_FOR_TERMINAL_EVIDENCE",
4013
+ pull_request=result.pull_request,
4014
+ reconciliation_pull_request=result.pull_request,
4015
+ terminal_condition="repository_reconciled",
4016
+ next_action="poll_required_checks",
4017
+ )
4018
+ self.store.save(reconciliation)
4019
+ write_live_status(self.root, reconciliation, reconciliation.next_action)
4020
+ evidence = self.github.pull_request(result.pull_request)
4021
+ if evidence.state == "MERGED":
4022
+ return self._poll(reconciliation, result)
4023
+ self.github.normalize_markdown_body(result.pull_request)
4024
+ self.github.ready(result.pull_request)
3935
4025
  return self._poll(reconciliation, result)
3936
4026
 
3937
4027
  def _save_terminal(
@@ -19,12 +19,12 @@ TERMINAL = frozenset({"COMPLETE", "BLOCKED", "FAILED"})
19
19
  _MANAGED_PATH = (
20
20
  "START", "INITIALIZE", "CAPABILITY_REVIEW", "EXECUTE_AGENT", "LOCAL_REPOSITORY_VALIDATION", "QUALITY_CONTROL_AGENT", "REPAIR_AGENT",
21
21
  "WAIT_FOR_OPERATOR_MERGE", "FINALIZE_AGENT", "WAIT_FOR_FINALIZATION_MERGE",
22
- "RECONCILE_AGENT", "REPOSITORY_CLEANUP", "TERMINAL",
22
+ "RECONCILE_AGENT", "WAIT_FOR_RECONCILIATION_MERGE", "REPOSITORY_CLEANUP", "TERMINAL",
23
23
  )
24
24
  _FINALIZATION_REPAIR_PATH = (
25
25
  "START", "INITIALIZE", "CAPABILITY_REVIEW", "EXECUTE_AGENT", "QUALITY_CONTROL_AGENT", "REPAIR_AGENT",
26
26
  "WAIT_FOR_OPERATOR_MERGE", "FINALIZE_AGENT", "FINALIZATION_REPAIR_AGENT", "WAIT_FOR_FINALIZATION_MERGE",
27
- "RECONCILE_AGENT", "REPOSITORY_CLEANUP", "TERMINAL",
27
+ "RECONCILE_AGENT", "WAIT_FOR_RECONCILIATION_MERGE", "REPOSITORY_CLEANUP", "TERMINAL",
28
28
  )
29
29
  # Genesis has no pull-request merge boundary. This is presentation of the
30
30
  # existing mode contract, not a new execution sequence.
@@ -52,6 +52,7 @@ _STEP_PHASES = {
52
52
  "WAIT_FOR_OPERATOR_MERGE": frozenset({"PR_OR_MERGE", "EXTERNAL_CI_WAIT"}),
53
53
  "FINALIZE_AGENT": frozenset({"REPOSITORY_FINALIZATION", "FINALIZATION"}),
54
54
  "RECONCILE_AGENT": frozenset({"RECONCILIATION", "REPORT_GENERATION", "EVIDENCE_PERSISTENCE"}),
55
+ "WAIT_FOR_RECONCILIATION_MERGE": frozenset({"PR_OR_MERGE", "EXTERNAL_CI_WAIT"}),
55
56
  "REPOSITORY_CLEANUP": frozenset({"REPOSITORY_CLEANUP"}),
56
57
  "TERMINAL": frozenset({"TOTAL_EXECUTION"}),
57
58
  }
@@ -397,6 +397,7 @@ def _commit_strategy(state: TransactionState, bundle: TerminalEvidenceBundle) ->
397
397
  f"- Implementation merge: `{state.implementation_merge_commit or 'not recorded'}`",
398
398
  f"- Finalization PR: `{state.finalization_pull_request or 'not recorded'}`",
399
399
  f"- Finalization merge: `{state.finalization_merge_commit or 'not recorded'}`",
400
+ f"- Reconciliation PR: `{state.reconciliation_pull_request or 'not recorded'}`",
400
401
  )
401
402
 
402
403
 
@@ -1080,6 +1081,7 @@ def _managed_autonomy_projection(root: Path, state: TransactionState, bundle: Te
1080
1081
  snapshot = managed_autonomy_snapshot(
1081
1082
  root, run_id=state.run_id, execution_outcome=state.phase,
1082
1083
  implementation_pr=state.implementation_pull_request, finalization_pr=state.finalization_pull_request,
1084
+ reconciliation_pr=state.reconciliation_pull_request,
1083
1085
  repository_state="MERGED_RECONCILED" if state.phase == "COMPLETE" and (state.finalization_merge_commit or state.action_intent == "VALIDATION_ONLY") else "UNAVAILABLE",
1084
1086
  workspace_state="WORKSPACE_READY" if state.phase == "COMPLETE" and bundle.worktree_state == "clean" else "UNAVAILABLE",
1085
1087
  main_origin_sync="YES" if bundle.target_branch == "main" else "UNAVAILABLE",
@@ -1503,6 +1505,7 @@ def generate_terminal_report(
1503
1505
  "## Pull Requests",
1504
1506
  f"- Implementation: branch `{state.implementation_branch}`, PR `{state.implementation_pull_request}`, merge `{state.implementation_merge_commit}`",
1505
1507
  f"- Finalization: branch `{state.finalization_branch}`, PR `{state.finalization_pull_request}`, merge `{state.finalization_merge_commit}`",
1508
+ f"- Reconciliation: branch `{state.branch if state.transaction_kind == 'RECONCILIATION' else None}`, PR `{state.reconciliation_pull_request}`",
1506
1509
  "",
1507
1510
  *_retry_relationship(state),
1508
1511
  "## Initial Repository Assessment",
@@ -1600,7 +1603,7 @@ def generate_terminal_report(
1600
1603
  "## Metrics",
1601
1604
  f"- Codex CLI execution time: {state.agent_execution_seconds if state.agent_execution_seconds is not None else 'not measured'} seconds",
1602
1605
  f"- Repair iterations: {state.repair_iterations}",
1603
- f"- PRs created: {sum(value is not None for value in (state.implementation_pull_request, state.finalization_pull_request))}",
1606
+ f"- PRs created: {sum(value is not None for value in (state.implementation_pull_request, state.finalization_pull_request, state.reconciliation_pull_request))}",
1604
1607
  f"- Merges performed: {sum(value is not None for value in (state.implementation_merge_commit, state.finalization_merge_commit))}",
1605
1608
  "",
1606
1609
  )
@@ -44,7 +44,9 @@ class GitHubClient(Protocol):
44
44
  def ready(self, number: int) -> None: ...
45
45
  def normalize_markdown_body(self, number: int) -> bool: ...
46
46
  def merge(self, number: int) -> None: ...
47
- def create_or_recover_pull_request(self, branch: str, base: str, title: str, body: str) -> PullRequestEvidence: ...
47
+ def create_or_recover_pull_request(
48
+ self, branch: str, base: str, title: str, body: str, *, draft: bool = False,
49
+ ) -> PullRequestEvidence: ...
48
50
  def qualification_for_exact_head(self, number: int, head_sha: str) -> dict[str, object]: ...
49
51
  def version_preparation_writer(self) -> dict[str, object]: ...
50
52
 
@@ -250,7 +252,9 @@ class GhCliClient:
250
252
  raise RunnerError("Finalization recovery found more than one pull request for its checkpointed branch.")
251
253
  return self.pull_request(numbers[0])
252
254
 
253
- def create_or_recover_pull_request(self, branch: str, base: str, title: str, body: str) -> PullRequestEvidence:
255
+ def create_or_recover_pull_request(
256
+ self, branch: str, base: str, title: str, body: str, *, draft: bool = False,
257
+ ) -> PullRequestEvidence:
254
258
  """Create one bounded PR or recover the sole existing branch identity."""
255
259
  existing = self.pull_request_for_head_branch(branch)
256
260
  if existing is not None:
@@ -258,7 +262,13 @@ class GhCliClient:
258
262
  raise RunnerError("Version preparation branch already has a pull request for another base.")
259
263
  return existing
260
264
  try:
261
- raw = self._github("pr", "create", "--head", branch, "--base", base, "--title", title, "--body", body)
265
+ arguments = [
266
+ "pr", "create", "--head", branch, "--base", base,
267
+ "--title", title, "--body", body,
268
+ ]
269
+ if draft:
270
+ arguments.append("--draft")
271
+ raw = self._github(*arguments)
262
272
  except RuntimeError as error:
263
273
  # A successful create may lose its acknowledgement. Only recover
264
274
  # the deterministic branch identity; never create a second PR.
@@ -30,13 +30,17 @@ AUTHORITIES = frozenset(
30
30
  "UNKNOWN_AUTHORITY",
31
31
  }
32
32
  )
33
- GATE_TYPES = frozenset({"IMPLEMENTATION_MERGE_APPROVAL", "FINALIZATION_MERGE_APPROVAL"})
33
+ GATE_TYPES = frozenset({
34
+ "IMPLEMENTATION_MERGE_APPROVAL",
35
+ "FINALIZATION_MERGE_APPROVAL",
36
+ "RECONCILIATION_MERGE_APPROVAL",
37
+ })
34
38
  GATE_STATUSES = frozenset({"NOT_REQUIRED", "WAITING", "SATISFIED", "UNAVAILABLE"})
35
39
  VALIDATION_STATES = frozenset(
36
40
  {"PASS", "FAIL", "NOT_EXECUTED", "NOT_APPLICABLE", "UNAVAILABLE", "WAITING"}
37
41
  )
38
42
  PR_CHECK_STATES = frozenset({"PASS", "FAIL", "WAITING", "UNAVAILABLE"})
39
- PR_ROLES = frozenset({"IMPLEMENTATION", "FINALIZATION"})
43
+ PR_ROLES = frozenset({"IMPLEMENTATION", "FINALIZATION", "RECONCILIATION"})
40
44
 
41
45
 
42
46
  def _connection(root: Path, central_database: Path | None) -> sqlite3.Connection:
@@ -256,6 +260,7 @@ def terminal_snapshot(
256
260
  worktree_state: str,
257
261
  active_blocker: str,
258
262
  recovery_required: str,
263
+ reconciliation_pr: int | None = None,
259
264
  retry_parent: str | None = None,
260
265
  resume_parent: str | None = None,
261
266
  submission_id: str | None = None,
@@ -358,6 +363,7 @@ def terminal_snapshot(
358
363
  "submission_id": submission_id or "UNAVAILABLE",
359
364
  "implementation_pr": implementation_pr,
360
365
  "finalization_pr": finalization_pr,
366
+ "reconciliation_pr": reconciliation_pr,
361
367
  "gates": [{"gate_type": row[0], "status": row[1], "related_pr": row[2]} for row in gates],
362
368
  "actions": [{"action": row[0], "authority": row[1]} for row in actions],
363
369
  "validation_current": validation,
@@ -385,7 +391,10 @@ def terminal_snapshot(
385
391
  ),
386
392
  "observed_at": _now(),
387
393
  }
388
- for role, pr_number in (("IMPLEMENTATION", implementation_pr), ("FINALIZATION", finalization_pr)):
394
+ deliveries = [("IMPLEMENTATION", implementation_pr), ("FINALIZATION", finalization_pr)]
395
+ if reconciliation_pr is not None:
396
+ deliveries.append(("RECONCILIATION", reconciliation_pr))
397
+ for role, pr_number in deliveries:
389
398
  check = pr_checks.get(role, {})
390
399
  snapshot[f"{role.lower()}_delivery"] = (
391
400
  "COMPLETE"
@@ -474,6 +483,11 @@ def evaluate(snapshot: dict[str, object]) -> tuple[str, list[str]]:
474
483
  reasons.append("IMPLEMENTATION_MERGE_GATE_UNPROVEN")
475
484
  if gate.get("FINALIZATION_MERGE_APPROVAL") != "SATISFIED":
476
485
  reasons.append("FINALIZATION_MERGE_GATE_UNPROVEN")
486
+ if snapshot.get("reconciliation_pr") is not None:
487
+ if snapshot.get("reconciliation_delivery") != "COMPLETE":
488
+ reasons.append("RECONCILIATION_DELIVERY_UNPROVEN")
489
+ if gate.get("RECONCILIATION_MERGE_APPROVAL") != "SATISFIED":
490
+ reasons.append("RECONCILIATION_MERGE_GATE_UNPROVEN")
477
491
  autonomous = {
478
492
  row.get("action")
479
493
  for row in snapshot.get("actions", [])
@@ -498,7 +512,10 @@ def evaluate(snapshot: dict[str, object]) -> tuple[str, list[str]]:
498
512
  reasons.append("EVIDENCE_CONFLICT")
499
513
  if snapshot.get("pr_check_projection_conflict"):
500
514
  reasons.append("EVIDENCE_CONFLICT")
501
- for role in (() if validation_only else ("IMPLEMENTATION", "FINALIZATION")):
515
+ roles = ["IMPLEMENTATION", "FINALIZATION"]
516
+ if snapshot.get("reconciliation_pr") is not None:
517
+ roles.append("RECONCILIATION")
518
+ for role in (() if validation_only else roles):
502
519
  if snapshot.get(f"{role.lower()}_pr") is not None:
503
520
  check = snapshot.get("pr_checks", {}).get(role, {})
504
521
  if check.get("required_checks_state") != "PASS":
@@ -12,7 +12,7 @@ from .storage import ENGINEERING_STORAGE_SCHEMA_VERSION
12
12
 
13
13
  SEMVER = re.compile(r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$")
14
14
  CONTRACT = re.compile(r"^(\d{4})\.(0[1-9]|1[0-2])$")
15
- CURRENT_PLATFORM_VERSION = "2.3.70"
15
+ CURRENT_PLATFORM_VERSION = "2.3.71"
16
16
  MANIFEST_FIELDS = frozenset(
17
17
  {
18
18
  "platform_version",