engineering-platform 2.3.90__tar.gz → 2.3.91__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.91}/PKG-INFO +1 -1
  2. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/pyproject.toml +1 -1
  3. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/ENGINEERING_PLATFORM_CONFIG.json +1 -1
  4. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/ENGINEERING_PLATFORM_VERSION.json +4 -4
  5. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_host.py +83 -4
  6. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_repository.py +76 -0
  7. engineering_platform-2.3.91/src/engineering_platform/managed_workspace_readiness.py +85 -0
  8. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/parity_context.py +5 -2
  9. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/parity_lifecycle_dispatcher.py +8 -1
  10. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/platform_version.py +1 -1
  11. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/revision_binding.py +10 -4
  12. engineering_platform-2.3.91/src/engineering_platform/schemas/producer-readback-v1.3.schema.json +103 -0
  13. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/server.py +31 -3
  14. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/submission_service.py +42 -3
  15. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/templates/workspace-config.json +1 -1
  16. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/workspace_preflight.py +10 -2
  17. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform.egg-info/PKG-INFO +1 -1
  18. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform.egg-info/SOURCES.txt +2 -0
  19. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/README.md +0 -0
  20. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/setup.cfg +0 -0
  21. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/__init__.py +0 -0
  22. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/__main__.py +0 -0
  23. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/agent_state.py +0 -0
  24. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/agent_trust.py +0 -0
  25. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/dashboard.css +0 -0
  26. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/dashboard.js +0 -0
  27. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/dashboard_locales.mjs +0 -0
  28. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/dashboard_status_store.mjs +0 -0
  29. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/dashboard_translation.mjs +0 -0
  30. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/apple-touch-icon-dark.png +0 -0
  31. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/apple-touch-icon-light.png +0 -0
  32. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/icon-dark.png +0 -0
  33. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/icon-light.png +0 -0
  34. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/icon-transparent.png +0 -0
  35. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/manifest.webmanifest +0 -0
  36. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assurance_scope.py +0 -0
  37. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/capability_preflight.py +0 -0
  38. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/capability_review.py +0 -0
  39. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/central_data_transfer.py +0 -0
  40. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/central_database.py +0 -0
  41. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/central_operational_reset.py +0 -0
  42. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/central_store_migration.py +0 -0
  43. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/codex_capacity.py +0 -0
  44. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/codex_chat.py +0 -0
  45. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/codex_observability.py +0 -0
  46. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/component_lock.py +0 -0
  47. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/component_logging.py +0 -0
  48. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/console_presentation.py +0 -0
  49. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/console_route_ownership.py +0 -0
  50. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/contracts/__init__.py +0 -0
  51. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/contracts/ep_consumer.py +0 -0
  52. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/contracts/models.py +0 -0
  53. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/contracts/projection.py +0 -0
  54. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/dashboard_browser_validation.py +0 -0
  55. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/dashboard_state.py +0 -0
  56. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/dashboard_supervisor.swift +0 -0
  57. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/dashboard_translation.py +0 -0
  58. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/dependabot_producer.py +0 -0
  59. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/development_profile.py +0 -0
  60. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/drift_diagnostics.py +0 -0
  61. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/emergency_recovery.py +0 -0
  62. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/engineering_memory.py +0 -0
  63. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/ep_consumer_credentials.py +0 -0
  64. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/evidence_projection.py +0 -0
  65. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_activity.py +0 -0
  66. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_context.py +0 -0
  67. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_errors.py +0 -0
  68. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_evidence.py +0 -0
  69. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_executor.py +0 -0
  70. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_finalization.py +0 -0
  71. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_host_evidence.py +0 -0
  72. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_lease.py +0 -0
  73. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_lifecycle.py +0 -0
  74. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_models.py +0 -0
  75. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_readiness.py +0 -0
  76. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_reporting.py +0 -0
  77. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_timeout_policy.py +0 -0
  78. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_timing.py +0 -0
  79. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_transaction.py +0 -0
  80. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/external_producer_binding.py +0 -0
  81. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/file_inbox.py +0 -0
  82. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/forensic_attribution.py +0 -0
  83. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/forensic_attribution_v2.py +0 -0
  84. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/forensic_delta.py +0 -0
  85. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/golden_scenario.py +0 -0
  86. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/historical_dashboard_configuration.py +0 -0
  87. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/host_admin.py +0 -0
  88. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/host_preflight.py +0 -0
  89. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_relocation.py +0 -0
  90. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_activation.py +0 -0
  91. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_admission.py +0 -0
  92. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_backup.py +0 -0
  93. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_composition.py +0 -0
  94. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_executor.py +0 -0
  95. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_migration.py +0 -0
  96. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_operation.py +0 -0
  97. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_plan.py +0 -0
  98. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_preparation.py +0 -0
  99. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/investigation_ledger.py +0 -0
  100. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/legacy_inbox_migration.py +0 -0
  101. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/legacy_installation_adoption.py +0 -0
  102. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/lifecycle_worker.py +0 -0
  103. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/live_status.py +0 -0
  104. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/local_api.py +0 -0
  105. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/local_api_keychain.py +0 -0
  106. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/local_repository_binding.py +0 -0
  107. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/managed_autonomy.py +0 -0
  108. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/managed_codex_runtime.py +0 -0
  109. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/merge_delegation.py +0 -0
  110. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/operational_installation.py +0 -0
  111. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/operational_installation_lock.py +0 -0
  112. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/operational_installation_record.py +0 -0
  113. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/owner_credential_recovery.py +0 -0
  114. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/platform_admin.py +0 -0
  115. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/platform_api.py +0 -0
  116. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/platform_bootstrap.py +0 -0
  117. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/platform_components.py +0 -0
  118. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/pr_check_repair.py +0 -0
  119. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/pr_evidence_backfill.py +0 -0
  120. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/producer.py +0 -0
  121. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/product_installation_readback.py +0 -0
  122. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/project_agent.py +0 -0
  123. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/project_agent_service.py +0 -0
  124. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/project_topology.py +0 -0
  125. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/prompt_history.py +0 -0
  126. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/provider_context.py +0 -0
  127. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/provider_context_benchmark.py +0 -0
  128. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/provider_context_scope.py +0 -0
  129. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/provider_interruption.py +0 -0
  130. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/provider_process_identity.py +0 -0
  131. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/provider_readiness.py +0 -0
  132. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/provider_recovery.py +0 -0
  133. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/provider_usage.py +0 -0
  134. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/providers.py +0 -0
  135. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/qualification.py +0 -0
  136. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/qualification_runtime.py +0 -0
  137. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/recommendation_handoff.py +0 -0
  138. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/reconciliation_adoption.py +0 -0
  139. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/release_operation.py +0 -0
  140. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/report_analysis.py +0 -0
  141. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/repository_attachment.py +0 -0
  142. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/repository_handoff.py +0 -0
  143. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/resources.py +0 -0
  144. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/reviewer_evidence.py +0 -0
  145. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/schemas/producer-readback-v1.2.schema.json +0 -0
  146. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/schemas/producer-readback-v1.schema.json +0 -0
  147. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/schemas/repository-attachment.schema.json +0 -0
  148. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/server_console_services.py +0 -0
  149. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/server_relay.py +0 -0
  150. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/server_service.py +0 -0
  151. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/status_model.py +0 -0
  152. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/status_reconciliation.py +0 -0
  153. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/storage.py +0 -0
  154. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/submission_cli.py +0 -0
  155. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/submission_intake.py +0 -0
  156. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/system_installation_topology.py +0 -0
  157. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/system_server_service.py +0 -0
  158. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/telemetry.py +0 -0
  159. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/telemetry_contract.py +0 -0
  160. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/telemetry_export.py +0 -0
  161. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/telemetry_metrics.py +0 -0
  162. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/terminal_evidence_reconciliation.py +0 -0
  163. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/validation_identity.py +0 -0
  164. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/validation_profile.py +0 -0
  165. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/version_preparation_delivery.py +0 -0
  166. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/worktree_provenance.py +0 -0
  167. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/worktree_tooling.py +0 -0
  168. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform.egg-info/dependency_links.txt +0 -0
  169. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform.egg-info/entry_points.txt +0 -0
  170. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform.egg-info/top_level.txt +0 -0
  171. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/tests/test_adaptive_action_sizing_design.py +0 -0
  172. {engineering_platform-2.3.90 → engineering_platform-2.3.91}/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.91
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.91"
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.91",
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.91",
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.91",
10
10
  "report_format": 2,
