engineering-platform 2.3.90__tar.gz → 2.3.92__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 (172) hide show
  1. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/PKG-INFO +1 -1
  2. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/pyproject.toml +1 -1
  3. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/ENGINEERING_PLATFORM_CONFIG.json +1 -1
  4. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/ENGINEERING_PLATFORM_VERSION.json +4 -4
  5. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/assets/dashboard.css +3 -3
  6. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/execution_host.py +124 -5
  7. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/execution_repository.py +76 -0
  8. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/execution_timing.py +23 -0
  9. engineering_platform-2.3.92/src/engineering_platform/managed_workspace_readiness.py +85 -0
  10. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/parity_context.py +5 -2
  11. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/parity_lifecycle_dispatcher.py +8 -1
  12. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/platform_version.py +1 -1
  13. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/revision_binding.py +10 -4
  14. engineering_platform-2.3.92/src/engineering_platform/schemas/producer-readback-v1.3.schema.json +103 -0
  15. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/server.py +64 -3
  16. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/submission_service.py +42 -3
  17. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/templates/workspace-config.json +1 -1
  18. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/workspace_preflight.py +10 -2
  19. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform.egg-info/PKG-INFO +1 -1
  20. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform.egg-info/SOURCES.txt +2 -0
  21. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/README.md +0 -0
  22. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/setup.cfg +0 -0
  23. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/__init__.py +0 -0
  24. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/__main__.py +0 -0
  25. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/agent_state.py +0 -0
  26. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/agent_trust.py +0 -0
  27. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/assets/dashboard.js +0 -0
  28. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/assets/dashboard_locales.mjs +0 -0
  29. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/assets/dashboard_status_store.mjs +0 -0
  30. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/assets/dashboard_translation.mjs +0 -0
  31. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/assets/operations-console/apple-touch-icon-dark.png +0 -0
  32. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/assets/operations-console/apple-touch-icon-light.png +0 -0
  33. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/assets/operations-console/icon-dark.png +0 -0
  34. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/assets/operations-console/icon-light.png +0 -0
  35. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/assets/operations-console/icon-transparent.png +0 -0
  36. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/assets/operations-console/manifest.webmanifest +0 -0
  37. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/assurance_scope.py +0 -0
  38. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/capability_preflight.py +0 -0
  39. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/capability_review.py +0 -0
  40. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/central_data_transfer.py +0 -0
  41. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/central_database.py +0 -0
  42. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/central_operational_reset.py +0 -0
  43. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/central_store_migration.py +0 -0
  44. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/codex_capacity.py +0 -0
  45. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/codex_chat.py +0 -0
  46. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/codex_observability.py +0 -0
  47. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/component_lock.py +0 -0
  48. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/component_logging.py +0 -0
  49. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/console_presentation.py +0 -0
  50. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/console_route_ownership.py +0 -0
  51. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/contracts/__init__.py +0 -0
  52. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/contracts/ep_consumer.py +0 -0
  53. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/contracts/models.py +0 -0
  54. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/contracts/projection.py +0 -0
  55. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/dashboard_browser_validation.py +0 -0
  56. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/dashboard_state.py +0 -0
  57. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/dashboard_supervisor.swift +0 -0
  58. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/dashboard_translation.py +0 -0
  59. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/dependabot_producer.py +0 -0
  60. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/development_profile.py +0 -0
  61. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/drift_diagnostics.py +0 -0
  62. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/emergency_recovery.py +0 -0
  63. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/engineering_memory.py +0 -0
  64. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/ep_consumer_credentials.py +0 -0
  65. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/evidence_projection.py +0 -0
  66. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/execution_activity.py +0 -0
  67. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/execution_context.py +0 -0
  68. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/execution_errors.py +0 -0
  69. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/execution_evidence.py +0 -0
  70. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/execution_executor.py +0 -0
  71. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/execution_finalization.py +0 -0
  72. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/execution_host_evidence.py +0 -0
  73. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/execution_lease.py +0 -0
  74. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/execution_lifecycle.py +0 -0
  75. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/execution_models.py +0 -0
  76. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/execution_readiness.py +0 -0
  77. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/execution_reporting.py +0 -0
  78. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/execution_timeout_policy.py +0 -0
  79. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/execution_transaction.py +0 -0
  80. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/external_producer_binding.py +0 -0
  81. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/file_inbox.py +0 -0
  82. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/forensic_attribution.py +0 -0
  83. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/forensic_attribution_v2.py +0 -0
  84. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/forensic_delta.py +0 -0
  85. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/golden_scenario.py +0 -0
  86. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/historical_dashboard_configuration.py +0 -0
  87. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/host_admin.py +0 -0
  88. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/host_preflight.py +0 -0
  89. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/installation_relocation.py +0 -0
  90. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/installation_update_activation.py +0 -0
  91. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/installation_update_admission.py +0 -0
  92. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/installation_update_backup.py +0 -0
  93. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/installation_update_composition.py +0 -0
  94. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/installation_update_executor.py +0 -0
  95. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/installation_update_migration.py +0 -0
  96. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/installation_update_operation.py +0 -0
  97. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/installation_update_plan.py +0 -0
  98. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/installation_update_preparation.py +0 -0
  99. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/investigation_ledger.py +0 -0
  100. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/legacy_inbox_migration.py +0 -0
  101. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/legacy_installation_adoption.py +0 -0
  102. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/lifecycle_worker.py +0 -0
  103. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/live_status.py +0 -0
  104. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/local_api.py +0 -0
  105. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/local_api_keychain.py +0 -0
  106. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/local_repository_binding.py +0 -0
  107. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/managed_autonomy.py +0 -0
  108. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/managed_codex_runtime.py +0 -0
  109. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/merge_delegation.py +0 -0
  110. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/operational_installation.py +0 -0
  111. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/operational_installation_lock.py +0 -0
  112. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/operational_installation_record.py +0 -0
  113. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/owner_credential_recovery.py +0 -0
  114. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/platform_admin.py +0 -0
  115. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/platform_api.py +0 -0
  116. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/platform_bootstrap.py +0 -0
  117. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/platform_components.py +0 -0
  118. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/pr_check_repair.py +0 -0
  119. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/pr_evidence_backfill.py +0 -0
  120. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/producer.py +0 -0
  121. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/product_installation_readback.py +0 -0
  122. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/project_agent.py +0 -0
  123. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/project_agent_service.py +0 -0
  124. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/project_topology.py +0 -0
  125. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/prompt_history.py +0 -0
  126. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/provider_context.py +0 -0
  127. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/provider_context_benchmark.py +0 -0
  128. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/provider_context_scope.py +0 -0
  129. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/provider_interruption.py +0 -0
  130. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/provider_process_identity.py +0 -0
  131. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/provider_readiness.py +0 -0
  132. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/provider_recovery.py +0 -0
  133. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/provider_usage.py +0 -0
  134. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/providers.py +0 -0
  135. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/qualification.py +0 -0
  136. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/qualification_runtime.py +0 -0
  137. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/recommendation_handoff.py +0 -0
  138. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/reconciliation_adoption.py +0 -0
  139. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/release_operation.py +0 -0
  140. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/report_analysis.py +0 -0
  141. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/repository_attachment.py +0 -0
  142. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/repository_handoff.py +0 -0
  143. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/resources.py +0 -0
  144. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/reviewer_evidence.py +0 -0
  145. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/schemas/producer-readback-v1.2.schema.json +0 -0
  146. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/schemas/producer-readback-v1.schema.json +0 -0
  147. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/schemas/repository-attachment.schema.json +0 -0
  148. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/server_console_services.py +0 -0
  149. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/server_relay.py +0 -0
  150. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/server_service.py +0 -0
  151. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/status_model.py +0 -0
  152. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/status_reconciliation.py +0 -0
  153. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/storage.py +0 -0
  154. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/submission_cli.py +0 -0
  155. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/submission_intake.py +0 -0
  156. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/system_installation_topology.py +0 -0
  157. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/system_server_service.py +0 -0
  158. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/telemetry.py +0 -0
  159. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/telemetry_contract.py +0 -0
  160. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/telemetry_export.py +0 -0
  161. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/telemetry_metrics.py +0 -0
  162. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/terminal_evidence_reconciliation.py +0 -0
  163. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/validation_identity.py +0 -0
  164. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/validation_profile.py +0 -0
  165. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/version_preparation_delivery.py +0 -0
  166. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/worktree_provenance.py +0 -0
  167. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform/worktree_tooling.py +0 -0
  168. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform.egg-info/dependency_links.txt +0 -0
  169. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform.egg-info/entry_points.txt +0 -0
  170. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/src/engineering_platform.egg-info/top_level.txt +0 -0
  171. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/tests/test_adaptive_action_sizing_design.py +0 -0
  172. {engineering_platform-2.3.90 → engineering_platform-2.3.92}/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.90
