engineering-platform 2.3.93__tar.gz → 2.3.95__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.93 → engineering_platform-2.3.95}/PKG-INFO +1 -1
  2. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/pyproject.toml +1 -1
  3. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/ENGINEERING_PLATFORM_CONFIG.json +1 -1
  4. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/ENGINEERING_PLATFORM_VERSION.json +4 -4
  5. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/agent_state.py +33 -1
  6. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/assets/dashboard.css +4 -0
  7. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/assets/dashboard.js +127 -47
  8. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/assets/dashboard_locales.mjs +10 -5
  9. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/capability_review.py +104 -3
  10. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/dashboard_state.py +8 -7
  11. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/execution_executor.py +64 -20
  12. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/execution_host.py +82 -33
  13. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/execution_reporting.py +18 -0
  14. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/live_status.py +11 -11
  15. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/platform_version.py +1 -1
  16. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/qualification_runtime.py +15 -2
  17. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/templates/workspace-config.json +1 -1
  18. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform.egg-info/PKG-INFO +1 -1
  19. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/README.md +0 -0
  20. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/setup.cfg +0 -0
  21. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/__init__.py +0 -0
  22. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/__main__.py +0 -0
  23. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/agent_trust.py +0 -0
  24. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/assets/dashboard_status_store.mjs +0 -0
  25. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/assets/dashboard_translation.mjs +0 -0
  26. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/assets/operations-console/apple-touch-icon-dark.png +0 -0
  27. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/assets/operations-console/apple-touch-icon-light.png +0 -0
  28. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/assets/operations-console/icon-dark.png +0 -0
  29. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/assets/operations-console/icon-light.png +0 -0
  30. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/assets/operations-console/icon-transparent.png +0 -0
  31. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/assets/operations-console/manifest.webmanifest +0 -0
  32. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/assurance_scope.py +0 -0
  33. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/capability_preflight.py +0 -0
  34. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/central_data_transfer.py +0 -0
  35. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/central_database.py +0 -0
  36. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/central_operational_reset.py +0 -0
  37. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/central_store_migration.py +0 -0
  38. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/codex_capacity.py +0 -0
  39. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/codex_chat.py +0 -0
  40. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/codex_observability.py +0 -0
  41. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/component_lock.py +0 -0
  42. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/component_logging.py +0 -0
  43. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/console_presentation.py +0 -0
  44. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/console_route_ownership.py +0 -0
  45. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/contracts/__init__.py +0 -0
  46. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/contracts/ep_consumer.py +0 -0
  47. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/contracts/models.py +0 -0
  48. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/contracts/projection.py +0 -0
  49. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/dashboard_browser_validation.py +0 -0
  50. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/dashboard_supervisor.swift +0 -0
  51. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/dashboard_translation.py +0 -0
  52. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/dependabot_producer.py +0 -0
  53. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/development_profile.py +0 -0
  54. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/drift_diagnostics.py +0 -0
  55. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/emergency_recovery.py +0 -0
  56. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/engineering_memory.py +0 -0
  57. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/ep_consumer_credentials.py +0 -0
  58. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/evidence_projection.py +0 -0
  59. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/execution_activity.py +0 -0
  60. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/execution_context.py +0 -0
  61. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/execution_errors.py +0 -0
  62. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/execution_evidence.py +0 -0
  63. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/execution_finalization.py +0 -0
  64. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/execution_host_evidence.py +0 -0
  65. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/execution_lease.py +0 -0
  66. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/execution_lifecycle.py +0 -0
  67. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/execution_models.py +0 -0
  68. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/execution_readiness.py +0 -0
  69. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/execution_repository.py +0 -0
  70. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/execution_timeout_policy.py +0 -0
  71. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/execution_timing.py +0 -0
  72. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/execution_transaction.py +0 -0
  73. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/external_producer_binding.py +0 -0
  74. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/file_inbox.py +0 -0
  75. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/forensic_attribution.py +0 -0
  76. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/forensic_attribution_v2.py +0 -0
  77. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/forensic_delta.py +0 -0
  78. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/golden_scenario.py +0 -0
  79. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/historical_dashboard_configuration.py +0 -0
  80. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/host_admin.py +0 -0
  81. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/host_preflight.py +0 -0
  82. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/installation_relocation.py +0 -0
  83. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/installation_update_activation.py +0 -0
  84. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/installation_update_admission.py +0 -0
  85. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/installation_update_backup.py +0 -0
  86. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/installation_update_composition.py +0 -0
  87. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/installation_update_executor.py +0 -0
  88. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/installation_update_migration.py +0 -0
  89. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/installation_update_operation.py +0 -0
  90. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/installation_update_plan.py +0 -0
  91. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/installation_update_preparation.py +0 -0
  92. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/investigation_ledger.py +0 -0
  93. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/legacy_inbox_migration.py +0 -0
  94. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/legacy_installation_adoption.py +0 -0
  95. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/lifecycle_worker.py +0 -0
  96. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/local_api.py +0 -0
  97. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/local_api_keychain.py +0 -0
  98. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/local_repository_binding.py +0 -0
  99. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/managed_autonomy.py +0 -0
  100. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/managed_codex_runtime.py +0 -0
  101. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/managed_workspace_readiness.py +0 -0
  102. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/merge_delegation.py +0 -0
  103. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/operational_installation.py +0 -0
  104. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/operational_installation_lock.py +0 -0
  105. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/operational_installation_record.py +0 -0
  106. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/owner_credential_recovery.py +0 -0
  107. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/parity_context.py +0 -0
  108. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/parity_lifecycle_dispatcher.py +0 -0
  109. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/platform_admin.py +0 -0
  110. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/platform_api.py +0 -0
  111. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/platform_bootstrap.py +0 -0
  112. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/platform_components.py +0 -0
  113. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/pr_check_repair.py +0 -0
  114. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/pr_evidence_backfill.py +0 -0
  115. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/producer.py +0 -0
  116. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/product_installation_readback.py +0 -0
  117. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/project_agent.py +0 -0
  118. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/project_agent_service.py +0 -0
  119. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/project_topology.py +0 -0
  120. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/prompt_history.py +0 -0
  121. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/provider_context.py +0 -0
  122. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/provider_context_benchmark.py +0 -0
  123. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/provider_context_scope.py +0 -0
  124. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/provider_interruption.py +0 -0
  125. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/provider_process_identity.py +0 -0
  126. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/provider_readiness.py +0 -0
  127. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/provider_recovery.py +0 -0
  128. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/provider_usage.py +0 -0
  129. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/providers.py +0 -0
  130. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/qualification.py +0 -0
  131. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/recommendation_handoff.py +0 -0
  132. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/reconciliation_adoption.py +0 -0
  133. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/release_operation.py +0 -0
  134. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/report_analysis.py +0 -0
  135. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/repository_attachment.py +0 -0
  136. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/repository_handoff.py +0 -0
  137. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/resources.py +0 -0
  138. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/reviewer_evidence.py +0 -0
  139. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/revision_binding.py +0 -0
  140. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/schemas/producer-readback-v1.2.schema.json +0 -0
  141. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/schemas/producer-readback-v1.3.schema.json +0 -0
  142. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/schemas/producer-readback-v1.schema.json +0 -0
  143. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/schemas/repository-attachment.schema.json +0 -0
  144. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/server.py +0 -0
  145. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/server_console_services.py +0 -0
  146. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/server_relay.py +0 -0
  147. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/server_service.py +0 -0
  148. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/status_model.py +0 -0
  149. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/status_reconciliation.py +0 -0
  150. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/storage.py +0 -0
  151. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/submission_cli.py +0 -0
  152. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/submission_intake.py +0 -0
  153. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/submission_service.py +0 -0
  154. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/system_installation_topology.py +0 -0
  155. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/system_server_service.py +0 -0
  156. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/telemetry.py +0 -0
  157. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/telemetry_contract.py +0 -0
  158. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/telemetry_export.py +0 -0
  159. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/telemetry_metrics.py +0 -0
  160. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/terminal_evidence_reconciliation.py +0 -0
  161. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/validation_identity.py +0 -0
  162. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/validation_profile.py +0 -0
  163. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/version_preparation_delivery.py +0 -0
  164. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/workspace_preflight.py +0 -0
  165. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/worktree_provenance.py +0 -0
  166. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform/worktree_tooling.py +0 -0
  167. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform.egg-info/SOURCES.txt +0 -0
  168. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform.egg-info/dependency_links.txt +0 -0
  169. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform.egg-info/entry_points.txt +0 -0
  170. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/src/engineering_platform.egg-info/top_level.txt +0 -0
  171. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/tests/test_adaptive_action_sizing_design.py +0 -0
  172. {engineering_platform-2.3.93 → engineering_platform-2.3.95}/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.93
