engineering-platform 2.3.89__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.89 → engineering_platform-2.3.91}/PKG-INFO +1 -1
  2. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/pyproject.toml +1 -1
  3. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/ENGINEERING_PLATFORM_CONFIG.json +1 -1
  4. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/ENGINEERING_PLATFORM_VERSION.json +4 -4
  5. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/central_operational_reset.py +32 -7
  6. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/execution_host.py +83 -4
  7. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/execution_repository.py +76 -0
  8. engineering_platform-2.3.91/src/engineering_platform/managed_workspace_readiness.py +85 -0
  9. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/parity_context.py +5 -2
  10. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/parity_lifecycle_dispatcher.py +8 -1
  11. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/platform_version.py +1 -1
  12. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/revision_binding.py +10 -4
  13. engineering_platform-2.3.91/src/engineering_platform/schemas/producer-readback-v1.3.schema.json +103 -0
  14. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/server.py +31 -3
  15. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/submission_service.py +42 -3
  16. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/templates/workspace-config.json +1 -1
  17. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/workspace_preflight.py +10 -2
  18. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform.egg-info/PKG-INFO +1 -1
  19. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform.egg-info/SOURCES.txt +2 -0
  20. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/README.md +0 -0
  21. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/setup.cfg +0 -0
  22. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/__init__.py +0 -0
  23. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/__main__.py +0 -0
  24. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/agent_state.py +0 -0
  25. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/agent_trust.py +0 -0
  26. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/assets/dashboard.css +0 -0
  27. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/assets/dashboard.js +0 -0
  28. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/assets/dashboard_locales.mjs +0 -0
  29. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/assets/dashboard_status_store.mjs +0 -0
  30. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/assets/dashboard_translation.mjs +0 -0
  31. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/apple-touch-icon-dark.png +0 -0
  32. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/apple-touch-icon-light.png +0 -0
  33. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/icon-dark.png +0 -0
  34. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/icon-light.png +0 -0
  35. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/icon-transparent.png +0 -0
  36. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/manifest.webmanifest +0 -0
  37. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/assurance_scope.py +0 -0
  38. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/capability_preflight.py +0 -0
  39. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/capability_review.py +0 -0
  40. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/central_data_transfer.py +0 -0
  41. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/central_database.py +0 -0
  42. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/central_store_migration.py +0 -0
  43. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/codex_capacity.py +0 -0
  44. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/codex_chat.py +0 -0
  45. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/codex_observability.py +0 -0
  46. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/component_lock.py +0 -0
  47. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/component_logging.py +0 -0
  48. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/console_presentation.py +0 -0
  49. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/console_route_ownership.py +0 -0
  50. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/contracts/__init__.py +0 -0
  51. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/contracts/ep_consumer.py +0 -0
  52. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/contracts/models.py +0 -0
  53. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/contracts/projection.py +0 -0
  54. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/dashboard_browser_validation.py +0 -0
  55. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/dashboard_state.py +0 -0
  56. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/dashboard_supervisor.swift +0 -0
  57. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/dashboard_translation.py +0 -0
  58. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/dependabot_producer.py +0 -0
  59. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/development_profile.py +0 -0
  60. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/drift_diagnostics.py +0 -0
  61. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/emergency_recovery.py +0 -0
  62. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/engineering_memory.py +0 -0
  63. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/ep_consumer_credentials.py +0 -0
  64. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/evidence_projection.py +0 -0
  65. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/execution_activity.py +0 -0
  66. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/execution_context.py +0 -0
  67. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/execution_errors.py +0 -0
  68. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/execution_evidence.py +0 -0
  69. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/execution_executor.py +0 -0
  70. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/execution_finalization.py +0 -0
  71. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/execution_host_evidence.py +0 -0
  72. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/execution_lease.py +0 -0
  73. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/execution_lifecycle.py +0 -0
  74. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/execution_models.py +0 -0
  75. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/execution_readiness.py +0 -0
  76. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/execution_reporting.py +0 -0
  77. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/execution_timeout_policy.py +0 -0
  78. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/execution_timing.py +0 -0
  79. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/execution_transaction.py +0 -0
  80. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/external_producer_binding.py +0 -0
  81. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/file_inbox.py +0 -0
  82. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/forensic_attribution.py +0 -0
  83. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/forensic_attribution_v2.py +0 -0
  84. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/forensic_delta.py +0 -0
  85. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/golden_scenario.py +0 -0
  86. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/historical_dashboard_configuration.py +0 -0
  87. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/host_admin.py +0 -0
  88. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/host_preflight.py +0 -0
  89. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/installation_relocation.py +0 -0
  90. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_activation.py +0 -0
  91. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_admission.py +0 -0
  92. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_backup.py +0 -0
  93. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_composition.py +0 -0
  94. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_executor.py +0 -0
  95. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_migration.py +0 -0
  96. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_operation.py +0 -0
  97. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_plan.py +0 -0
  98. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_preparation.py +0 -0
  99. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/investigation_ledger.py +0 -0
  100. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/legacy_inbox_migration.py +0 -0
  101. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/legacy_installation_adoption.py +0 -0
  102. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/lifecycle_worker.py +0 -0
  103. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/live_status.py +0 -0
  104. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/local_api.py +0 -0
  105. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/local_api_keychain.py +0 -0
  106. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/local_repository_binding.py +0 -0
  107. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/managed_autonomy.py +0 -0
  108. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/managed_codex_runtime.py +0 -0
  109. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/merge_delegation.py +0 -0
  110. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/operational_installation.py +0 -0
  111. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/operational_installation_lock.py +0 -0
  112. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/operational_installation_record.py +0 -0
  113. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/owner_credential_recovery.py +0 -0
  114. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/platform_admin.py +0 -0
  115. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/platform_api.py +0 -0
  116. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/platform_bootstrap.py +0 -0
  117. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/platform_components.py +0 -0
  118. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/pr_check_repair.py +0 -0
  119. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/pr_evidence_backfill.py +0 -0
  120. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/producer.py +0 -0
  121. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/product_installation_readback.py +0 -0
  122. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/project_agent.py +0 -0
  123. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/project_agent_service.py +0 -0
  124. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/project_topology.py +0 -0
  125. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/prompt_history.py +0 -0
  126. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/provider_context.py +0 -0
  127. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/provider_context_benchmark.py +0 -0
  128. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/provider_context_scope.py +0 -0
  129. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/provider_interruption.py +0 -0
  130. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/provider_process_identity.py +0 -0
  131. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/provider_readiness.py +0 -0
  132. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/provider_recovery.py +0 -0
  133. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/provider_usage.py +0 -0
  134. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/providers.py +0 -0
  135. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/qualification.py +0 -0
  136. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/qualification_runtime.py +0 -0
  137. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/recommendation_handoff.py +0 -0
  138. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/reconciliation_adoption.py +0 -0
  139. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/release_operation.py +0 -0
  140. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/report_analysis.py +0 -0
  141. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/repository_attachment.py +0 -0
  142. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/repository_handoff.py +0 -0
  143. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/resources.py +0 -0
  144. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/reviewer_evidence.py +0 -0
  145. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/schemas/producer-readback-v1.2.schema.json +0 -0
  146. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/schemas/producer-readback-v1.schema.json +0 -0
  147. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/schemas/repository-attachment.schema.json +0 -0
  148. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/server_console_services.py +0 -0
  149. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/server_relay.py +0 -0
  150. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/server_service.py +0 -0
  151. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/status_model.py +0 -0
  152. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/status_reconciliation.py +0 -0
  153. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/storage.py +0 -0
  154. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/submission_cli.py +0 -0
  155. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/submission_intake.py +0 -0
  156. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/system_installation_topology.py +0 -0
  157. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/system_server_service.py +0 -0
  158. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/telemetry.py +0 -0
  159. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/telemetry_contract.py +0 -0
  160. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/telemetry_export.py +0 -0
  161. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/telemetry_metrics.py +0 -0
  162. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/terminal_evidence_reconciliation.py +0 -0
  163. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/validation_identity.py +0 -0
  164. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/validation_profile.py +0 -0
  165. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/version_preparation_delivery.py +0 -0
  166. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/worktree_provenance.py +0 -0
  167. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform/worktree_tooling.py +0 -0
  168. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform.egg-info/dependency_links.txt +0 -0
  169. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform.egg-info/entry_points.txt +0 -0
  170. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/src/engineering_platform.egg-info/top_level.txt +0 -0
  171. {engineering_platform-2.3.89 → engineering_platform-2.3.91}/tests/test_adaptive_action_sizing_design.py +0 -0
  172. {engineering_platform-2.3.89 → 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.89
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.89"
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.89",
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.89",
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.89",
9
+ "platform_version": "2.3.91",
10
10
  "report_format": 2,
11
- "runner_version": "2.3.89",
11
+ "runner_version": "2.3.91",
12
12
  "status_model": 1,
13
13
  "storage_schema": 45,
14
- "watcher_version": "2.3.89"
14
+ "watcher_version": "2.3.91"
15
15
  }