3
+ Version: 2.3.92
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.90"
7
+ version = "2.3.92"
8
8
  description = "Local-first Engineering Platform execution operations runtime"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.14,<3.15"
@@ -3,7 +3,7 @@
3
3
  "platform": {
4
4
  "id": "engineering-platform",
5
5
  "name": "Engineering Platform",
6
- "version": "2.3.90",
6
+ "version": "2.3.92",
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.90",
4
+ "dashboard_version": "2.3.92",
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.90",
9
+ "platform_version": "2.3.92",
10
10
  "report_format": 2,
11
- "runner_version": "2.3.90",
11
+ "runner_version": "2.3.92",
12
12
  "status_model": 1,
13
13
  "storage_schema": 45,
14
- "watcher_version": "2.3.90"
14
+ "watcher_version": "2.3.92"
15
15
  }
@@ -1144,9 +1144,9 @@ html[data-theme="light"] .dashboard-status-banner--provider-auth_required .provi
1144
1144
  the other operational banners rather than as a scrolling dashboard card. */
1145
1145
  .dashboard-sticky-header:has(#noProjectSelected:not([hidden])){padding-bottom:7px}.dashboard-status-banner--no-project{align-items:center;background:#163b59;border-bottom:1px solid #62bdf0;color:#e7f5ff;font:600 13px/1.45 system-ui}.dashboard-status-banner--no-project strong{color:#9bd7ff}.no-project-selected__dismiss{align-items:center;background:transparent!important;border:1px solid currentColor!important;border-radius:50%;color:inherit!important;cursor:pointer;display:inline-flex;flex:none;font:22px/1 system-ui;height:30px;justify-content:center;margin-left:auto;min-height:30px;min-width:30px;padding:0;width:30px}.no-project-selected__dismiss:hover,.no-project-selected__dismiss:focus-visible{background:#9bd7ff!important;border-color:#9bd7ff!important;color:#123a58!important}.no-project-selected__dismiss:focus-visible{box-shadow:0 0 0 4px #9bd7ff55!important;outline:2px solid #9bd7ff!important;outline-offset:2px!important}html[data-theme="light"] .dashboard-status-banner--no-project{background:#e2f3ff;color:#12466b}html[data-theme="light"] .dashboard-status-banner--no-project strong{color:#075985}html[data-theme="light"] .no-project-selected__dismiss:hover,html[data-theme="light"] .no-project-selected__dismiss:focus-visible{background:#075985!important;border-color:#075985!important;color:#fff!important}
1146
1146
 
1147
- /* Managed PR evidence remains a normal link, positioned under execution
1148
- context so both current and historical operational context stay scannable. */
1149
- .prompt-detail-card--pull-requests,.active-run-pull-requests{grid-column:2}.prompt-detail-pr-link{border-bottom:1px solid currentColor;color:var(--category-color);font-weight:400;text-decoration:none}.prompt-detail-pr-link:hover{background:transparent;color:var(--category-color);outline:0}.prompt-detail-pr-link:focus-visible{background:transparent;color:var(--category-color);outline:2px solid var(--category-color);outline-offset:2px}.prompt-detail-pr-metrics{color:var(--dashboard-modal-muted);display:flex;flex-wrap:wrap;font-size:12px;gap:5px 12px;margin-top:7px}.prompt-detail-pr-metric{white-space:nowrap}@media (max-width:620px){.prompt-detail-card--pull-requests,.active-run-pull-requests{grid-column:1;grid-row:auto}}
1147
+ /* Active PR evidence starts in the left column directly below the lifecycle.
1148
+ Historical Mission details keep PR evidence in their right context column. */
1149
+ .prompt-detail-card--pull-requests{grid-column:2}.active-run-pull-requests{grid-column:1}.prompt-detail-pr-link{border-bottom:1px solid currentColor;color:var(--category-color);font-weight:400;text-decoration:none}.prompt-detail-pr-link:hover{background:transparent;color:var(--category-color);outline:0}.prompt-detail-pr-link:focus-visible{background:transparent;color:var(--category-color);outline:2px solid var(--category-color);outline-offset:2px}.prompt-detail-pr-metrics{color:var(--dashboard-modal-muted);display:flex;flex-wrap:wrap;font-size:12px;gap:5px 12px;margin-top:7px}.prompt-detail-pr-metric{white-space:nowrap}@media (max-width:620px){.prompt-detail-card--pull-requests,.active-run-pull-requests{grid-column:1;grid-row:auto}}
1150
1150
 
1151
1151
  /* Authentication is a recoverable warning, not a destructive provider failure. */
1152
1152
  .dashboard-status-banner--provider-auth_required .provider-readiness-banner__action{background:transparent!important;border-color:#ffd768!important;color:#fff4d6!important}.dashboard-status-banner--provider-auth_required .provider-readiness-banner__action:hover:not(:disabled),.dashboard-status-banner--provider-auth_required .provider-readiness-banner__action:focus-visible{background:#f4c34f!important;border-color:#ffd768!important;color:#2a2415!important}.dashboard-status-banner--provider-auth_required .provider-readiness-banner__action:focus-visible{box-shadow:0 0 0 4px #f4c34f88;outline-color:#ffd768}
@@ -119,6 +119,7 @@ from .execution_timing import complete_active_phase as _complete_active_phase
119
119
  from .execution_timing import complete_phase as _complete_phase
120
120
  from .execution_timing import start_or_resume_phase as _start_or_resume_phase
121
121
  from .execution_timing import start_phase as _start_phase
122
+ from .execution_timing import update_active_phase_metadata as _update_active_phase_metadata
122
123
  from .managed_autonomy import (
123
124
  append_action as record_managed_action,
124
125
  append_pr_check_observation as record_managed_pr_check,
@@ -194,6 +195,20 @@ def complete_phase(root: Path, active: ActivePhase | None, **kwargs: object) ->
194
195
  )
195
196
 
196
197
 
198
+ def update_phase_metadata(
199
+ root: Path, active: ActivePhase | None, metadata: dict[str, object],
200
+ ) -> None:
201
+ """Publish optional bounded live phase data without affecting the run."""
202
+ if active is None:
203
+ return
204
+ try:
205
+ _update_active_phase_metadata(root, active, metadata)
206
+ except EngineeringStorageError as error:
207
+ _timing_unavailable(
208
+ root, active.run_id, error, central_database=active.central_database,
209
+ )
210
+
211
+
197
212
  def complete_active_phase(root: Path, run_id: str, phase_name: str, **kwargs: object) -> bool:
198
213
  try:
199
214
  return _complete_active_phase(root, run_id, phase_name, **kwargs)
@@ -700,6 +715,7 @@ class EngineeringRunner:
700
715
  selection: ReviewerSelection,
701
716
  event: str,
702
717
  result: ReviewerResult | None = None,
718
+ phase: ActivePhase | None = None,
703
719
  ) -> None:
704
720
  """Publish bounded reviewer lifecycle status without granting reviewer authority."""
705
721
  self._heartbeat()
@@ -725,6 +741,18 @@ class EngineeringRunner:
725
741
  else 0
726
742
  )