3
+ Version: 2.3.95
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.93"
7
+ version = "2.3.95"
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.93",
6
+ "version": "2.3.95",
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.93",
4
+ "dashboard_version": "2.3.95",
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.93",
9
+ "platform_version": "2.3.95",
10
10
  "report_format": 2,
11
- "runner_version": "2.3.93",
11
+ "runner_version": "2.3.95",
12
12
  "status_model": 1,
13
13
  "storage_schema": 45,
14
- "watcher_version": "2.3.93"
14
+ "watcher_version": "2.3.95"
15
15
  }
@@ -458,13 +458,16 @@ class TransactionState:
458
458
  raise StateError("checkpoint assurance profile is invalid")
459
459
  review_fields = {"reviewer", "status", "candidate_sha", "profile_digest", "invocation_id", "findings"}
460
460
  current_review_fields = review_fields | {"contract_version", "started_at", "completed_at"}
461
+ integral_review_fields = current_review_fields | {"coverage", "finding_dispositions"}
461
462
  finding_fields = {"id", "fingerprint", "category", "criterion", "observation", "severity", "confidence", "blocking", "disposition"}
462
463
  current_finding_fields = finding_fields | {"evidence_ref"}
464
+ coverage_fields = {"surface", "status", "evidence_ref"}
465
+ finding_disposition_fields = {"finding_id", "disposition", "evidence_ref"}
463
466
  if (
464
467
  not isinstance(state.assurance_reviews, tuple)
465
468
  or len(state.assurance_reviews) > 16
466
469
  or any(
467
- not isinstance(review, dict) or set(review) not in (review_fields, current_review_fields)
470
+ not isinstance(review, dict) or set(review) not in (review_fields, current_review_fields, integral_review_fields)
468
471
  or review.get("reviewer") not in {"quality", "security"}
469
472
  or review.get("status") not in {"PASS", "FAIL", "UNRESOLVED"}
470
473
  or not isinstance(review.get("candidate_sha"), str) or not re.fullmatch(r"[0-9a-f]{40}", review["candidate_sha"])
@@ -486,6 +489,35 @@ class TransactionState:
486
489
  or not isinstance(review.get("started_at"), str)
487
490
  or not isinstance(review.get("completed_at"), str)
488
491
  ))
492
+ or (set(review) == integral_review_fields and (
493
+ review.get("contract_version") != "2.0"
494
+ or not isinstance(review.get("started_at"), str)
495
+ or not isinstance(review.get("completed_at"), str)
496
+ or not isinstance(review.get("coverage"), list)
497
+ or not 1 <= len(review["coverage"]) <= 16
498
+ or any(
499
+ not isinstance(item, dict) or set(item) != coverage_fields
500
+ or item.get("status") not in {"REVIEWED", "NOT_APPLICABLE"}
501
+ or any(
502
+ not isinstance(value, str) or not value or len(value) > 240
503
+ or value != redact_diagnostic(value, limit=240)
504
+ for value in item.values()
505
+ )
506
+ for item in review["coverage"]
507
+ )
508
+ or not isinstance(review.get("finding_dispositions"), list)
509
+ or len(review["finding_dispositions"]) > 64
510
+ or any(
511
+ not isinstance(item, dict) or set(item) != finding_disposition_fields
512
+ or item.get("disposition") not in {"RESOLVED", "OPEN"}
513
+ or any(
514
+ not isinstance(value, str) or not value or len(value) > 240
515
+ or value != redact_diagnostic(value, limit=240)
516
+ for value in item.values()
517
+ )
518
+ for item in review["finding_dispositions"]
519
+ )
520
+ ))
489
521
  for review in state.assurance_reviews
490
522
  )
491
523
  ):
