engineering-platform 2.3.96__tar.gz → 2.3.98__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 (173) hide show
  1. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/PKG-INFO +1 -1
  2. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/pyproject.toml +1 -1
  3. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/ENGINEERING_PLATFORM_CONFIG.json +1 -1
  4. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/ENGINEERING_PLATFORM_VERSION.json +4 -4
  5. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/assets/dashboard.css +3 -0
  6. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/assets/dashboard.js +4 -2
  7. engineering_platform-2.3.98/src/engineering_platform/managed_workspace_recovery.py +174 -0
  8. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/platform_version.py +1 -1
  9. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/server.py +27 -1
  10. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/templates/workspace-config.json +1 -1
  11. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform.egg-info/PKG-INFO +1 -1
  12. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform.egg-info/SOURCES.txt +1 -0
  13. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/README.md +0 -0
  14. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/setup.cfg +0 -0
  15. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/__init__.py +0 -0
  16. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/__main__.py +0 -0
  17. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/agent_state.py +0 -0
  18. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/agent_trust.py +0 -0
  19. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/assets/dashboard_locales.mjs +0 -0
  20. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/assets/dashboard_status_store.mjs +0 -0
  21. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/assets/dashboard_translation.mjs +0 -0
  22. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/assets/operations-console/apple-touch-icon-dark.png +0 -0
  23. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/assets/operations-console/apple-touch-icon-light.png +0 -0
  24. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/assets/operations-console/icon-dark.png +0 -0
  25. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/assets/operations-console/icon-light.png +0 -0
  26. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/assets/operations-console/icon-transparent.png +0 -0
  27. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/assets/operations-console/manifest.webmanifest +0 -0
  28. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/assurance_scope.py +0 -0
  29. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/capability_preflight.py +0 -0
  30. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/capability_review.py +0 -0
  31. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/central_data_transfer.py +0 -0
  32. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/central_database.py +0 -0
  33. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/central_operational_reset.py +0 -0
  34. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/central_store_migration.py +0 -0
  35. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/codex_capacity.py +0 -0
  36. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/codex_chat.py +0 -0
  37. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/codex_observability.py +0 -0
  38. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/component_lock.py +0 -0
  39. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/component_logging.py +0 -0
  40. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/console_presentation.py +0 -0
  41. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/console_route_ownership.py +0 -0
  42. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/contracts/__init__.py +0 -0
  43. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/contracts/ep_consumer.py +0 -0
  44. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/contracts/models.py +0 -0
  45. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/contracts/projection.py +0 -0
  46. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/dashboard_browser_validation.py +0 -0
  47. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/dashboard_state.py +0 -0
  48. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/dashboard_supervisor.swift +0 -0
  49. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/dashboard_translation.py +0 -0
  50. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/dependabot_producer.py +0 -0
  51. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/development_profile.py +0 -0
  52. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/drift_diagnostics.py +0 -0
  53. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/emergency_recovery.py +0 -0
  54. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/engineering_memory.py +0 -0
  55. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/ep_consumer_credentials.py +0 -0
  56. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/evidence_projection.py +0 -0
  57. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/execution_activity.py +0 -0
  58. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/execution_context.py +0 -0
  59. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/execution_errors.py +0 -0
  60. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/execution_evidence.py +0 -0
  61. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/execution_executor.py +0 -0
  62. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/execution_finalization.py +0 -0
  63. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/execution_host.py +0 -0
  64. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/execution_host_evidence.py +0 -0
  65. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/execution_lease.py +0 -0
  66. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/execution_lifecycle.py +0 -0
  67. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/execution_models.py +0 -0
  68. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/execution_readiness.py +0 -0
  69. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/execution_reporting.py +0 -0
  70. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/execution_repository.py +0 -0
  71. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/execution_timeout_policy.py +0 -0
  72. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/execution_timing.py +0 -0
  73. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/execution_transaction.py +0 -0
  74. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/external_producer_binding.py +0 -0
  75. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/file_inbox.py +0 -0
  76. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/forensic_attribution.py +0 -0
  77. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/forensic_attribution_v2.py +0 -0
  78. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/forensic_delta.py +0 -0
  79. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/golden_scenario.py +0 -0
  80. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/historical_dashboard_configuration.py +0 -0
  81. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/host_admin.py +0 -0
  82. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/host_preflight.py +0 -0
  83. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/installation_relocation.py +0 -0
  84. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/installation_update_activation.py +0 -0
  85. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/installation_update_admission.py +0 -0
  86. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/installation_update_backup.py +0 -0
  87. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/installation_update_composition.py +0 -0
  88. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/installation_update_executor.py +0 -0
  89. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/installation_update_migration.py +0 -0
  90. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/installation_update_operation.py +0 -0
  91. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/installation_update_plan.py +0 -0
  92. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/installation_update_preparation.py +0 -0
  93. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/investigation_ledger.py +0 -0
  94. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/legacy_inbox_migration.py +0 -0
  95. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/legacy_installation_adoption.py +0 -0
  96. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/lifecycle_worker.py +0 -0
  97. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/live_status.py +0 -0
  98. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/local_api.py +0 -0
  99. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/local_api_keychain.py +0 -0
  100. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/local_repository_binding.py +0 -0
  101. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/managed_autonomy.py +0 -0
  102. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/managed_codex_runtime.py +0 -0
  103. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/managed_workspace_readiness.py +0 -0
  104. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/merge_delegation.py +0 -0
  105. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/operational_installation.py +0 -0
  106. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/operational_installation_lock.py +0 -0
  107. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/operational_installation_record.py +0 -0
  108. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/owner_credential_recovery.py +0 -0
  109. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/parity_context.py +0 -0
  110. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/parity_lifecycle_dispatcher.py +0 -0
  111. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/platform_admin.py +0 -0
  112. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/platform_api.py +0 -0
  113. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/platform_bootstrap.py +0 -0
  114. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/platform_components.py +0 -0
  115. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/pr_check_repair.py +0 -0
  116. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/pr_evidence_backfill.py +0 -0
  117. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/producer.py +0 -0
  118. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/product_installation_readback.py +0 -0
  119. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/project_agent.py +0 -0
  120. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/project_agent_service.py +0 -0
  121. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/project_topology.py +0 -0
  122. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/prompt_history.py +0 -0
  123. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/provider_context.py +0 -0
  124. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/provider_context_benchmark.py +0 -0
  125. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/provider_context_scope.py +0 -0
  126. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/provider_interruption.py +0 -0
  127. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/provider_process_identity.py +0 -0
  128. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/provider_readiness.py +0 -0
  129. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/provider_recovery.py +0 -0
  130. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/provider_usage.py +0 -0
  131. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/providers.py +0 -0
  132. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/qualification.py +0 -0
  133. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/qualification_runtime.py +0 -0
  134. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/recommendation_handoff.py +0 -0
  135. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/reconciliation_adoption.py +0 -0
  136. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/release_operation.py +0 -0
  137. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/report_analysis.py +0 -0
  138. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/repository_attachment.py +0 -0
  139. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/repository_handoff.py +0 -0
  140. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/resources.py +0 -0
  141. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/reviewer_evidence.py +0 -0
  142. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/revision_binding.py +0 -0
  143. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/schemas/producer-readback-v1.2.schema.json +0 -0
  144. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/schemas/producer-readback-v1.3.schema.json +0 -0
  145. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/schemas/producer-readback-v1.schema.json +0 -0
  146. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/schemas/repository-attachment.schema.json +0 -0
  147. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/server_console_services.py +0 -0
  148. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/server_relay.py +0 -0
  149. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/server_service.py +0 -0
  150. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/status_model.py +0 -0
  151. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/status_reconciliation.py +0 -0
  152. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/storage.py +0 -0
  153. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/submission_cli.py +0 -0
  154. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/submission_intake.py +0 -0
  155. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/submission_service.py +0 -0
  156. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/system_installation_topology.py +0 -0
  157. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/system_server_service.py +0 -0
  158. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/telemetry.py +0 -0
  159. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/telemetry_contract.py +0 -0
  160. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/telemetry_export.py +0 -0
  161. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/telemetry_metrics.py +0 -0
  162. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/terminal_evidence_reconciliation.py +0 -0
  163. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/validation_identity.py +0 -0
  164. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/validation_profile.py +0 -0
  165. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/version_preparation_delivery.py +0 -0
  166. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/workspace_preflight.py +0 -0
  167. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/worktree_provenance.py +0 -0
  168. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform/worktree_tooling.py +0 -0
  169. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform.egg-info/dependency_links.txt +0 -0
  170. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform.egg-info/entry_points.txt +0 -0
  171. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/src/engineering_platform.egg-info/top_level.txt +0 -0
  172. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/tests/test_adaptive_action_sizing_design.py +0 -0
  173. {engineering_platform-2.3.96 → engineering_platform-2.3.98}/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.96