727
743
  break
744
+ update_phase_metadata(
745
+ self.root,
746
+ phase,
747
+ {"reviewer_agents": [
748
+ {
749
+ "reviewer": str(item.get("reviewer", ""))[:80],
750
+ "capability": str(item.get("capability", "engineering"))[:80],
751
+ "status": str(item.get("status", "selected"))[:16],
752
+ }
753
+ for item in self.reviewer_runtime[:12]
754
+ ]},
755
+ )
728
756
  write_live_status(self.root, state, "Capability review: " + selection.reviewer, self.reviewer_runtime)
729
757
 
730
758
  def _persist_agent_usage(self, run_id: str) -> None:
@@ -3086,6 +3114,15 @@ First implementation pull-request publication gate:
3086
3114
  "Accepted repository revision binding is invalid.",
3087
3115
  )
3088
3116
  if revision_binding is not None:
3117
+ if (revision_binding.repository_identity is not None
3118
+ and (evidence.repository != revision_binding.repository_identity
3119
+ or not callable(getattr(self.repository, "trusted_origin_identity", None))
3120
+ or self.repository.trusted_origin_identity(self.root)
3121
+ != revision_binding.repository_identity)):
3122
+ return self._save_terminal(
3123
+ state, "BLOCKED", "repository_identity_binding",
3124
+ "Managed workspace origin differs from the authorized repository identity.",
3125
+ )
3089
3126
  if state.requested_repository_revision not in {None, revision_binding.requested_revision}:
3090
3127
  return self._save_terminal(
3091
3128
  state, "BLOCKED", "repository_revision_binding",
@@ -3212,10 +3249,47 @@ First implementation pull-request publication gate:
3212
3249
  self.transaction = self.transaction.with_lease(self.active_lease)
3213
3250
  self.lease_heartbeat.start()
3214
3251
  if recovered_resume:
3252
+ self._heartbeat()
3253
+ if revision_binding is not None and context.execution_mode == "MANAGED":
3254
+ recovered_head = self.repository.inspect(self.root)
3255
+ expected_baseline = (
3256
+ revision_binding.allowed_baseline_revision
3257
+ or revision_binding.requested_revision
3258
+ )
3259
+ if (state.execution_baseline_sha != expected_baseline
3260
+ or recovered_head.repository != state.repository
3261
+ or not recovered_head.clean
3262
+ or self.repository.workspace_operation_active(self.root)
3263
+ or (state.branch is not None and recovered_head.branch not in {state.branch, "main"})
3264
+ or not callable(getattr(self.repository, "trusted_origin_identity", None))
3265
+ or self.repository.trusted_origin_identity(self.root) != state.repository
3266
+ or not callable(getattr(self.repository, "revision_is_ancestor", None))
3267
+ or not self.repository.revision_is_ancestor(
3268
+ self.root, expected_baseline, recovered_head.head_sha,
3269
+ )):
3270
+ return self._save_terminal(
3271
+ state, "BLOCKED", "recovered_workspace_baseline",
3272
+ "Recovered provider result lacks the authorized workspace baseline.",
3273
+ )
3215
3274
  # Consume immutable attempt-two evidence before any fresh provider
3216
3275
  # preparation or repository synchronization. This preserves the
3217
3276
  # original branch/worktree and cannot allocate a new invocation.
3218
3277
  result = self._invoke_agent_with_timing(state, "")
3278
+ if (revision_binding is not None and context.execution_mode == "MANAGED"
3279
+ and recovery_snapshot["lifecycle_phase"] == "EXECUTE_AGENT"
3280
+ and result.terminal_state == "COMPLETE"):
3281
+ result_head = self.repository.inspect(self.root)
3282
+ if (not result.commit_sha or not result.branch
3283
+ or result_head.head_sha != result.commit_sha
3284
+ or result_head.branch != result.branch or not result_head.clean
3285
+ or self.repository.trusted_origin_identity(self.root) != state.repository
3286
+ or not self.repository.revision_is_ancestor(
3287
+ self.root, state.execution_baseline_sha, result.commit_sha,
3288
+ )):
3289
+ return self._save_terminal(
3290
+ state, "BLOCKED", "recovered_workspace_result_drift",
3291
+ "Recovered provider result no longer matches the bound workspace commit.",
3292
+ )
3219
3293
  state = self._record_agent_execution_time(state)
3220
3294
  state = self._record_validation_evidence(state, result)
3221
3295
  state = self._record_verified_result_commit(
@@ -3229,10 +3303,18 @@ First implementation pull-request publication gate:
3229
3303
  # and so the bounded retry policy in the repository client is used.
3230
3304
  if context.execution_mode == "MANAGED":
3231
3305
  try:
3232
- # A pin is a pre-mutation contract: never synchronize it to a
3233
- # newer ambient main. A named transition is explicit and is
3234
- # verified immediately after the normal host-owned sync.
3235
- if revision_binding is None or revision_binding.allowed_baseline_revision is not None:
3306
+ # An exact pin is prepared by the owning repository client
3307
+ # under this lease. It may fast forward only to that SHA,
3308
+ # never to an ambient newer origin/main.
3309
+ if revision_binding is not None and revision_binding.allowed_baseline_revision is None:
3310
+ prepare = getattr(self.repository, "prepare_main_revision", None)
3311
+ if not callable(prepare):
3312
+ raise RunnerError("MANAGED_PREPARATION_UNAVAILABLE")
3313
+ evidence = prepare(
3314
+ self.root, revision_binding.requested_revision,
3315
+ revision_binding.repository_identity or state.repository,
3316
+ )
3317
+ else:
3236
3318
  self.repository.synchronize_main(self.root)
3237
3319
  # The initial observation predates lease acquisition. Always
3238
3320
  # refresh it here: an exact pin skips synchronization, never
@@ -3312,6 +3394,18 @@ First implementation pull-request publication gate:
3312
3394
  }
3313
3395
  for item in selections
3314
3396
  ]