@@ -280,6 +280,8 @@ html[data-theme="light"] :is(#rateLimitReset,#codexCliUpdate){background:#e8fff5
280
280
  .prompt-detail-provider-review{display:grid;gap:18px;grid-column:1/-1;grid-template-columns:minmax(0,1fr) minmax(320px,.78fr)}@media (max-width:760px){.prompt-detail-provider-review{grid-template-columns:1fr}}
281
281
  .prompt-detail-provider-review-stack{display:grid;gap:18px;grid-column:1/-1}.prompt-detail-provider-review>.prompt-detail-card{align-self:stretch;box-sizing:border-box}.prompt-detail-card--commit-timeline{display:flex;flex-direction:column;min-height:0}.prompt-detail-commit-timeline__list{align-content:start;display:grid;flex:1;gap:10px;list-style:none;margin:0;max-height:none;min-height:0;overflow:auto;padding:0 8px 0 0;scrollbar-gutter:stable}.prompt-detail-commit-timeline__item{border-top:1px solid color-mix(in srgb,var(--category-color) 38%,transparent);display:grid;gap:3px;padding-top:10px}.prompt-detail-commit-timeline__item:first-child{border-top:0;padding-top:0}.prompt-detail-commit-timeline__meta{color:var(--dashboard-modal-muted);font-size:12px;line-height:1.35}.prompt-detail-commit-timeline__item code{font-size:12px;overflow-wrap:normal;white-space:nowrap}.prompt-detail-commit-timeline__item>span{font-size:13px;line-height:1.4;overflow-wrap:anywhere}
282
282
  .prompt-detail-card--evidence pre,.prompt-detail-card--reviewers pre,.prompt-detail-card--assurance-reviews pre{font-family:inherit}
283
+ .reviewer-agent__status--failed{background:#ff6b6b;box-shadow:0 0 8px #ff6b6b66;color:#351313;font:800 14px/1 system-ui;height:16px;width:16px}
284
+ .prompt-detail-reviewer{border-top:1px solid color-mix(in srgb,var(--category-color) 45%,transparent);display:grid;gap:7px;padding-top:14px}.prompt-detail-reviewer:first-of-type{border-top:0;padding-top:0}.prompt-detail-reviewer h4{color:var(--category-color);font-size:16px;margin:0 0 4px}.prompt-detail-reviewer>.field{margin:2px 0 0}.prompt-detail-reviewer__status{align-items:center;display:inline-flex;gap:8px}.prompt-detail-reviewer__status-icon{align-items:center;border-radius:50%;display:inline-flex;flex:0 0 18px;font:800 12px/1 system-ui;height:18px;justify-content:center}.prompt-detail-reviewer__status-icon--completed{background:#51d88a;box-shadow:0 0 8px #51d88a66;color:#11261d}.prompt-detail-reviewer__status-icon--failed{background:#ff6b6b;box-shadow:0 0 8px #ff6b6b66;color:#351313}.prompt-detail-reviewer__status-icon--neutral{background:#9a9aa3;color:#202027}
283
285
  .prompt-detail-commit-timeline__phase{border-top:1px solid color-mix(in srgb,var(--category-color) 52%,transparent);list-style:none;padding:13px 0 0}.prompt-detail-commit-timeline__phase:first-child{border-top:0;padding-top:0}.prompt-detail-commit-timeline__phase h4{color:var(--category-color);font-size:12px;letter-spacing:.04em;margin:0 0 9px;text-transform:uppercase}.prompt-detail-commit-timeline__phase-entries{display:grid;gap:10px;list-style:none;margin:0;padding:0 0 2px}.prompt-detail-commit-timeline__phase-entries .prompt-detail-commit-timeline__item{border-top:0;padding-top:0}
284
286
  .prompt-detail-commit-timeline__phase{--commit-kind-color:#8dc7ff;border-left:3px solid var(--commit-kind-color);padding-left:10px}.prompt-detail-commit-timeline__phase--implementation{--commit-kind-color:#65c5d9}.prompt-detail-commit-timeline__phase--validation{--commit-kind-color:#65d9a3}.prompt-detail-commit-timeline__phase--quality{--commit-kind-color:#c29aff}.prompt-detail-commit-timeline__phase--repair{--commit-kind-color:#f4c34f}.prompt-detail-commit-timeline__phase--finalization{--commit-kind-color:#ff99a7}.prompt-detail-commit-timeline__phase--implementation_merge,.prompt-detail-commit-timeline__phase--finalization_merge,.prompt-detail-commit-timeline__phase--reconciliation_merge{--commit-kind-color:#f3d36a}.prompt-detail-commit-timeline__phase--reconciliation{--commit-kind-color:#73d6b0}.prompt-detail-commit-timeline__phase h4{align-items:center;color:var(--commit-kind-color);display:flex;gap:7px}.prompt-detail-commit-timeline__kind{border:1px solid currentColor;border-radius:999px;font-size:10px;line-height:1;padding:4px 6px}.prompt-detail-commit-timeline__phase-name{color:var(--dashboard-modal-muted);font-size:11px;font-weight:500;letter-spacing:0;text-transform:none;white-space:nowrap}
285
287
 
@@ -1335,3 +1337,5 @@ html[data-theme="light"] :is(.dashboard-status-banner--provider-auth_required .p
1335
1337
  }
1336
1338
  .telemetry-note{color:var(--dashboard-muted,#b9b6c0);font-size:12px;line-height:1.45;margin:0 0 10px}.telemetry-run-detail{border-top:1px solid color-mix(in srgb,var(--category-color,#fb7185) 45%,transparent);display:grid;gap:16px;margin-top:16px;padding-top:16px}.telemetry-run-detail h4{color:var(--category-color,#fb7185);font-size:14px;margin:14px 0 8px}.telemetry-scope-switcher{display:flex;flex-wrap:wrap;gap:8px}.telemetry-scope-switcher button{background:var(--dashboard-expanded-content-surface,#24242d);border:1px solid color-mix(in srgb,var(--category-color,#fb7185) 72%,transparent);border-radius:7px;color:inherit;cursor:pointer;font:400 12px/1.2 system-ui;min-height:32px;padding:5px 9px}.telemetry-scope-switcher button:hover{background:color-mix(in srgb,var(--category-color,#fb7185) 14%,var(--dashboard-expanded-content-surface,#24242d));border-color:var(--category-color,#fb7185)}.telemetry-scope-switcher button[aria-selected="true"]{background:color-mix(in srgb,var(--category-color,#fb7185) 24%,var(--dashboard-expanded-content-surface,#24242d));border-color:var(--category-color,#fb7185);box-shadow:inset 3px 0 0 var(--category-color,#fb7185)}.telemetry-scope-switcher button:focus:not(:focus-visible){box-shadow:none!important;outline:0!important}.telemetry-scope-switcher button[aria-selected="true"]:focus:not(:focus-visible){box-shadow:inset 3px 0 0 var(--category-color,#fb7185)!important}.telemetry-timeline{display:grid;gap:5px;list-style:none;margin:0;padding:0}.telemetry-timeline li{border-left:2px solid color-mix(in srgb,var(--category-color,#fb7185) 60%,transparent);margin-left:calc(var(--timeline-depth,0) * 14px);padding:6px 8px;word-break:break-word}@media(max-width:620px){.telemetry-detail-modal__content .technical-grid{grid-template-columns:minmax(0,1fr)}.telemetry-scope-switcher{position:sticky;top:0;background:var(--dashboard-expanded-content-surface,#24242d);padding-block:6px;z-index:2}.telemetry-timeline li{margin-left:min(calc(var(--timeline-depth,0) * 9px),45px)}}
1337
1339
  .telemetry-timeline-axis{color:var(--dashboard-muted,#b9b6c0);display:flex;font:11px ui-monospace,monospace;justify-content:space-between;margin:4px 8px}.telemetry-timeline__label{font-size:12px;line-height:1.35}.telemetry-timeline__track{background:color-mix(in srgb,var(--category-color,#fb7185) 10%,transparent);border:1px solid color-mix(in srgb,var(--category-color,#fb7185) 24%,transparent);border-radius:4px;height:14px;margin:5px 0;overflow:hidden;position:relative}.telemetry-timeline__bar{background:var(--category-color,#fb7185);border-radius:3px;display:block;height:100%;min-width:2px;position:absolute}.telemetry-timeline__bar:focus-visible{outline:3px solid var(--house-style);outline-offset:2px}.telemetry-timeline__bar--unpositioned{background:repeating-linear-gradient(135deg,var(--category-color,#fb7185) 0 4px,transparent 4px 8px);inset:0;width:100%}.telemetry-timeline__identity{color:var(--dashboard-muted,#b9b6c0);display:block;font-size:10px;overflow-wrap:anywhere}.telemetry-export{min-width:38px}@media(max-width:620px){.telemetry-timeline-axis{margin-inline:0}.telemetry-timeline__identity{font-size:9px}.telemetry-actions{flex-wrap:wrap}}
1340
+ /* Status markers inside lifecycle assurance evidence mirror the main status language. */
1341
+ .lifecycle-detail-modal__assurance-heading{align-items:center;display:inline-flex;gap:8px}.lifecycle-detail-modal__assurance-status{align-items:center;border-radius:50%;display:inline-flex;flex:0 0 18px;font:800 12px/1 system-ui;height:18px;justify-content:center;width:18px!important}.lifecycle-detail-modal__assurance-status--passed{background:#51d88a;box-shadow:0 0 8px #51d88a66;color:#11261d!important}.lifecycle-detail-modal__assurance-status--failed{background:#ff6b6b;box-shadow:0 0 8px #ff6b6b66;color:#351313!important}.lifecycle-detail-modal__assurance-status--neutral{background:#9a9aa3;color:#202027!important}
@@ -148,6 +148,24 @@ function reviewerCapabilityLabel(value, fallback = t("format.not_available")) {
148
148
  const raw = String(value || "").trim();
149
149
  return raw ? enumLabel(raw.toUpperCase(), raw) : fallback;
150
150
  }
151
+ function reviewerStatusPresentation(reviewer) {
152
+ const rawStatus = String(reviewer?.status || "completed").toLowerCase();
153
+ const failed = rawStatus === "failed", completed = ["completed", "uitgevoerd"].includes(rawStatus);
154
+ const label = document.createElement("span"), row = document.createElement("span"),
155
+ indicator = document.createElement("span"), text = document.createElement("span");
156
+ label.className = "label";
157
+ label.textContent = t("detail.prompt_status");
158
+ row.className = "prompt-detail-reviewer__status";
159
+ indicator.className = "prompt-detail-reviewer__status-icon prompt-detail-reviewer__status-icon--"
160
+ + (failed ? "failed" : completed ? "completed" : "neutral");
161
+ indicator.setAttribute("aria-hidden", "true");
162
+ indicator.textContent = failed ? "×" : completed ? "✓" : "•";
163
+ text.className = "prompt-detail-reviewer__status-text";
164
+ text.textContent = reviewerCapabilityLabel(reviewer?.capability || "ENGINEERING")
165
+ + " · " + reviewerStatusLabel(rawStatus);
166
+ row.append(indicator, text);
167
+ return { label, row };
168
+ }
151
169
  function driftPresentationValue(field, drift = {}) {
152
170
  const value = String(drift?.[field] || "").trim();
153
171
  if (!value) return t("format.not_available");
@@ -831,12 +849,12 @@ function reviewerPresentationState(status = {}) {
831
849
  }
832
850
  function activeReviewerAgents(items, executionStatus = {}) {
833
851
  const reviewActive = String(executionStatus?.current_phase || "").toUpperCase() === "CAPABILITY_REVIEW";
834
- // A later phase may retain only a fully successful review as compact
835
- // historical evidence. Running, failed or incomplete projections stay
836
- // phase-scoped so they can never look like live reviewer work.
837
- const completedReview = Array.isArray(items) && items.length > 0
838
- && items.every((agent) => String(agent?.status || "").toLowerCase() === "completed");
839
- const agents = (reviewActive || completedReview) && Array.isArray(items) ? items : [];
852
+ // A later phase may retain a wholly terminal review as compact historical
853
+ // evidence. Running or incomplete projections stay phase-scoped so they can
854
+ // never look like live reviewer work.
855
+ const terminalReview = Array.isArray(items) && items.length > 0
856
+ && items.every((agent) => ["completed", "failed"].includes(String(agent?.status || "").toLowerCase()));
857
+ const agents = (reviewActive || terminalReview) && Array.isArray(items) ? items : [];
840
858
  let card = $("activeReviewerAgents");
841
859
  if (!card) {
842
860
  card = document.createElement("section");
@@ -871,19 +889,23 @@ function activeReviewerAgents(items, executionStatus = {}) {
871
889
  indicator = document.createElement("span"), rawStatus = String(agent?.status || "").toLowerCase(),
872
890
  status = rawStatus === "running" && presentation !== "active" ? presentation : rawStatus;
873
891
  const isRunning = status === "running";
874
- const isCompleted = ["completed", "uitgevoerd"].includes(status);
892
+ const isCompleted = ["completed", "uitgevoerd"].includes(status), isFailed = status === "failed";
875
893
  row.className = "reviewer-agent";
876
894
  header.className = "reviewer-agent__header";
877
895
  name.className = "reviewer-agent__name";
878
896
  meta.className = "reviewer-agent__meta";
879
897
  name.textContent = reviewerLabel(agent.reviewer);
880
898
  meta.textContent = `${reviewerCapabilityLabel(agent.capability || "ENGINEERING")} · ${reviewerStatusLabel(status || "selected")}`;
881
- if (isRunning || isCompleted) {
882
- indicator.className = `reviewer-agent__status reviewer-agent__status--${isRunning ? "running" : "completed"}`;
899
+ if (isRunning || isCompleted || isFailed) {
900
+ const tone = isRunning ? "running" : isFailed ? "failed" : "completed";
901
+ indicator.className = `reviewer-agent__status reviewer-agent__status--${tone}`;
883
902
  indicator.setAttribute("role", "status");
884
- indicator.setAttribute("aria-label", isRunning ? t("ui.reviewer_status_running") : t("ui.reviewer_status_completed"));
903
+ indicator.setAttribute("aria-label", isRunning
904
+ ? t("ui.reviewer_status_running")
905
+ : isFailed ? reviewerStatusLabel("failed") : t("ui.reviewer_status_completed"));
885
906
  indicator.setAttribute("title", indicator.getAttribute("aria-label"));
886
907
  if (isCompleted) indicator.textContent = "✓";
908
+ if (isFailed) indicator.textContent = "×";
887
909
  }
888
910
  header.append(name, indicator);
889
911
  row.append(header, meta);
@@ -2143,12 +2165,31 @@ function lifecycleAssuranceEvidence(step) {
2143
2165
  for (const review of reviews) {
2144
2166
  if (!review || typeof review !== "object") continue;
2145
2167
  const findings = Array.isArray(review.findings) ? review.findings : [];
2146
- const item = document.createElement("li");
2147
- const role = String(review.reviewer || ""); const status = String(review.status || "UNRESOLVED");
2148
- item.append(Object.assign(document.createElement("strong"), { textContent: `${reviewerLabel(role, role)} · ${assuranceStatusLabel(status, status)}` }));
2168
+ const item = document.createElement("li"), heading = document.createElement("strong"),
2169
+ statusIcon = document.createElement("span"), status = String(review.status || "UNRESOLVED"),
2170
+ normalizedStatus = status.toUpperCase();
2171
+ const role = String(review.reviewer || "");
2172
+ const passed = normalizedStatus === "PASS", failed = ["FAIL", "FAILED", "UNRESOLVED"].includes(normalizedStatus);
2173
+ heading.className = "lifecycle-detail-modal__assurance-heading";
2174
+ statusIcon.className = "lifecycle-detail-modal__assurance-status lifecycle-detail-modal__assurance-status--"
2175
+ + (passed ? "passed" : failed ? "failed" : "neutral");
2176
+ statusIcon.setAttribute("aria-hidden", "true");
2177
+ statusIcon.textContent = passed ? "✓" : failed ? "×" : "•";
2178
+ heading.append(statusIcon, Object.assign(document.createElement("span"), {
2179
+ textContent: `${reviewerLabel(role, role)} · ${assuranceStatusLabel(status, status)}`,
2180
+ }));
2181
+ item.append(heading);
2149
2182
  const summary = findings.map((finding) => String(finding?.observation || "").trim()).filter(Boolean).join("; ");
2150
2183
  const summaryElement = Object.assign(document.createElement("span"), { textContent: summary || t("lifecycle.assurance_no_findings") });
2151
2184
  item.append(summaryElement);
2185
+ const coverage = Array.isArray(review.coverage) ? review.coverage : [];
2186
+ const dispositions = Array.isArray(review.finding_dispositions) ? review.finding_dispositions : [];
2187
+ if (coverage.length || dispositions.length) item.append(Object.assign(document.createElement("span"), {
2188
+ className: "estimate-meta",
2189
+ textContent: t("lifecycle.assurance_integral_evidence", {
2190
+ coverage: coverage.length, dispositions: dispositions.length,
2191
+ }),
2192
+ }));
2152
2193
  if (summary) dynamicRows.push({ source: summary, element: summaryElement });
2153
2194
  list.append(item);
2154
2195
  }
@@ -2169,10 +2210,9 @@ function lifecycleCapabilityEvidence(step) {
2169
2210
  list.className = "lifecycle-detail-modal__phase-list";
2170
2211
  for (const reviewer of reviewers) {
2171
2212
  if (!reviewer || typeof reviewer !== "object") continue;
2172
- const item = document.createElement("li");
2213
+ const item = document.createElement("li"), heading = document.createElement("strong"),
2214
+ status = reviewerStatusPresentation(reviewer);
2173
2215
  const details = [
2174
- reviewerCapabilityLabel(reviewer.capability || "ENGINEERING"),
2175
- reviewerStatusLabel(reviewer.status || "completed"),
2176
2216
  reviewer.selected_because
2177
2217
  ? t("detail.selected_because") + ": " + String(reviewer.selected_because)
2178
2218
  : null,
@@ -2186,34 +2226,57 @@ function lifecycleCapabilityEvidence(step) {
2186
2226
  ? t("detail.rejected_recommendations") + ": " + reviewer.rejected_recommendations
2187
2227
  : null,
2188
2228
  ].filter(Boolean);
2189
- item.append(
2190
- Object.assign(document.createElement("strong"), {
2191
- textContent: reviewerLabel(reviewer.reviewer, t("detail.specialist_review")),
2192
- }),
2193
- Object.assign(document.createElement("span"), {
2194
- textContent: details.join("\n"),
2195
- }),
2196
- );
2229
+ heading.textContent = reviewerLabel(reviewer.reviewer, t("detail.specialist_review"));
2230
+ item.append(heading, status.label, status.row);
2231
+ if (details.length) item.append(Object.assign(document.createElement("span"), {
2232
+ textContent: details.join("\n"),
2233
+ }));
2197
2234
  list.append(item);
2198
2235
  }
2199
2236
  if (!list.childElementCount) return null;
2200
2237
  section.append(list);
2201
2238
  return section;
2202
2239
  }
2203
- function lifecycleImplementationEvidence(step) {
2204
- const evidence = step?.implementation_evidence;
2240
+ function lifecyclePullRequestField(label, pullRequest, repository) {
2241
+ const field = lifecycleDetailField(label, `#${pullRequest}`);
2242
+ const repositoryIdentity = String(repository || "").trim();
2243
+ if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repositoryIdentity)) return field;
2244
+ const link = document.createElement("a");
2245
+ link.className = "prompt-detail-pr-link";
2246
+ link.href = `https://github.com/${repositoryIdentity.split("/").map(encodeURIComponent).join("/")}/pull/${pullRequest}`;
2247
+ link.target = "_blank";
2248
+ link.rel = "noopener noreferrer";
2249
+ link.textContent = `#${pullRequest} ↗`;
2250
+ link.setAttribute("aria-label", `${label} #${pullRequest}`);
2251
+ field.lastElementChild.replaceWith(link);
2252
+ return field;
2253
+ }
2254
+ function lifecycleDeliveryEvidence(step) {
2255
+ const evidence = step?.delivery_evidence || step?.implementation_evidence;
2205
2256
  if (!evidence || typeof evidence !== "object") return null;
2257
+ const role = String(evidence.role || "implementation").toLowerCase();
2258
+ const titles = {
2259
+ implementation: "lifecycle.detail_implementation_evidence",
2260
+ finalization: "lifecycle.detail_finalization_evidence",
2261
+ reconciliation: "lifecycle.detail_reconciliation_evidence",
2262
+ };
2263
+ const pullRequestLabels = {
2264
+ implementation: "detail.implementation_pull_request",
2265
+ finalization: "detail.finalization_pull_request",
2266
+ reconciliation: "detail.reconciliation_pull_request",
2267
+ };
2206
2268
  const fields = [];
2207
2269
  if (evidence.branch) fields.push(lifecycleDetailField(t("workspace.current_branch"), String(evidence.branch)));
2208
2270
  if (evidence.candidate_sha) fields.push(lifecycleDetailField(t("detail.candidate_sha"), String(evidence.candidate_sha)));
2209
2271
  if (Number.isInteger(evidence.pull_request) && evidence.pull_request > 0) {
2210
- fields.push(lifecycleDetailField(t("detail.implementation_pull_request"), `#${evidence.pull_request}`));
2272
+ const label = t(pullRequestLabels[role] || pullRequestLabels.implementation);
2273
+ fields.push(lifecyclePullRequestField(label, evidence.pull_request, evidence.repository));
2211
2274
  }
2212
2275
  if (!fields.length) return null;
2213
2276
  const section = document.createElement("section");
2214
2277
  section.className = "lifecycle-detail-modal__implementation-evidence";
2215
2278
  section.append(
2216
- Object.assign(document.createElement("h3"), { textContent: t("lifecycle.detail_implementation_evidence") }),
2279
+ Object.assign(document.createElement("h3"), { textContent: t(titles[role] || titles.implementation) }),
2217
2280
  Object.assign(document.createElement("div"), { className: "technical-grid" }),
2218
2281
  );
2219
2282
  section.lastElementChild.append(...fields);
@@ -2346,8 +2409,8 @@ function openLifecycleDetail(step, trigger, { presentationOnly = false } = {}) {
2346
2409
  if (capabilityEvidence) content.append(capabilityEvidence);
2347
2410
  const assuranceEvidence = lifecycleAssuranceEvidence(step);
2348
2411
  if (assuranceEvidence) content.append(assuranceEvidence);
2349
- const implementationEvidence = lifecycleImplementationEvidence(step);
2350
- if (implementationEvidence) content.append(implementationEvidence);
2412
+ const deliveryEvidence = lifecycleDeliveryEvidence(step);
2413
+ if (deliveryEvidence) content.append(deliveryEvidence);
2351
2414
  const repairEvidence = lifecycleRepairEvidence(step);
2352
2415
  if (repairEvidence) content.append(repairEvidence);
2353
2416
  if (!modal.open) modal.showModal();
@@ -2364,15 +2427,24 @@ function lifecycleStepWithEvidence(step, context = {}) {
2364
2427
  : Array.isArray(context.reviewers) ? context.reviewers : [];
2365
2428
  if (reviewers.length) enriched.reviewer_evidence = reviewers;
2366
2429
  }
2367
- if (id === "EXECUTE_AGENT") {
2368
- const candidate = String(context.implementation_candidate_sha || "").trim();
2369
- const branch = String(context.implementation_branch || "").trim();
2370
- const pullRequest = Number(context.implementation_pr);
2430
+ const deliverySteps = {
2431
+ EXECUTE_AGENT: { role: "implementation", candidate: "implementation_candidate_sha", branch: "implementation_branch", pullRequest: "implementation_pr" },
2432
+ FINALIZE_AGENT: { role: "finalization", candidate: "finalization_candidate_sha", branch: "finalization_branch", pullRequest: "finalization_pr" },
2433
+ RECONCILE_AGENT: { role: "reconciliation", candidate: "reconciliation_candidate_sha", branch: "reconciliation_branch", pullRequest: "reconciliation_pr" },
2434
+ };
2435
+ const delivery = deliverySteps[id];
2436
+ if (delivery) {
2437
+ const candidate = String(context[delivery.candidate] || "").trim();
2438
+ const branch = String(context[delivery.branch] || "").trim();
2439
+ const pullRequest = Number(context[delivery.pullRequest]);
2440
+ const repository = String(context.github_repository || context.target_repository || "").trim();
2371
2441
  const evidence = {};
2442
+ evidence.role = delivery.role;
2372
2443
  if (/^[0-9a-f]{40}$/.test(candidate)) evidence.candidate_sha = candidate;
2373
2444
  if (branch) evidence.branch = branch;
2374
2445
  if (Number.isInteger(pullRequest) && pullRequest > 0) evidence.pull_request = pullRequest;
2375
- if (Object.keys(evidence).length) enriched.implementation_evidence = evidence;
2446
+ if (/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repository)) evidence.repository = repository;
2447
+ if (Object.keys(evidence).length > 1) enriched.delivery_evidence = evidence;
2376
2448
  }
2377
2449
  return enriched;
2378
2450
  }
@@ -8543,19 +8615,27 @@ function promptDetailRecommendationHandoff(handoff) {
8543
8615
  }
8544
8616
  function promptDetailReviewersSection(reviewers, { wide = true } = {}) {
8545
8617
  if (!reviewers.length) return null;
8546
- const fields = reviewers.map((reviewer) =>
8547
- detailField(
8548
- reviewerLabel(reviewer.reviewer, t("detail.specialist_review")),
8549
- t("detail.capability") + ": " +
8550
- reviewerCapabilityLabel(reviewer.capability || "ENGINEERING") + " · " +
8551
- reviewerStatusLabel(reviewer.status || "completed") + " · " +
8552
- t("detail.accepted_recommendations") + ": " +
8553
- (Number(reviewer.accepted_recommendations) || 0) + "\n" +
8554
- t("detail.selected_because") + ": " +
8618
+ const fields = reviewers.map((reviewer) => {
8619
+ const field = document.createElement("article"), heading = document.createElement("h4"),
8620
+ status = reviewerStatusPresentation(reviewer);
8621
+ field.className = "field prompt-detail-reviewer";
8622
+ heading.textContent = reviewerLabel(reviewer.reviewer, t("detail.specialist_review"));
8623
+ field.append(
8624
+ heading,
8625
+ status.label,
8626
+ status.row,
8627
+ detailField(
8628
+ t("detail.selected_because"),
8555
8629
  String(reviewer.selected_because || t("detail.not_recorded")),
8556
- true,
8557
- ),
8558
- );
8630
+ true,
8631
+ ),
8632
+ detailField(
8633
+ t("detail.accepted_recommendations"),
8634
+ String(Number(reviewer.accepted_recommendations) || 0),
8635
+ ),
8636
+ );
8637
+ return field;
8638
+ });
8559
8639
  return promptDetailCard(t("detail.specialist_reviews"), fields, wide, "prompt-detail-card--reviewers");
8560
8640
  }
8561
8641
  function promptDetailAssuranceReviewsSection(reviews) {
@@ -4273,8 +4273,13 @@ Object.assign(DASHBOARD_MESSAGES.es, {
4273
4273
  // A repair step can be entered before a pull request exists and shares its
4274
4274
  // bounded budget across local validation, assurance, hosted checks and
4275
4275
  // finalization. Keep the flow label generic; detail evidence names the cause.
4276
- Object.assign(DASHBOARD_MESSAGES.en, {"lifecycle.step.repair_agent":"Repair round","lifecycle.detail_repair_evidence":"Completed repair rounds","lifecycle.detail_implementation_evidence":"Implementation result","lifecycle.detail_validation_pass":"Validation pass {iteration}","detail.initial_observation":"Initial observation","detail.rejected_recommendations":"Rejected recommendations"});
4277
- Object.assign(DASHBOARD_MESSAGES.nl, {"lifecycle.step.repair_agent":"Herstelronde","lifecycle.detail_repair_evidence":"Uitgevoerde herstelrondes","lifecycle.detail_implementation_evidence":"Implementatieresultaat","lifecycle.detail_validation_pass":"Validatiepass {iteration}","detail.initial_observation":"Initiële bevinding","detail.rejected_recommendations":"Afgewezen aanbevelingen"});
4278
- Object.assign(DASHBOARD_MESSAGES.de, {"lifecycle.step.repair_agent":"Reparaturrunde","lifecycle.detail_repair_evidence":"Abgeschlossene Reparaturrunden","lifecycle.detail_implementation_evidence":"Implementierungsergebnis","lifecycle.detail_validation_pass":"Validierungsdurchlauf {iteration}","detail.initial_observation":"Erste Beobachtung","detail.rejected_recommendations":"Abgelehnte Empfehlungen"});
4279
- Object.assign(DASHBOARD_MESSAGES.fr, {"lifecycle.step.repair_agent":"Cycle de réparation","lifecycle.detail_repair_evidence":"Cycles de réparation terminés","lifecycle.detail_implementation_evidence":"Résultat de l’implémentation","lifecycle.detail_validation_pass":"Passe de validation {iteration}","detail.initial_observation":"Observation initiale","detail.rejected_recommendations":"Recommandations rejetées"});
4280
- Object.assign(DASHBOARD_MESSAGES.es, {"lifecycle.step.repair_agent":"Ronda de reparación","lifecycle.detail_repair_evidence":"Rondas de reparación completadas","lifecycle.detail_implementation_evidence":"Resultado de la implementación","lifecycle.detail_validation_pass":"Pasada de validación {iteration}","detail.initial_observation":"Observación inicial","detail.rejected_recommendations":"Recomendaciones rechazadas"});
4276
+ Object.assign(DASHBOARD_MESSAGES.en, {"lifecycle.step.repair_agent":"Repair round","lifecycle.detail_repair_evidence":"Completed repair rounds","lifecycle.detail_implementation_evidence":"Implementation result","lifecycle.detail_finalization_evidence":"Finalization result","lifecycle.detail_reconciliation_evidence":"Reconciliation result","lifecycle.detail_validation_pass":"Validation pass {iteration}","detail.initial_observation":"Initial observation","detail.rejected_recommendations":"Rejected recommendations"});
4277
+ Object.assign(DASHBOARD_MESSAGES.nl, {"lifecycle.step.repair_agent":"Herstelronde","lifecycle.detail_repair_evidence":"Uitgevoerde herstelrondes","lifecycle.detail_implementation_evidence":"Implementatieresultaat","lifecycle.detail_finalization_evidence":"Finalisatieresultaat","lifecycle.detail_reconciliation_evidence":"Reconciliatieresultaat","lifecycle.detail_validation_pass":"Validatiepass {iteration}","detail.initial_observation":"Initiële bevinding","detail.rejected_recommendations":"Afgewezen aanbevelingen"});
4278
+ Object.assign(DASHBOARD_MESSAGES.de, {"lifecycle.step.repair_agent":"Reparaturrunde","lifecycle.detail_repair_evidence":"Abgeschlossene Reparaturrunden","lifecycle.detail_implementation_evidence":"Implementierungsergebnis","lifecycle.detail_finalization_evidence":"Finalisierungsergebnis","lifecycle.detail_reconciliation_evidence":"Abstimmungsergebnis","lifecycle.detail_validation_pass":"Validierungsdurchlauf {iteration}","detail.initial_observation":"Erste Beobachtung","detail.rejected_recommendations":"Abgelehnte Empfehlungen"});
4279
+ Object.assign(DASHBOARD_MESSAGES.fr, {"lifecycle.step.repair_agent":"Cycle de réparation","lifecycle.detail_repair_evidence":"Cycles de réparation terminés","lifecycle.detail_implementation_evidence":"Résultat de l’implémentation","lifecycle.detail_finalization_evidence":"Résultat de la finalisation","lifecycle.detail_reconciliation_evidence":"Résultat de la réconciliation","lifecycle.detail_validation_pass":"Passe de validation {iteration}","detail.initial_observation":"Observation initiale","detail.rejected_recommendations":"Recommandations rejetées"});
4280
+ Object.assign(DASHBOARD_MESSAGES.es, {"lifecycle.step.repair_agent":"Ronda de reparación","lifecycle.detail_repair_evidence":"Rondas de reparación completadas","lifecycle.detail_implementation_evidence":"Resultado de la implementación","lifecycle.detail_finalization_evidence":"Resultado de la finalización","lifecycle.detail_reconciliation_evidence":"Resultado de la conciliación","lifecycle.detail_validation_pass":"Pasada de validación {iteration}","detail.initial_observation":"Observación inicial","detail.rejected_recommendations":"Recomendaciones rechazadas"});
4281
+ Object.assign(DASHBOARD_MESSAGES.en, {"lifecycle.assurance_integral_evidence":"Impact surfaces reviewed: {coverage} · Prior findings reassessed: {dispositions}"});
4282
+ Object.assign(DASHBOARD_MESSAGES.nl, {"lifecycle.assurance_integral_evidence":"Impactvlakken beoordeeld: {coverage} · Eerdere bevindingen herbeoordeeld: {dispositions}"});
4283
+ Object.assign(DASHBOARD_MESSAGES.de, {"lifecycle.assurance_integral_evidence":"Geprüfte Auswirkungsbereiche: {coverage} · Frühere Befunde erneut bewertet: {dispositions}"});
4284
+ Object.assign(DASHBOARD_MESSAGES.fr, {"lifecycle.assurance_integral_evidence":"Surfaces d’impact examinées : {coverage} · Constats antérieurs réévalués : {dispositions}"});
4285
+ Object.assign(DASHBOARD_MESSAGES.es, {"lifecycle.assurance_integral_evidence":"Superficies de impacto revisadas: {coverage} · Hallazgos anteriores reevaluados: {dispositions}"});
@@ -44,10 +44,47 @@ REVIEWER_LABELS = {
44
44
  "documentation": "Documentation Reviewer",
45
45
  "finalization": "Finalization Reviewer",
46
46
  }
47
- MANDATORY_REVIEW_OUTPUT_CONTRACT_VERSION = "1.0"
47
+ ADVISORY_REVIEW_OUTPUT_CONTRACT_VERSION = "1.0"
48
+ MANDATORY_REVIEW_OUTPUT_CONTRACT_VERSION = "2.0"
48
49
  MANDATORY_FINDING_FIELDS = frozenset({
49
50
  "id", "observation", "category", "criterion", "severity", "confidence", "evidence_ref",
50
51
  })
52
+ MANDATORY_COVERAGE_FIELDS = frozenset({"surface", "status", "evidence_ref"})
53
+ MANDATORY_DISPOSITION_FIELDS = frozenset({"finding_id", "disposition", "evidence_ref"})
54
+ MANDATORY_COVERAGE_SURFACES = {
55
+ "IMPLEMENTATION": {
56
+ "quality": (
57
+ "approved_criteria", "full_candidate_diff", "callers_and_consumers",
58
+ "service_lifecycle", "maintenance_recovery_cleanup", "persistence_concurrency",
59
+ "transport_schema_compatibility", "regression_validation",
60
+ ),
61
+ "security": (
62
+ "approved_criteria", "full_candidate_diff", "trust_authentication",
63
+ "data_integrity_concurrency", "maintenance_recovery_cleanup",
64
+ "failure_redaction", "availability_resource_bounds", "security_validation",
65
+ ),
66
+ },
67
+ "FINALIZATION": {
68
+ "quality": (
69
+ "delivery_role_scope", "full_candidate_diff", "record_consistency",
70
+ "lifecycle_state_claims", "regression_validation",
71
+ ),
72
+ "security": (
73
+ "delivery_role_scope", "full_candidate_diff", "authority_scope",
74
+ "sensitive_data", "lifecycle_state_claims", "security_validation",
75
+ ),
76
+ },
77
+ "RECONCILIATION": {
78
+ "quality": (
79
+ "delivery_role_scope", "full_candidate_diff", "record_consistency",
80
+ "lifecycle_state_claims", "regression_validation",
81
+ ),
82
+ "security": (
83
+ "delivery_role_scope", "full_candidate_diff", "authority_scope",
84
+ "sensitive_data", "lifecycle_state_claims", "security_validation",
85
+ ),
86
+ },
87
+ }
51
88
  PRODUCT_MATCHERS = {
52
89
  "apple_platform": (("apps/apple/", "engineering-platform-app", "swiftui", "watchos", "macos", "ios"), "Apple platform capability"),
53
90
  "windows_platform": (("apps/windows/", "engineering-platform-windows", "maui", "windows packaging"), "Windows platform capability"),
@@ -77,13 +114,15 @@ class ReviewerResult:
77
114
  # In-process deterministic/test adapters construct this typed result only
78
115
  # after choosing the mandatory contract. Raw provider JSON is still
79
116
  # required to carry this field by ``CodexCliClient.review``.
80
- contract_version: str | None = MANDATORY_REVIEW_OUTPUT_CONTRACT_VERSION
117
+ contract_version: str | None = ADVISORY_REVIEW_OUTPUT_CONTRACT_VERSION
81
118
  failed: bool = False
82
119
  usage: dict[str, object] = field(default_factory=dict)
83
120
  runtime_metadata: dict[str, object] = field(default_factory=dict)
84
121
  churn: dict[str, object] = field(default_factory=dict)
85
122
  duration_seconds: float | None = None
86
123
  usage_snapshots: tuple[dict[str, int], ...] = ()
124
+ coverage: tuple[dict[str, str], ...] = ()
125
+ finding_dispositions: tuple[dict[str, str], ...] = ()
87
126
 
88
127
 
89
128
  class ReviewerClient(Protocol):
@@ -164,6 +203,8 @@ def run_reviews(
164
203
  result.churn,
165
204
  result.duration_seconds,
166
205
  result.usage_snapshots,
206
+ tuple(dict(item) for item in result.coverage if isinstance(item, dict)),
207
+ tuple(dict(item) for item in result.finding_dispositions if isinstance(item, dict)),
167
208
  )
168
209
  except Exception: # Reviewer failure is advisory and cannot block the transaction.
169
210
  result = ReviewerResult(selection.reviewer, "Reviewer failed; primary review continues.", failed=True)
@@ -192,7 +233,8 @@ def mandatory_findings(result: ReviewerResult) -> tuple[dict[str, str], ...] | N
192
233
  """Return validated mandatory output, never advice-shaped pseudo-evidence."""
193
234
  if result.failed or result.contract_version != MANDATORY_REVIEW_OUTPUT_CONTRACT_VERSION:
194
235
  return None
195
- if not isinstance(result.findings, tuple) or len(result.findings) > 64:
236
+ finding_limit = 12 if result.reviewer in {"quality", "security"} else 64
237
+ if not isinstance(result.findings, tuple) or len(result.findings) > finding_limit:
196
238
  return None
197
239
  normalized: list[dict[str, str]] = []
198
240
  seen: set[str] = set()
@@ -210,6 +252,65 @@ def mandatory_findings(result: ReviewerResult) -> tuple[dict[str, str], ...] | N
210
252
  return tuple(normalized)
211
253
 
212
254
 
255
+ def mandatory_coverage_surfaces(reviewer: str, delivery_role: str) -> tuple[str, ...]:
256
+ """Return the host-owned complete impact checklist for one assurance role."""
257
+ return MANDATORY_COVERAGE_SURFACES.get(delivery_role, {}).get(reviewer, ())
258
+
259
+
260
+ def mandatory_assessment(
261
+ result: ReviewerResult,
262
+ *,
263
+ delivery_role: str,
264
+ prior_finding_ids: tuple[str, ...],
265
+ ) -> tuple[tuple[dict[str, str], ...], tuple[dict[str, str], ...], tuple[dict[str, str], ...]] | None:
266
+ """Validate complete impact coverage and explicit prior-finding closure.
267
+
268
+ A syntactically valid list of findings is insufficient for mandatory
269
+ assurance. Each reviewer must cover every host-owned impact surface and
270
+ explicitly reassess every still-open finding previously raised by that
271
+ same independent role.
272
+ """
273
+ findings = mandatory_findings(result)
274
+ required_surfaces = mandatory_coverage_surfaces(result.reviewer, delivery_role)
275
+ if findings is None or not required_surfaces:
276
+ return None
277
+ coverage = result.coverage
278
+ dispositions = result.finding_dispositions
279
+ if (
280
+ not isinstance(coverage, tuple)
281
+ or len(coverage) != len(required_surfaces)
282
+ or any(not isinstance(item, dict) or set(item) != MANDATORY_COVERAGE_FIELDS for item in coverage)
283
+ or any(
284
+ item["surface"] not in required_surfaces
285
+ or item["status"] not in {"REVIEWED", "NOT_APPLICABLE"}
286
+ or not isinstance(item["evidence_ref"], str) or not item["evidence_ref"].strip()
287
+ or len(item["evidence_ref"]) > 240
288
+ for item in coverage
289
+ )
290
+ or {item["surface"] for item in coverage} != set(required_surfaces)
291
+ ):
292
+ return None
293
+ if (
294
+ not isinstance(dispositions, tuple)
295
+ or len(dispositions) != len(prior_finding_ids)
296
+ or any(not isinstance(item, dict) or set(item) != MANDATORY_DISPOSITION_FIELDS for item in dispositions)
297
+ or any(
298
+ item["finding_id"] not in prior_finding_ids
299
+ or item["disposition"] not in {"RESOLVED", "OPEN"}
300
+ or not isinstance(item["evidence_ref"], str) or not item["evidence_ref"].strip()
301
+ or len(item["evidence_ref"]) > 240
302
+ for item in dispositions
303
+ )
304
+ or {item["finding_id"] for item in dispositions} != set(prior_finding_ids)
305
+ ):
306
+ return None
307
+ return (
308
+ findings,
309
+ tuple({key: redact_diagnostic(value, limit=240) for key, value in item.items()} for item in coverage),
310
+ tuple({key: redact_diagnostic(value, limit=240) for key, value in item.items()} for item in dispositions),
311
+ )
312
+
313
+
213
314
  def records_for_storage(selections: tuple[ReviewerSelection, ...], results: tuple[ReviewerResult, ...]) -> tuple[dict[str, object], ...]:
214
315
  by_reviewer = {result.reviewer: result for result in results}
215
316
  records: list[dict[str, object]] = []
@@ -29,14 +29,15 @@ RunJsonReader = Callable[[Path, str | None], bytes]
29
29
  TERMINAL_PHASES = frozenset({"COMPLETE", "BLOCKED", "FAILED"})
30
30
 
31
31
 
32
- def _successful_reviewer_agents(value: object) -> list[dict[str, object]]:
33
- """Expose only a wholly successful review beyond its live phase."""
32
+ def _terminal_reviewer_agents(value: object) -> list[dict[str, object]]:
33
+ """Expose a wholly terminal review beyond its live phase."""
34
34
  if not isinstance(value, list):
35
35
  return []
36
36
  reviewers = [item for item in value if isinstance(item, dict)]
37
37
  if not reviewers or len(reviewers) != len(value):
38
38
  return []
39
- return reviewers if all(item.get("status") == "completed" for item in reviewers) else []
39
+ terminal = {"completed", "failed"}
40
+ return reviewers if all(item.get("status") in terminal for item in reviewers) else []
40
41
 
41
42
 
42
43
  def _run_workspace_preflight(root: Path, run_id: object) -> dict[str, object]:
@@ -417,13 +418,13 @@ def status(root: Path) -> bytes:
417
418
  "candidate_sha": live.get("candidate_sha"),
418
419
  "repair_iteration": live.get("repair_iteration"),
419
420
  "workspace_preflight": run_workspace_preflight,
420
- # Only a wholly successful specialist review survives its
421
- # live phase as historical evidence for this active run. A
422
- # partial, failed or stale reviewer projection remains hidden.
421
+ # Only a wholly terminal specialist review survives its live
422
+ # phase as historical evidence for this active run. A running
423
+ # or malformed reviewer projection remains hidden.
423
424
  "reviewer_agents": (
424
425
  live.get("reviewer_agents", [])
425
426
  if live.get("phase") == "CAPABILITY_REVIEW"
426
- else _successful_reviewer_agents(live.get("reviewer_agents"))
427
+ else _terminal_reviewer_agents(live.get("reviewer_agents"))
427
428
  ),
428
429
  "runtime_metadata": live.get("runtime_metadata", {}),
429
430
  "workspace_progress": live.get("workspace_progress"),