3
+ Version: 2.3.98
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.96"
7
+ version = "2.3.98"
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.96",
6
+ "version": "2.3.98",
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.96",
4
+ "dashboard_version": "2.3.98",
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.96",
9
+ "platform_version": "2.3.98",
10
10
  "report_format": 2,
11
- "runner_version": "2.3.96",
11
+ "runner_version": "2.3.98",
12
12
  "status_model": 1,
13
13
  "storage_schema": 45,
14
- "watcher_version": "2.3.96"
14
+ "watcher_version": "2.3.98"
15
15
  }
@@ -886,6 +886,8 @@ html:not([data-theme="light"]) .dashboard-modal-shell{--modal-header-surface:col
886
886
  .dashboard-action--download:hover:not(:disabled){background:#f0b66a!important;border-color:#f0b66a!important;color:#201812!important}
887
887
  .dashboard-action--copy{background:#34283f!important;border-color:#d0a4ff!important;color:#eadcff!important}
888
888
  .dashboard-action--copy:hover:not(:disabled){background:#d0a4ff!important;border-color:#d0a4ff!important;color:#17151a!important}
889
+ .report-view-modal__reload{background:#3b281b!important;border-color:#f0b66a!important;color:#fff0dc!important;font-size:21px!important}
890
+ .report-view-modal__reload:hover:not(:disabled){background:#f0b66a!important;border-color:#f0b66a!important;color:#201812!important}
889
891
  .dashboard-action--destructive{background:#3a2028!important;border-color:#ff718f!important;color:#ffd9e1!important}
890
892
  .dashboard-action--destructive:hover:not(:disabled){background:#ff718f!important;border-color:#ff718f!important;color:#23131a!important}
891
893
  .operator-merge-wait{grid-column:1/-1}.operator-merge-wait__actions{align-items:stretch;display:flex;flex-wrap:wrap;gap:10px}.operator-merge-wait__actions .dashboard-action{border-radius:10px;flex:1 1 15rem;font:700 15px/1.25 system-ui;height:auto;justify-content:center;min-height:40px;min-width:0;padding:8px 14px;text-align:center;text-decoration:none;width:auto}.operator-merge-wait__actions .dashboard-action--primary{background:#3b281b!important;border-color:#f0b66a!important;color:#fff0dc!important}.operator-merge-wait__actions .dashboard-action--primary:hover:not(:disabled){background:#f0b66a!important;border-color:#f0b66a!important;color:#201812!important}
@@ -900,6 +902,7 @@ html[data-theme="light"] .operator-merge-wait__actions .dashboard-action--primar
900
902
  .operator-merge-wait__actions .dashboard-action--destructive::before,#operatorMergeWaitModal .dashboard-modal-shell__action--destructive::before{text-shadow:.35px 0 currentColor,-.35px 0 currentColor}
901
903
  html[data-theme="light"] .dashboard-action--copy{background:#fff7ff!important;border-color:#b99ae5!important;color:#68498a!important}
902
904
  html[data-theme="light"] .dashboard-action--copy:hover:not(:disabled){background:#d0a4ff!important;border-color:#d0a4ff!important;color:#17151a!important}
905
+ html[data-theme="light"] .report-view-modal__reload{background:#fff8ef!important;border-color:#d68b23!important;color:#643a13!important}
903
906
  html[data-theme="light"] .dashboard-action--destructive{background:#fff1f4!important;border-color:#ff718f!important;color:#b32649!important}
904
907
 
905
908
  /* Keep late modal-family rules from drifting the shared title scale. */
@@ -8021,11 +8021,13 @@ function promptHistoryReportReloadButton() {
8021
8021
  let button = $("promptHistoryReportReload");
8022
8022
  if (button) return button;
8023
8023
  button = document.createElement("button");
8024
- button.className = "dashboard-action dashboard-action--primary report-analysis-retry";
8024
+ button.className = "dashboard-action report-view-modal__reload report-analysis-retry";
8025
8025
  button.id = "promptHistoryReportReload";
8026
8026
  button.type = "button";
8027
8027
  button.hidden = true;
8028
- button.textContent = t("history.retry_report_load");
8028
+ button.textContent = "↻";
8029
+ button.setAttribute("aria-label", t("history.retry_report_load"));
8030
+ button.title = t("history.retry_report_load");
8029
8031
  button.addEventListener("click", () => { void reloadPromptHistoryReport(); });
8030
8032
  $("promptHistoryReportDownload").before(button);
8031
8033
  return button;
@@ -0,0 +1,174 @@
1
+ """Owner-operated recovery for one abandoned Managed execution checkout."""
2
+ from __future__ import annotations
3
+
4
+ from datetime import datetime, timezone
5
+ from hashlib import sha256
6
+ import json
7
+ import os
8
+ from pathlib import Path, PurePosixPath
9
+ import re
10
+ import shutil
11
+ import sqlite3
12
+ import tempfile
13
+
14
+ from .execution_errors import RunnerError
15
+ from .execution_repository import SubprocessRepositoryClient
16
+ from .local_repository_binding import resolve_execution_repository
17
+ from .providers import GitProvider
18
+
19
+
20
+ class ManagedWorkspaceRecoveryError(ValueError):
21
+ """Stable fail-closed error for abandoned-workspace recovery."""
22
+
23
+
24
+ _OPERATION = re.compile(r"[a-z0-9][a-z0-9-]{7,95}")
25
+ _BRANCH = re.compile(r"(?!main$)[A-Za-z0-9][A-Za-z0-9._/-]{0,127}")
26
+ _SHA = re.compile(r"[0-9a-f]{40}")
27
+ _MAX_FILES = 2048
28
+ _MAX_BYTES = 64 * 1024 * 1024
29
+
30
+
31
+ def _now() -> str:
32
+ return datetime.now(timezone.utc).isoformat()
33
+
34
+
35
+ def _digest(path: Path) -> str:
36
+ value = sha256()
37
+ with path.open("rb") as stream:
38
+ for chunk in iter(lambda: stream.read(1024 * 1024), b""):
39
+ value.update(chunk)
40
+ return "sha256:" + value.hexdigest()
41
+
42
+
43
+ def _command(provider: GitProvider, root: Path, *arguments: str) -> str:
44
+ try:
45
+ return provider.command(root, "git", *arguments)
46
+ except RuntimeError as error:
47
+ raise ManagedWorkspaceRecoveryError("MANAGED_WORKSPACE_RECOVERY_GIT_FAILED") from error
48
+
49
+
50
+ def _relative_untracked(provider: GitProvider, root: Path) -> tuple[Path, ...]:
51
+ raw = _command(provider, root, "ls-files", "--others", "--exclude-standard", "-z")
52
+ values = tuple(value for value in raw.split("\0") if value)
53
+ if len(values) > _MAX_FILES:
54
+ raise ManagedWorkspaceRecoveryError("MANAGED_WORKSPACE_RECOVERY_TOO_MANY_FILES")
55
+ paths: list[Path] = []
56
+ total = 0
57
+ for value in values:
58
+ posix = PurePosixPath(value)
59
+ if posix.is_absolute() or not posix.parts or any(part in {"", ".", ".."} for part in posix.parts):
60
+ raise ManagedWorkspaceRecoveryError("MANAGED_WORKSPACE_RECOVERY_PATH_INVALID")
61
+ source = root.joinpath(*posix.parts)
62
+ try:
63
+ metadata = source.lstat()
64
+ except OSError as error:
65
+ raise ManagedWorkspaceRecoveryError("MANAGED_WORKSPACE_RECOVERY_SOURCE_UNAVAILABLE") from error
66
+ if not source.is_file() or source.is_symlink():
67
+ raise ManagedWorkspaceRecoveryError("MANAGED_WORKSPACE_RECOVERY_SOURCE_UNSUPPORTED")
68
+ total += metadata.st_size
69
+ if total > _MAX_BYTES:
70
+ raise ManagedWorkspaceRecoveryError("MANAGED_WORKSPACE_RECOVERY_TOO_LARGE")
71
+ paths.append(source)
72
+ return tuple(paths)
73
+
74
+
75
+ def _write_recovery_backup(*, root: Path, destination: Path, operation_id: str,
76
+ project_id: str, repository_id: str, branch: str,
77
+ head_sha: str, provider: GitProvider) -> dict[str, object]:
78
+ destination.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
79
+ if destination.exists():
80
+ raise ManagedWorkspaceRecoveryError("MANAGED_WORKSPACE_RECOVERY_DESTINATION_EXISTS")
81
+ staging = Path(tempfile.mkdtemp(prefix=f".{operation_id}-", dir=destination.parent))
82
+ os.chmod(staging, 0o700)
83
+ try:
84
+ patch = staging / "tracked.patch"
85
+ patch.write_text(_command(provider, root, "diff", "--binary", "--full-index", "HEAD"), encoding="utf-8")
86
+ patch.chmod(0o600)
87
+ untracked_root = staging / "untracked"
88
+ untracked_root.mkdir(mode=0o700)
89
+ files: list[dict[str, object]] = []
90
+ for source in _relative_untracked(provider, root):
91
+ relative = source.relative_to(root)
92
+ target = untracked_root / relative
93
+ target.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
94
+ shutil.copyfile(source, target, follow_symlinks=False)
95
+ target.chmod(0o600)
96
+ if _digest(source) != _digest(target):
97
+ raise ManagedWorkspaceRecoveryError("MANAGED_WORKSPACE_RECOVERY_BACKUP_MISMATCH")
98
+ files.append({"path": relative.as_posix(), "size": target.stat().st_size,
99
+ "sha256": _digest(target)})
100
+ manifest: dict[str, object] = {
101
+ "contract_version": "managed-workspace-recovery/v1",
102
+ "operation_id": operation_id, "project_id": project_id,
103
+ "repository_id": repository_id, "branch": branch, "head_sha": head_sha,
104
+ "created_at": _now(), "tracked_patch": {"path": "tracked.patch", "sha256": _digest(patch)},
105
+ "untracked_files": files,
106
+ }
107
+ manifest_path = staging / "manifest.json"
108
+ manifest_path.write_text(json.dumps(manifest, sort_keys=True, indent=2) + "\n", encoding="utf-8")
109
+ manifest_path.chmod(0o600)
110
+ os.replace(staging, destination)
111
+ return manifest
112
+ except Exception:
113
+ shutil.rmtree(staging, ignore_errors=True)
114
+ raise
115
+
116
+
117
+ def recover(*, connection: sqlite3.Connection, data_root: Path, project_id: str,
118
+ repository_id: str, operation_id: str, expected_branch: str,
119
+ expected_head: str, backup_root: Path,
120
+ provider: GitProvider | None = None) -> dict[str, object]:
121
+ """Preserve abandoned WIP, restore synchronized main, and remove its local branch."""
122
+ if _OPERATION.fullmatch(operation_id) is None:
123
+ raise ManagedWorkspaceRecoveryError("MANAGED_WORKSPACE_RECOVERY_OPERATION_INVALID")
124
+ if _BRANCH.fullmatch(expected_branch) is None or ".." in expected_branch or expected_branch.endswith("/"):
125
+ raise ManagedWorkspaceRecoveryError("MANAGED_WORKSPACE_RECOVERY_BRANCH_INVALID")
126
+ if _SHA.fullmatch(expected_head) is None:
127
+ raise ManagedWorkspaceRecoveryError("MANAGED_WORKSPACE_RECOVERY_HEAD_INVALID")
128
+ try:
129
+ backup_root = backup_root.expanduser().resolve()
130
+ data_root = data_root.resolve()
131
+ except OSError as error:
132
+ raise ManagedWorkspaceRecoveryError("MANAGED_WORKSPACE_RECOVERY_PATH_UNAVAILABLE") from error
133
+ if backup_root == data_root or backup_root.is_relative_to(data_root):
134
+ raise ManagedWorkspaceRecoveryError("MANAGED_WORKSPACE_RECOVERY_BACKUP_UNSAFE")
135
+ active = connection.execute(
136
+ "SELECT COUNT(*) FROM execution_run_leases WHERE lease_state='ACTIVE'"
137
+ ).fetchone()
138
+ if active is None or int(active[0]) != 0:
139
+ raise ManagedWorkspaceRecoveryError("MANAGED_WORKSPACE_RECOVERY_ACTIVE_LEASE")
140
+ binding = resolve_execution_repository(
141
+ connection, project_id=project_id, repository_id=repository_id, data_root=data_root,
142
+ )
143
+ root = binding.local_root
144
+ git = provider or GitProvider()
145
+ client = SubprocessRepositoryClient(git)
146
+ before = client.inspect(root)
147
+ if (before.branch != expected_branch or before.head_sha != expected_head
148
+ or before.repository != client.trusted_origin_identity(root)
149
+ or client.workspace_operation_active(root)):
150
+ raise ManagedWorkspaceRecoveryError("MANAGED_WORKSPACE_RECOVERY_EXPECTATION_MISMATCH")
151
+ destination = backup_root / operation_id
152
+ manifest = _write_recovery_backup(
153
+ root=root, destination=destination, operation_id=operation_id,
154
+ project_id=project_id, repository_id=repository_id, branch=expected_branch,
155
+ head_sha=expected_head, provider=git,
156
+ )
157
+ _command(git, root, "reset", "--hard", expected_head)
158
+ _command(git, root, "clean", "-fd")
159
+ client.synchronize_main(root)
160
+ if git.execute(root, "git", "show-ref", "--verify", "--quiet", f"refs/heads/{expected_branch}").returncode == 0:
161
+ _command(git, root, "branch", "-D", expected_branch)
162
+ after = client.inspect(root)
163
+ if (after.branch != "main" or not after.clean or not after.main_contains_head
164
+ or after.repository != before.repository):
165
+ raise ManagedWorkspaceRecoveryError("MANAGED_WORKSPACE_RECOVERY_RESULT_UNCERTAIN")
166
+ receipt = {
167
+ **manifest, "completed_at": _now(), "result": "RECOVERED",
168
+ "backup_path": str(destination), "final_branch": after.branch,
169
+ "final_head_sha": after.head_sha, "final_clean": after.clean,
170
+ }
171
+ receipt_path = destination / "receipt.json"
172
+ receipt_path.write_text(json.dumps(receipt, sort_keys=True, indent=2) + "\n", encoding="utf-8")
173
+ receipt_path.chmod(0o600)
174
+ return receipt
@@ -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.96"
15
+ CURRENT_PLATFORM_VERSION = "2.3.98"
16
16
  MANIFEST_FIELDS = frozenset(
17
17
  {
18
18
  "platform_version",
@@ -78,6 +78,7 @@ from . import storage
78
78
  from . import telemetry_export
79
79
  from . import managed_codex_runtime
80
80
  from . import managed_workspace_readiness
81
+ from . import managed_workspace_recovery
81
82
  from . import provider_readiness
82
83
  from .platform_components import (
83
84
  PLATFORM_COMPONENT_BY_ID,
@@ -7544,7 +7545,7 @@ def health(data_root: Path) -> dict[str, object]:
7544
7545
 
7545
7546
  def build_parser() -> argparse.ArgumentParser:
7546
7547
  parser = argparse.ArgumentParser(prog="engineering-platform-server", description="Manage the standalone Engineering Platform Server foundation")
7547
- parser.add_argument("command", choices=("init", "start", "serve", "stop", "status", "health", "operational-diagnose", "operational-qualify", "operational-readback", "operational-update-assess", "operational-inventory", "system-service-inventory", "legacy-adoption-inspect", "legacy-adoption-authorize", "installation-update-plan", "installation-update-prepare", "installation-update-admit", "installation-update-apply", "installation-update-resume", "installation-update-status", "owner-consumer-readback", "owner-credential-recover", "owner-credential-recovery-adopt-peer-configuration", "owner-credential-recovery-status", "service-install", "service-uninstall", "relay-install", "relay-uninstall", "pairing-create", "agent-status", "agent-revoke", "agent-reset", "topology", "submission-diagnose", "bootstrap-topology", "register-topology", "provision-declaration", "issue-consumer-credential", "issue-development-consumer-credential", "grant-operator-capability", "revoke-operator-capability", "inspect-assurance-target", "select-assurance-target", "revoke-assurance-target", "reserve-merge-delegation", "activate-merge-delegation", "revoke-merge-delegation", "bind-repository", "rebind-repository", "unbind-repository", "resolve-repository", "register-producer-binding", "list-producer-bindings", "deactivate-producer-binding"))
7548
+ parser.add_argument("command", choices=("init", "start", "serve", "stop", "status", "health", "operational-diagnose", "operational-qualify", "operational-readback", "operational-update-assess", "operational-inventory", "system-service-inventory", "legacy-adoption-inspect", "legacy-adoption-authorize", "installation-update-plan", "installation-update-prepare", "installation-update-admit", "installation-update-apply", "installation-update-resume", "installation-update-status", "owner-consumer-readback", "owner-credential-recover", "owner-credential-recovery-adopt-peer-configuration", "owner-credential-recovery-status", "service-install", "service-uninstall", "relay-install", "relay-uninstall", "pairing-create", "agent-status", "agent-revoke", "agent-reset", "topology", "submission-diagnose", "bootstrap-topology", "register-topology", "provision-declaration", "issue-consumer-credential", "issue-development-consumer-credential", "grant-operator-capability", "revoke-operator-capability", "inspect-assurance-target", "select-assurance-target", "revoke-assurance-target", "reserve-merge-delegation", "activate-merge-delegation", "revoke-merge-delegation", "bind-repository", "rebind-repository", "unbind-repository", "resolve-repository", "recover-managed-workspace", "register-producer-binding", "list-producer-bindings", "deactivate-producer-binding"))
7548
7549
  parser.add_argument("--data-root", type=Path, default=default_data_root())
7549
7550
  parser.add_argument("--runtime-profile", choices=("operational", "development"), default="operational")
7550
7551
  parser.add_argument("--development-venv", type=Path)
@@ -7565,6 +7566,9 @@ def build_parser() -> argparse.ArgumentParser:
7565
7566
  parser.add_argument("--reason")
7566
7567
  parser.add_argument("--capability", choices=("QUEUE_HOLD_RESUME", "QUEUE_DECLINE"))
7567
7568
  parser.add_argument("--operation-id")
7569
+ parser.add_argument("--expected-branch")
7570
+ parser.add_argument("--expected-head")
7571
+ parser.add_argument("--backup-root", type=Path)
7568
7572
  parser.add_argument("--delegation-id")
7569
7573
  parser.add_argument("--mission-id")
7570
7574
  parser.add_argument("--mission-revision")
@@ -8593,6 +8597,27 @@ def main(argv: list[str] | None = None) -> int:
8593
8597
  target.write_text(json.dumps(declaration, sort_keys=True, indent=2) + "\n", encoding="utf-8")
8594
8598
  target.chmod(0o600)
8595
8599
  result = {"project_id": args.project_id, "repository_id": args.repository_id, "path": str(target), "result": "PROVISIONED"}
8600
+ elif args.command == "recover-managed-workspace":
8601
+ if not all((args.project_id, args.repository_id, args.operation_id,
8602
+ args.expected_branch, args.expected_head)) or args.backup_root is None:
8603
+ raise ServerConfigurationError(
8604
+ "Managed workspace recovery requires project, repository, operation, expected branch/head and backup root."
8605
+ )
8606
+ if status(args.data_root)["running"]:
8607
+ raise ServerConfigurationError("MANAGED_WORKSPACE_RECOVERY_SERVER_ACTIVE")
8608
+ initialize(args.data_root)
8609
+ try:
8610
+ from .platform_admin import require_installation_owner
8611
+ require_installation_owner(args.data_root)
8612
+ with storage.sqlite_connection(args.data_root / SERVER_DATABASE_FILENAME) as connection:
8613
+ result = managed_workspace_recovery.recover(
8614
+ connection=connection, data_root=args.data_root,
8615
+ project_id=args.project_id, repository_id=args.repository_id,
8616
+ operation_id=args.operation_id, expected_branch=args.expected_branch,
8617
+ expected_head=args.expected_head, backup_root=args.backup_root,
8618
+ )
8619
+ except PermissionError as error:
8620
+ raise ServerConfigurationError("PLATFORM_ADMIN_FORBIDDEN") from error
8596
8621
  elif args.command in {"bind-repository", "rebind-repository", "unbind-repository", "resolve-repository"}:
8597
8622
  if not args.project_id or not args.repository_id:
8598
8623
  raise ServerConfigurationError("--project-id and --repository-id are required for local binding commands.")
@@ -8637,6 +8662,7 @@ def main(argv: list[str] | None = None) -> int:
8637
8662
  server_service.ServerServiceError,
8638
8663
  development_profile.DevelopmentProfileError,
8639
8664
  local_repository_binding.LocalRepositoryBindingError,
8665
+ managed_workspace_recovery.ManagedWorkspaceRecoveryError,
8640
8666
  external_producer_binding.ProducerBindingError) as error:
8641
8667
  print(json.dumps({"error": str(error), "ready": False}, sort_keys=True))
8642
8668
  return 2
@@ -3,7 +3,7 @@
3
3
  "platform": {
4
4
  "id": "engineering-platform",
5
5
  "name": "Engineering Platform",
6
- "version": "2.3.96",
6
+ "version": "2.3.98",
7
7
  "generation": 2,
8
8
  "documentation_namespace": "engineering-platform",
9
9
  "capability_registry_version": 1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: engineering-platform
3
- Version: 2.3.96
3
+ Version: 2.3.98
4
4
  Summary: Local-first Engineering Platform execution operations runtime
5
5
  Author: Peter van Tol
6
6
  License: MIT
@@ -78,6 +78,7 @@ src/engineering_platform/local_repository_binding.py
78
78
  src/engineering_platform/managed_autonomy.py
79
79
  src/engineering_platform/managed_codex_runtime.py
80
80
  src/engineering_platform/managed_workspace_readiness.py
81
+ src/engineering_platform/managed_workspace_recovery.py
81
82
  src/engineering_platform/merge_delegation.py
82
83
  src/engineering_platform/operational_installation.py
83
84
  src/engineering_platform/operational_installation_lock.py