3397
+ update_phase_metadata(
3398
+ self.root,
3399
+ capability_review,
3400
+ {"reviewer_agents": [
3401
+ {
3402
+ "reviewer": str(item["reviewer"])[:80],
3403
+ "capability": str(item["capability"])[:80],
3404
+ "status": "selected",
3405
+ }
3406
+ for item in self.reviewer_runtime[:12]
3407
+ ]},
3408
+ )
3315
3409
  write_live_status(
3316
3410
  self.root,
3317
3411
  state
@@ -3326,13 +3420,26 @@ First implementation pull-request publication gate:
3326
3420
  self.reviewer_runtime,
3327
3421
  )
3328
3422
  self._require_provider_dispatch_admission(state)
3423
+ self._heartbeat()
3424
+ if state.execution_mode == "MANAGED" and state.execution_baseline_sha is not None:
3425
+ reviewer_head = self.repository.inspect(self.root)
3426
+ if (reviewer_head.repository != state.repository or reviewer_head.branch != "main"
3427
+ or not reviewer_head.clean or reviewer_head.head_sha != state.execution_baseline_sha
3428
+ or self.repository.workspace_operation_active(self.root)
3429
+ or (state.requested_repository_revision is not None
3430
+ and self.repository.trusted_origin_identity(self.root) != state.repository)):
3431
+ complete_phase(self.root, capability_review, outcome="FAILED")
3432
+ return self._save_terminal(
3433
+ state, "BLOCKED", "managed_workspace_drift",
3434
+ "Managed workspace changed after baseline admission and before capability review.",
3435
+ )
3329
3436
  results = run_reviews(
3330
3437
  self.root,
3331
3438
  selections,
3332
3439
  objective,
3333
3440
  self.agent if hasattr(self.agent, "review") else None,
3334
3441
  progress=lambda selection, event, result: self._publish_reviewer_progress(
3335
- state, selection, event, result
3442
+ state, selection, event, result, capability_review
3336
3443
  ),
3337
3444
  evidence=reviewer_evidence,
3338
3445
  )
@@ -3426,6 +3533,18 @@ First implementation pull-request publication gate:
3426
3533
  ),
3427
3534
  )[1]
3428
3535
  )