11
- "runner_version": "2.3.90",
11
+ "runner_version": "2.3.91",
12
12
  "status_model": 1,
13
13
  "storage_schema": 45,
14
- "watcher_version": "2.3.90"
14
+ "watcher_version": "2.3.91"
15
15
  }
@@ -3086,6 +3086,15 @@ First implementation pull-request publication gate:
3086
3086
  "Accepted repository revision binding is invalid.",
3087
3087
  )
3088
3088
  if revision_binding is not None:
3089
+ if (revision_binding.repository_identity is not None
3090
+ and (evidence.repository != revision_binding.repository_identity
3091
+ or not callable(getattr(self.repository, "trusted_origin_identity", None))
3092
+ or self.repository.trusted_origin_identity(self.root)
3093
+ != revision_binding.repository_identity)):
3094
+ return self._save_terminal(
3095
+ state, "BLOCKED", "repository_identity_binding",
3096
+ "Managed workspace origin differs from the authorized repository identity.",
3097
+ )
3089
3098
  if state.requested_repository_revision not in {None, revision_binding.requested_revision}:
3090
3099
  return self._save_terminal(
3091
3100
  state, "BLOCKED", "repository_revision_binding",
@@ -3212,10 +3221,47 @@ First implementation pull-request publication gate:
3212
3221
  self.transaction = self.transaction.with_lease(self.active_lease)
3213
3222
  self.lease_heartbeat.start()
3214
3223
  if recovered_resume:
3224
+ self._heartbeat()
3225
+ if revision_binding is not None and context.execution_mode == "MANAGED":
3226
+ recovered_head = self.repository.inspect(self.root)
3227
+ expected_baseline = (
3228
+ revision_binding.allowed_baseline_revision
3229
+ or revision_binding.requested_revision
3230
+ )
3231
+ if (state.execution_baseline_sha != expected_baseline
3232
+ or recovered_head.repository != state.repository
3233
+ or not recovered_head.clean
3234
+ or self.repository.workspace_operation_active(self.root)
3235
+ or (state.branch is not None and recovered_head.branch not in {state.branch, "main"})
3236
+ or not callable(getattr(self.repository, "trusted_origin_identity", None))
3237
+ or self.repository.trusted_origin_identity(self.root) != state.repository
3238
+ or not callable(getattr(self.repository, "revision_is_ancestor", None))
3239
+ or not self.repository.revision_is_ancestor(
3240
+ self.root, expected_baseline, recovered_head.head_sha,
3241
+ )):
3242
+ return self._save_terminal(
3243
+ state, "BLOCKED", "recovered_workspace_baseline",
3244
+ "Recovered provider result lacks the authorized workspace baseline.",
3245
+ )
3215
3246
  # Consume immutable attempt-two evidence before any fresh provider
3216
3247
  # preparation or repository synchronization. This preserves the
3217
3248
  # original branch/worktree and cannot allocate a new invocation.
3218
3249
  result = self._invoke_agent_with_timing(state, "")
3250
+ if (revision_binding is not None and context.execution_mode == "MANAGED"
3251
+ and recovery_snapshot["lifecycle_phase"] == "EXECUTE_AGENT"
3252
+ and result.terminal_state == "COMPLETE"):
3253
+ result_head = self.repository.inspect(self.root)
3254
+ if (not result.commit_sha or not result.branch
3255
+ or result_head.head_sha != result.commit_sha
3256
+ or result_head.branch != result.branch or not result_head.clean
3257
+ or self.repository.trusted_origin_identity(self.root) != state.repository
3258
+ or not self.repository.revision_is_ancestor(
3259
+ self.root, state.execution_baseline_sha, result.commit_sha,
3260
+ )):
3261
+ return self._save_terminal(
3262
+ state, "BLOCKED", "recovered_workspace_result_drift",
3263
+ "Recovered provider result no longer matches the bound workspace commit.",
3264
+ )
3219
3265
  state = self._record_agent_execution_time(state)
3220
3266
  state = self._record_validation_evidence(state, result)
3221
3267
  state = self._record_verified_result_commit(
@@ -3229,10 +3275,18 @@ First implementation pull-request publication gate:
3229
3275
  # and so the bounded retry policy in the repository client is used.
3230
3276
  if context.execution_mode == "MANAGED":
3231
3277
  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:
3278
+ # An exact pin is prepared by the owning repository client
3279
+ # under this lease. It may fast forward only to that SHA,
3280
+ # never to an ambient newer origin/main.
3281
+ if revision_binding is not None and revision_binding.allowed_baseline_revision is None:
3282
+ prepare = getattr(self.repository, "prepare_main_revision", None)
3283
+ if not callable(prepare):
3284
+ raise RunnerError("MANAGED_PREPARATION_UNAVAILABLE")
3285
+ evidence = prepare(
3286
+ self.root, revision_binding.requested_revision,
3287
+ revision_binding.repository_identity or state.repository,
3288
+ )
3289
+ else:
3236
3290
  self.repository.synchronize_main(self.root)
3237
3291
  # The initial observation predates lease acquisition. Always
3238
3292
  # refresh it here: an exact pin skips synchronization, never
@@ -3326,6 +3380,19 @@ First implementation pull-request publication gate:
3326
3380
  self.reviewer_runtime,
3327
3381
  )
3328
3382
  self._require_provider_dispatch_admission(state)
3383
+ self._heartbeat()
3384
+ if state.execution_mode == "MANAGED" and state.execution_baseline_sha is not None:
3385
+ reviewer_head = self.repository.inspect(self.root)
3386
+ if (reviewer_head.repository != state.repository or reviewer_head.branch != "main"
3387
+ or not reviewer_head.clean or reviewer_head.head_sha != state.execution_baseline_sha
3388
+ or self.repository.workspace_operation_active(self.root)
3389
+ or (state.requested_repository_revision is not None
3390
+ and self.repository.trusted_origin_identity(self.root) != state.repository)):
3391
+ complete_phase(self.root, capability_review, outcome="FAILED")
3392
+ return self._save_terminal(
3393
+ state, "BLOCKED", "managed_workspace_drift",
3394
+ "Managed workspace changed after baseline admission and before capability review.",
3395
+ )
3329
3396
  results = run_reviews(
3330
3397
  self.root,
3331
3398
  selections,
@@ -3426,6 +3493,18 @@ First implementation pull-request publication gate:
3426
3493
  ),
3427
3494
  )[1]