@@ -223,6 +223,31 @@ def _implementation_digest() -> str:
223
223
  })
224
224
 
225
225
 
226
+ # The published 2.3.89 reset implementation can leave an operation at
227
+ # ARTIFACTS_ARCHIVED when operational engineering_metadata rows are present.
228
+ # Permit only that exact installed implementation to cross the fixed apply
229
+ # boundary; the plan, backup, identity, schema, generation and fences are still
230
+ # verified by apply before any database effect.
231
+ _ARCHIVED_RESET_COMPATIBILITY = frozenset({
232
+ ("2.3.89", "UNAVAILABLE_IN_INSTALLED_PACKAGE",
233
+ "sha256:17ec03725e15672ef9511382e0b3d2c72e3f502823a35b89bea170882f4a14b8"),
234
+ })
235
+
236
+
237
+ def _implementation_binding_valid(plan: dict[str, object], state: str) -> bool:
238
+ binding = (
239
+ plan.get("product_version"), plan.get("implementation_source_revision"),
240
+ plan.get("implementation_digest"),
241
+ )
242
+ current = (
243
+ CURRENT_PLATFORM_VERSION, _implementation_source_revision(),
244
+ _implementation_digest(),
245
+ )
246
+ return binding == current or (
247
+ state == "ARTIFACTS_ARCHIVED" and binding in _ARCHIVED_RESET_COMPATIBILITY
248
+ )
249
+
250
+
226
251
  def _implementation_source_revision() -> str:
227
252
  try:
228
253
  module = Path(__file__).resolve()
@@ -2302,11 +2327,7 @@ def apply(data_root: Path, *, operation_id: str, plan_digest: str) -> dict[str,
2302
2327
  raise OperationalResetError("PRESERVED_BINDINGS_CHANGED")
2303
2328
  if _schema_objects(connection) != plan["schema_objects"]:
2304
2329
  raise OperationalResetError("SCHEMA_OBJECTS_CHANGED")
2305
- if (
2306
- plan.get("product_version") != CURRENT_PLATFORM_VERSION
2307
- or plan.get("implementation_source_revision") != _implementation_source_revision()
2308
- or plan.get("implementation_digest") != _implementation_digest()
2309
- ):
2330
+ if not _implementation_binding_valid(plan, "ARTIFACTS_ARCHIVED"):
2310
2331
  raise OperationalResetError("IMPLEMENTATION_PROVENANCE_CHANGED")
2311
2332
  generation_before = int(connection.execute(
2312
2333
  "SELECT generation FROM ep_operational_dataset_state WHERE singleton=1"
@@ -2316,11 +2337,15 @@ def apply(data_root: Path, *, operation_id: str, plan_digest: str) -> dict[str,
2316
2337
  # Immutable-evidence and maintenance-block triggers are removed and
2317
2338
  # recreated inside this one uncommitted transaction. Other writers
2318
2339
  # cannot observe an unfenced schema window.
2340
+ reset_trigger_tables = (
2341
+ OPERATIONAL_HISTORY | DERIVED_CACHE_OR_PROJECTION
2342
+ | {"engineering_metadata"}
2343
+ )
2319
2344
  triggers = [
2320
2345
  (str(name), str(sql)) for name, sql in connection.execute(
2321
2346
  "SELECT name,sql FROM sqlite_master WHERE type='trigger' AND tbl_name IN ("
2322
- + ",".join("?" for _ in (OPERATIONAL_HISTORY | DERIVED_CACHE_OR_PROJECTION)) + ")",
2323
- tuple(sorted(OPERATIONAL_HISTORY | DERIVED_CACHE_OR_PROJECTION)),
2347
+ + ",".join("?" for _ in reset_trigger_tables) + ")",
2348
+ tuple(sorted(reset_trigger_tables)),
2324
2349
  ).fetchall() if sql is not None
2325
2350
  ]
2326
2351
  for name, _sql in triggers:
@@ -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.89"
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
+ }