3536
+ if state.execution_mode == "MANAGED" and state.execution_baseline_sha is not None:
3537
+ self._heartbeat()
3538
+ execution_head = self.repository.inspect(self.root)
3539
+ if (execution_head.repository != state.repository or execution_head.branch != "main"
3540
+ or not execution_head.clean or execution_head.head_sha != state.execution_baseline_sha
3541
+ or self.repository.workspace_operation_active(self.root)
3542
+ or (state.requested_repository_revision is not None
3543
+ and self.repository.trusted_origin_identity(self.root) != state.repository)):
3544
+ return self._save_terminal(
3545
+ state, "BLOCKED", "managed_workspace_drift",
3546
+ "Managed workspace changed after baseline admission and before provider implementation.",
3547
+ )
3429
3548
  result = self._invoke_agent_with_timing(
3430
3549
  state,
3431
3550
  assemble_prompt(
@@ -31,6 +31,18 @@ def github_repository_slug(remote: str) -> str:
31
31
  return value
32
32
 
33
33
 
34
+ def trusted_github_repository_slug(remote: str) -> str | None:
35
+ """Resolve only an authorized GitHub transport, never a path or alias."""
36
+ value = remote.strip()
37
+ if value.startswith("git@github.com:"):
38
+ slug = value.removeprefix("git@github.com:").removesuffix(".git")
39
+ elif value.startswith("https://github.com/"):
40
+ slug = value.removeprefix("https://github.com/").removesuffix(".git")
41
+ else:
42
+ return None
43
+ return slug if re.fullmatch(r"[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+", slug) else None
44
+
45
+
34
46
  class RepositoryClient(Protocol):
35
47
  def inspect(self, root: Path) -> RepositoryEvidence: ...
36
48
  def main_contains(self, root: Path, sha: str) -> bool: ...
@@ -38,6 +50,10 @@ class RepositoryClient(Protocol):
38
50
  def refresh_main_reference(self, root: Path) -> None: ...
39
51
  def remote_main_contains(self, root: Path, sha: str) -> bool: ...
40
52
  def synchronize_main(self, root: Path) -> None: ...
53
+ def prepare_main_revision(self, root: Path, revision: str, repository_id: str) -> RepositoryEvidence: ...
54
+ def revision_is_ancestor(self, root: Path, ancestor: str, descendant: str) -> bool: ...
55
+ def trusted_origin_identity(self, root: Path) -> str | None: ...
56
+ def workspace_operation_active(self, root: Path) -> bool: ...
41
57
 
42
58
 
43
59
  class GitHubClient(Protocol):
@@ -73,6 +89,19 @@ class SubprocessRepositoryClient:
73
89
  contained = self.provider.execute(root, "git", "merge-base", "--is-ancestor", head_sha, "main").returncode == 0
74
90
  return RepositoryEvidence(repository, branch, head_sha, clean, contained)
75
91
 
92
+ def trusted_origin_identity(self, root: Path) -> str | None:
93
+ return trusted_github_repository_slug(self._run(root, "git", "remote", "get-url", "origin"))
94
+
95
+ def workspace_operation_active(self, root: Path) -> bool:
96
+ for name in ("index.lock", "MERGE_HEAD", "REBASE_HEAD", "CHERRY_PICK_HEAD",
97
+ "REVERT_HEAD", "BISECT_LOG", "rebase-merge", "rebase-apply"):
98
+ path = Path(self._run(root, "git", "rev-parse", "--git-path", name))
99
+ if not path.is_absolute():
100
+ path = root / path
101
+ if path.exists():
102
+ return True
103
+ return False
104
+
76
105
  def main_contains(self, root: Path, sha: str) -> bool:
77
106
  return self.provider.execute(root, "git", "merge-base", "--is-ancestor", sha, "main").returncode == 0
78
107
 
@@ -91,6 +120,13 @@ class SubprocessRepositoryClient:
91
120
  def remote_main_contains(self, root: Path, sha: str) -> bool:
92
121
  return self.provider.execute(root, "git", "merge-base", "--is-ancestor", sha, "origin/main").returncode == 0
93
122
 
123
+ def revision_is_ancestor(self, root: Path, ancestor: str, descendant: str) -> bool:
124
+ if not all(re.fullmatch(r"[0-9a-f]{40}", value) for value in (ancestor, descendant)):
125
+ return False
126
+ return self.provider.execute(
127
+ root, "git", "merge-base", "--is-ancestor", ancestor, descendant,
128
+ ).returncode == 0
129
+
94
130
  @staticmethod
95
131
  def _is_transient_index_lock_conflict(error: RuntimeError) -> bool:
96
132
  """Recognize only Git's contention signal, never a permission failure."""
@@ -180,6 +216,46 @@ class SubprocessRepositoryClient:
180
216
  f"{self._managed_sync_diagnostics(root)}; {self._managed_sync_error_detail(error)}"
181
217
  ) from error
182
218
 
219
+ def prepare_main_revision(self, root: Path, revision: str, repository_id: str) -> RepositoryEvidence:
220
+ """Fast forward a clean Managed main to one authorized protected revision.
221
+
222
+ The caller owns the execution lease. This method never resets local
223
+ work, switches branches, or treats a branch name as commit evidence.
224
+ """
225
+ if re.fullmatch(r"[0-9a-f]{40}", revision) is None:
226
+ raise RunnerError("MANAGED_PREPARATION_INVALID_REVISION")
227
+ before = self.inspect(root)
228
+ if self.trusted_origin_identity(root) != repository_id:
229
+ raise RunnerError("MANAGED_PREPARATION_ORIGIN_UNTRUSTED")
230
+ if before.repository != repository_id:
231
+ raise RunnerError("MANAGED_PREPARATION_REPOSITORY_MISMATCH")
232
+ if before.branch != "main" or not before.clean or not before.main_contains_head:
233
+ raise RunnerError("MANAGED_PREPARATION_UNSAFE_WORKTREE")
234
+ if self.workspace_operation_active(root):
235
+ raise RunnerError("MANAGED_PREPARATION_GIT_OPERATION_ACTIVE")
236
+ self._synchronize_command(root, "git", "fetch", "--prune", "origin", "main")
237
+ if not self.remote_main_contains(root, revision):
238
+ raise RunnerError("MANAGED_PREPARATION_REVISION_NOT_ON_PROTECTED_MAIN")
239
+ if before.head_sha == revision:
240
+ if (self.inspect(root) != before or self.trusted_origin_identity(root) != repository_id
241
+ or self.workspace_operation_active(root)):
242
+ raise RunnerError("MANAGED_PREPARATION_WORKSPACE_DRIFT")
243
+ return before
244
+ if self.provider.execute(root, "git", "merge-base", "--is-ancestor", before.head_sha, revision).returncode:
245
+ raise RunnerError("MANAGED_PREPARATION_NON_FAST_FORWARD")
246
+ after_fetch = self.inspect(root)
247
+ if (after_fetch != before or self.trusted_origin_identity(root) != repository_id
248
+ or self.workspace_operation_active(root)):
249
+ raise RunnerError("MANAGED_PREPARATION_WORKSPACE_DRIFT")
250
+ self._synchronize_command(root, "git", "merge", "--ff-only", revision)
251
+ prepared = self.inspect(root)
252
+ if (prepared.repository != repository_id or prepared.branch != "main" or not prepared.clean
253
+ or not prepared.main_contains_head or prepared.head_sha != revision
254
+ or self.trusted_origin_identity(root) != repository_id
255
+ or self.workspace_operation_active(root)):
256
+ raise RunnerError("MANAGED_PREPARATION_RESULT_UNCERTAIN")
257
+ return prepared
258
+
183
259
  def cleanup_transaction(self, root: Path, branches: tuple[str | None, ...]) -> str:
184
260
  self._run(root, "git", "fetch", "--prune"); self.synchronize_main(root)
185
261
  if not self.inspect(root).clean: raise RunnerError("Cleanup blocked: workspace is not clean.")
@@ -132,6 +132,29 @@ def complete_phase(root: Path, active: ActivePhase, *, outcome: str = "COMPLETE"
132
132
  connection.close()
133
133
 
134
134
 
135
+ def update_active_phase_metadata(
136
+ root: Path, active: ActivePhase, metadata: Mapping[str, object],
137
+ ) -> None:
138
+ """Replace bounded metadata for one exact active phase.
139
+
140
+ Live projections can change while the measured phase is active. The phase
141
+ identity and timing remain immutable; only its bounded presentation data is
142
+ replaced, and a terminal phase cannot be rewritten.
143
+ """
144
+ encoded = _metadata(metadata)
145
+ connection = _connection(root, active.central_database)
146
+ try:
147
+ changed = connection.execute(
148
+ """UPDATE execution_phase_spans SET metadata=?
149
+ WHERE phase_id=? AND run_id=? AND outcome='ACTIVE'""",
150
+ (encoded, active.phase_id, active.run_id),
151
+ ).rowcount
152
+ if changed != 1:
153
+ raise EngineeringStorageError("Execution phase is not active.")
154
+ finally:
155
+ connection.close()
156
+
157
+
135
158
  def start_or_resume_phase(root: Path, run_id: str, phase_name: str, **kwargs: object) -> ActivePhase:
136
159
  """Start one envelope or resume its observed active database boundary.
137
160
 
@@ -0,0 +1,85 @@
1
+ """Read-only, project-scoped Managed workspace capability inspection."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime, timezone
6
+ from pathlib import Path
7
+ import re
8
+ import sqlite3
9
+
10
+ from .execution_repository import trusted_github_repository_slug
11
+ from .providers import GitProvider
12
+
13
+
14
+ def project_readiness(connection: sqlite3.Connection, *, project_id: str,
15
+ repository_id: str) -> dict[str, object]:
16
+ """Report known blockers without planning or changing the checkout."""
17
+ observed_at = datetime.now(timezone.utc).isoformat()
18
+ binding = connection.execute(
19
+ "SELECT b.local_root FROM ep_local_repository_bindings b "
20
+ "JOIN ep_repository_registrations r ON r.repository_id=b.repository_id "
21
+ "AND r.project_id=b.project_id AND r.role='authority' "
22
+ "WHERE b.project_id=? AND b.repository_id=? AND b.state='BOUND'",
23
+ (project_id, repository_id),
24
+ ).fetchone()
25
+ response: dict[str, object] = {
26
+ "contract_version": "1.0", "project_id": project_id,
27
+ "repository_id": repository_id,
28
+ "managed_workspace_id": f"{project_id}:{repository_id}",
29
+ "execution_mode": "MANAGED", "repository_identity": None,
30
+ "origin": None, "head_sha": None, "branch": None, "clean": None,
31
+ "busy": None, "active_lease": None,
32
+ "preparation_capability": "EXACT_MAIN_FAST_FORWARD_V1",
33
+ "status": "BLOCKED", "known_blocker": "MANAGED_WORKSPACE_UNBOUND",
34
+ "observed_at": observed_at,
35
+ }
36
+ if binding is None:
37
+ return response
38
+ root = Path(str(binding[0]))
39
+ git = GitProvider()
40
+
41
+ def read(*args: str) -> str | None:
42
+ try:
43
+ result = git.execute(root, "git", *args)
44
+ except (OSError, RuntimeError):
45
+ return None
46
+ return result.stdout.strip() if result.returncode == 0 else None
47
+
48
+ head = read("rev-parse", "--verify", "HEAD")
49
+ branch = read("branch", "--show-current")
50
+ remote = read("remote", "get-url", "origin")
51
+ status = read("status", "--porcelain", "--untracked-files=all")
52
+ identity = trusted_github_repository_slug(remote) if remote else None
53
+ active_lease = connection.execute(
54
+ "SELECT 1 FROM execution_run_leases l JOIN ep_execution_runs r ON r.run_id=l.run_id "
55
+ "WHERE r.project_id=? AND l.lease_state='ACTIVE' LIMIT 1",
56
+ (project_id,),
57
+ ).fetchone() is not None
58
+ operation = False
59
+ for name in ("index.lock", "MERGE_HEAD", "REBASE_HEAD", "CHERRY_PICK_HEAD",
60
+ "REVERT_HEAD", "BISECT_LOG", "rebase-merge", "rebase-apply"):
61
+ marker = read("rev-parse", "--git-path", name)
62
+ if marker is None:
63
+ operation = True
64
+ break
65
+ path = Path(marker)
66
+ if (path if path.is_absolute() else root / path).exists():
67
+ operation = True
68
+ break
69
+ blocker = (
70
+ "MANAGED_WORKSPACE_UNAVAILABLE" if head is None or status is None or branch is None
71
+ or re.fullmatch(r"[0-9a-f]{40}", head) is None else
72
+ "MANAGED_ORIGIN_UNTRUSTED" if identity is None else
73
+ "MANAGED_BRANCH_UNEXPECTED" if branch != "main" else
74
+ "MANAGED_WORKSPACE_DIRTY" if status else
75
+ "MANAGED_GIT_OPERATION_ACTIVE" if operation else
76
+ "MANAGED_LEASE_ACTIVE" if active_lease else None
77
+ )
78
+ response.update({
79
+ "repository_identity": identity, "origin": identity,
80
+ "head_sha": head if head and re.fullmatch(r"[0-9a-f]{40}", head) else None,
81
+ "branch": branch, "clean": status == "" if status is not None else None,
82
+ "busy": operation or active_lease, "active_lease": active_lease,
83
+ "status": "READY" if blocker is None else "BLOCKED", "known_blocker": blocker,
84
+ })
85
+ return response
@@ -78,8 +78,11 @@ class ParityProjectStore:
78
78
 
79
79
  def queued_submissions(self) -> list[dict[str, object]]:
80
80
  rows = self.connection.execute(
81
- "SELECT submission_id,repository_id,state,admission,created_at,prompt_digest "
82
- "FROM ep_submissions WHERE project_id=? ORDER BY created_at,submission_id",
81
+ "SELECT s.submission_id,s.repository_id,s.state,s.admission,s.created_at,s.prompt_digest "
82
+ "FROM ep_submissions AS s LEFT JOIN ep_parity_lifecycle_dispatches AS d "
83
+ "ON d.submission_id=s.submission_id WHERE s.project_id=? AND d.submission_id IS NULL "
84
+ "AND s.state='QUEUED' AND s.admission='ADMITTED' "
85
+ "ORDER BY s.created_at,s.submission_id",
83
86
  (self.context.project_id,),
84
87
  ).fetchall()
85
88
  return [{"submission_id": str(row[0]), "repository_id": str(row[1]), "state": str(row[2]),
@@ -117,6 +117,8 @@ def _retry_constraints_for_current_main(
117
117
  "requested_revision": binding.requested_revision,
118
118
  "allowed_baseline_revision": current_main,
119
119
  }
120
+ if binding.repository_identity is not None:
121
+ rebound[CONSTRAINT_KEY]["repository_identity"] = binding.repository_identity
120
122
  return rebound
121
123
 
122
124
 
@@ -477,7 +479,12 @@ class ParityLifecycleDispatcher:
477
479
  retry_parent_run_id=candidate.retry_parent_run_id, resume_parent_run_id=None, recorded_at=now,
478
480
  )
479
481
  host = execute_host_preflight(repository_root, run_id=run_id)
480
- workspace = execute_workspace_preflight(repository_root, candidate.prompt, run_id=run_id)
482
+ revision_binding = parse_repository_revision_binding(candidate.constraints)
483
+ workspace = execute_workspace_preflight(
484
+ repository_root, candidate.prompt, run_id=run_id,
485
+ **({"exact_revision_preparation": True}
486
+ if revision_binding is not None and revision_binding.allowed_baseline_revision is None else {}),
487
+ )
481
488
  capability = execute_capability_preflight(repository_root, candidate.prompt, run_id=run_id)
482
489
  decision, _ = _record_provider_free_admission(
483
490
  repository_root, run_id=run_id, submission_id=canonical_submission_id,
@@ -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.90"
15
+ CURRENT_PLATFORM_VERSION = "2.3.92"
16
16
  MANIFEST_FIELDS = frozenset(
17
17
  {
18
18
  "platform_version",
@@ -17,6 +17,7 @@ class RepositoryRevisionBinding:
17
17
 
18
18
  requested_revision: str
19
19
  allowed_baseline_revision: str | None = None
20
+ repository_identity: str | None = None
20
21
 
21
22
 
22
23
  def parse_repository_revision_binding(
@@ -26,9 +27,10 @@ def parse_repository_revision_binding(
26
27
  if constraints is None or CONSTRAINT_KEY not in constraints:
27
28
  return None
28
29
  raw = constraints[CONSTRAINT_KEY]
29
- if not isinstance(raw, Mapping) or set(raw) != {
30
- "requested_revision", "allowed_baseline_revision",
31
- }:
30
+ if not isinstance(raw, Mapping) or set(raw) not in (
31
+ {"requested_revision", "allowed_baseline_revision"},
32
+ {"requested_revision", "allowed_baseline_revision", "repository_identity"},
33
+ ):
32
34
  raise ValueError("repository revision binding shape is invalid")
33
35
  requested = raw.get("requested_revision")
34
36
  allowed = raw.get("allowed_baseline_revision")
@@ -36,4 +38,8 @@ def parse_repository_revision_binding(
36
38
  raise ValueError("requested repository revision is invalid")
37
39
  if allowed is not None and (not isinstance(allowed, str) or _SHA.fullmatch(allowed) is None):
38
40
  raise ValueError("allowed baseline repository revision is invalid")
39
- return RepositoryRevisionBinding(requested, allowed)
41
+ identity = raw.get("repository_identity")
42
+ if identity is not None and (not isinstance(identity, str)
43
+ or re.fullmatch(r"[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+", identity) is None):
44
+ raise ValueError("repository identity is invalid")
45
+ return RepositoryRevisionBinding(requested, allowed, identity)