3428
3495
  )
3496
+ if state.execution_mode == "MANAGED" and state.execution_baseline_sha is not None:
3497
+ self._heartbeat()
3498
+ execution_head = self.repository.inspect(self.root)
3499
+ if (execution_head.repository != state.repository or execution_head.branch != "main"
3500
+ or not execution_head.clean or execution_head.head_sha != state.execution_baseline_sha
3501
+ or self.repository.workspace_operation_active(self.root)
3502
+ or (state.requested_repository_revision is not None
3503
+ and self.repository.trusted_origin_identity(self.root) != state.repository)):
3504
+ return self._save_terminal(
3505
+ state, "BLOCKED", "managed_workspace_drift",
3506
+ "Managed workspace changed after baseline admission and before provider implementation.",
3507
+ )
3429
3508
  result = self._invoke_agent_with_timing(
3430
3509
  state,
3431
3510
  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.")
@@ -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.91"
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)
@@ -0,0 +1,103 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://engineering-platform.local/schemas/producer-readback-v1.3.schema.json",
4
+ "title": "Engineering Platform producer readback v1.3 current execution disposition",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["contract_version", "submission", "producer", "correlation", "provenance", "disposition", "run", "result", "evidence"],
8
+ "properties": {
9
+ "contract_version": {"const": "1.3"},
10
+ "submission": {
11
+ "type": "object", "additionalProperties": false,
12
+ "required": ["id", "project_id", "repository_id", "state", "admission", "transport", "created_at", "accepted_request_digest"],
13
+ "properties": {
14
+ "id": {"type": "string"}, "project_id": {"type": "string"},
15
+ "repository_id": {"type": "string"}, "state": {"type": "string"},
16
+ "admission": {"type": "string"}, "transport": {"type": "string"},
17
+ "created_at": {"type": "string"},
18
+ "accepted_request_digest": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"}
19
+ }
20
+ },
21
+ "producer": {
22
+ "type": "object", "additionalProperties": false,
23
+ "required": ["id", "type", "version"],
24
+ "properties": {
25
+ "id": {"type": "string"}, "type": {"type": "string"},
26
+ "version": {"type": ["string", "null"]}
27
+ }
28
+ },
29
+ "correlation": {
30
+ "type": "object", "additionalProperties": false,
31
+ "required": ["correlation_id", "mission_id", "engineering_action_id"],
32
+ "properties": {
33
+ "correlation_id": {"type": ["string", "null"]},
34
+ "mission_id": {"type": ["string", "null"]},
35
+ "engineering_action_id": {"type": ["string", "null"]}
36
+ }
37
+ },
38
+ "provenance": {
39
+ "type": "object", "additionalProperties": false,
40
+ "required": ["status", "forge_execution"],
41
+ "properties": {
42
+ "status": {"type": "string"},
43
+ "forge_execution": {"type": ["object", "null"]}
44
+ }
45
+ },
46
+ "disposition": {
47
+ "type": "object",
48
+ "additionalProperties": false,
49
+ "required": ["state", "terminal", "execution_eligible", "revision", "operation_id", "event_reference", "reason", "actor_reference", "recorded_at", "resolution_submission_id", "retry_parent_run_id"],
50
+ "properties": {
51
+ "state": {"enum": ["QUEUED", "DEFERRED", "QUARANTINED", "DECLINED", "CLAIMED", "RUNNING", "COMPLETE", "BLOCKED", "FAILED", "DISMISSED"]},
52
+ "terminal": {"type": "boolean"},
53
+ "execution_eligible": {"type": "boolean"},
54
+ "revision": {"type": "integer", "minimum": 0},
55
+ "operation_id": {"type": ["string", "null"]},
56
+ "event_reference": {"type": ["string", "null"]},
57
+ "reason": {"type": "string"},
58
+ "actor_reference": {"type": "string"},
59
+ "recorded_at": {"type": ["string", "null"]},
60
+ "resolution_submission_id": {"type": ["string", "null"]},
61
+ "retry_parent_run_id": {"type": ["string", "null"]}
62
+ }
63
+ },
64
+ "run": {
65
+ "type": ["object", "null"],
66
+ "properties": {
67
+ "id": {"type": "string", "minLength": 1},
68
+ "state": {"type": "string", "minLength": 1},
69
+ "terminal": {"type": "boolean"},
70
+ "operator_resolution": {"type": "string"},
71
+ "updated_at": {"type": "string"},
72
+ "execution_started_at": {"type": "string", "format": "date-time"},
73
+ "execution_completed_at": {"type": "string", "format": "date-time"},
74
+ "execution_duration_ms": {"type": "integer", "minimum": 1}
75
+ }
76
+ },
77
+ "result": {
78
+ "type": "object", "additionalProperties": false,
79
+ "required": ["outcome", "terminal", "delivery_qualified"],
80
+ "properties": {
81
+ "outcome": {"type": "string"},
82
+ "terminal": {"type": "boolean"},
83
+ "delivery_qualified": {"type": "boolean"}
84
+ }
85
+ },
86
+ "evidence": {
87
+ "type": "object", "additionalProperties": false,
88
+ "required": ["status", "terminal_artifact", "repository"],
89
+ "properties": {
90
+ "status": {"type": "string"},
91
+ "terminal_artifact": {"type": ["object", "null"]},
92
+ "repository": {
93
+ "type": "object", "additionalProperties": false,
94
+ "required": ["id", "revision"],
95
+ "properties": {
96
+ "id": {"type": ["string", "null"]},
97
+ "revision": {"type": ["string", "null"]}
98
+ }
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
@@ -77,6 +77,7 @@ from . import system_server_service
77
77
  from . import storage
78
78
  from . import telemetry_export
79
79
  from . import managed_codex_runtime
80
+ from . import managed_workspace_readiness
80
81
  from . import provider_readiness
81
82
  from .platform_components import (
82
83
  PLATFORM_COMPONENT_BY_ID,
@@ -3972,7 +3973,7 @@ def _no_project_console_snapshot(data_root: Path) -> dict[str, object]:
3972
3973
  ).fetchone()[0])
3973
3974
  queue_depth = int(connection.execute(
3974
3975
  """SELECT COUNT(*) FROM ep_submissions AS submission
3975
- WHERE submission.state IN ('QUEUED','ADMITTED')
3976
+ WHERE submission.state='QUEUED' AND submission.admission='ADMITTED'
3976
3977
  AND NOT EXISTS (
3977
3978
  SELECT 1 FROM ep_parity_lifecycle_dispatches AS dispatch
3978
3979
  WHERE dispatch.submission_id=submission.submission_id
@@ -6971,7 +6972,7 @@ class _HealthHandler(http.server.BaseHTTPRequestHandler):
6971
6972
  "producer": {"id": "engineering-platform", "version": CURRENT_PLATFORM_VERSION},
6972
6973
  "instance": {"id": identity},
6973
6974
  "contracts": {
6974
- "producer_readback": [submission_service.PRODUCER_READBACK_CONTRACT_VERSION],
6975
+ "producer_readback": [submission_service.PRODUCER_READBACK_CONTRACT_VERSION, "1.3"],
6975
6976
  "terminal_evidence": [submission_service.TERMINAL_EVIDENCE_CONTRACT_VERSION],
6976
6977
  },
6977
6978
  }
@@ -7044,6 +7045,29 @@ class _HealthHandler(http.server.BaseHTTPRequestHandler):
7044
7045
  return
7045
7046
  self._send(200, declaration, identity)
7046
7047
  return
7048
+ workspace_readiness = re.fullmatch(
7049
+ r"/v1/projects/([^/]+)/managed-workspace-readiness", request.path,
7050
+ )
7051
+ if workspace_readiness:
7052
+ project_id = workspace_readiness.group(1)
7053
+ repository_id = self.headers.get("EP-Repository-ID")
7054
+ authorization = self.headers.get("Authorization", "")
7055
+ token = authorization[7:] if authorization.startswith("Bearer ") else None
7056
+ try:
7057
+ with storage.sqlite_connection(self.server.data_root / SERVER_DATABASE_FILENAME) as connection: # type: ignore[attr-defined]
7058
+ if _authenticated_consumer(connection, token, project_id) is None:
7059
+ self._send(401, {"error": "UNAUTHENTICATED"})
7060
+ return
7061
+ if not repository_id:
7062
+ self._send(400, {"error": "REPOSITORY_SCOPE_REQUIRED"})
7063
+ return
7064
+ readiness = managed_workspace_readiness.project_readiness(
7065
+ connection, project_id=project_id, repository_id=repository_id,
7066
+ )
7067
+ self._send(200, readiness, initialize(self.server.data_root).instance_id) # type: ignore[attr-defined]
7068
+ except sqlite3.Error:
7069
+ self._send(503, {"error": "CENTRAL_UNAVAILABLE"})
7070
+ return
7047
7071
  if request.path == "/diagnostics/topology":
7048
7072
  try:
7049
7073
  self._send(200, operations_projection(self.server.data_root), initialize(self.server.data_root).instance_id) # type: ignore[attr-defined]
@@ -7121,6 +7145,10 @@ class _HealthHandler(http.server.BaseHTTPRequestHandler):
7121
7145
  return
7122
7146
  projection = submission_service.producer_readback(
7123
7147
  connection, project_id=project_id, submission_id=submission_id,
7148
+ contract_version=(
7149
+ "1.3" if self.headers.get("EP-Producer-Readback-Contract") == "1.3"
7150
+ else "1.2"
7151
+ ),
7124
7152
  )
7125
7153
  if projection is None:
7126
7154
  self._send(404, {"error": "SUBMISSION_NOT_FOUND"})
@@ -8258,7 +8286,7 @@ def main(argv: list[str] | None = None) -> int:
8258
8286
  early = {"diagnostic_code": "EARLY_FAILURE_EVIDENCE_UNAVAILABLE"}
8259
8287
  receipt_complete = transport != "FILE_INBOX" or (isinstance(receipt_id, str) and bool(receipt_id) and isinstance(received_at, str) and bool(received_at))
8260
8288
  scope_complete = run_id is not None and (dispatch_project, dispatch_repository) == (project_id, repository_id)
8261
- result = {"submission_id": args.submission_id, "project_id": project_id, "repository_id": repository_id, "submission_state": state, "admission": admission, "run_id": run_id, "dispatch_state": dispatch_state, "operator_resolution": resolution, "transport_provenance": "COMPLETE" if receipt_complete else "INCOMPLETE", "admission_audit_provenance": "PRESENT" if admission_audit else "UNAVAILABLE", "receipt_run_provenance": "PRESENT" if run_id else "UNAVAILABLE", "dispatch_scope_provenance": "COMPLETE" if scope_complete else "UNAVAILABLE", "lane_blocker": {"run_id": blocked[0], "state": blocked[1]} if blocked else None, "early_failure": early, "worker_eligible": state == "QUEUED" and admission == "ADMITTED" and blocked is None}
8289
+ result = {"submission_id": args.submission_id, "project_id": project_id, "repository_id": repository_id, "submission_state": state, "admission": admission, "run_id": run_id, "dispatch_state": dispatch_state, "operator_resolution": resolution, "transport_provenance": "COMPLETE" if receipt_complete else "INCOMPLETE", "admission_audit_provenance": "PRESENT" if admission_audit else "UNAVAILABLE", "receipt_run_provenance": "PRESENT" if run_id else "UNAVAILABLE", "dispatch_scope_provenance": "COMPLETE" if scope_complete else "UNAVAILABLE", "lane_blocker": {"run_id": blocked[0], "state": blocked[1]} if blocked else None, "early_failure": early, "worker_eligible": state == "QUEUED" and admission == "ADMITTED" and run_id is None and blocked is None}
8262
8290
  elif args.command == "register-topology":
8263
8291
  if args.declaration is None:
8264
8292
  raise ServerConfigurationError("--declaration is required for explicit topology registration.")