engineering-platform 2.2.0__tar.gz → 2.3.1__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 (140) hide show
  1. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/PKG-INFO +1 -1
  2. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/pyproject.toml +1 -1
  3. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/ENGINEERING_PLATFORM_CONFIG.json +10 -3
  4. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/ENGINEERING_PLATFORM_VERSION.json +4 -4
  5. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/agent_state.py +79 -1
  6. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/assets/dashboard.css +19 -7
  7. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/assets/dashboard.js +246 -19
  8. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/assets/dashboard_locales.mjs +90 -0
  9. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/capability_preflight.py +10 -1
  10. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/capability_review.py +37 -1
  11. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/central_data_transfer.py +117 -22
  12. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/central_database.py +23 -1
  13. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/component_logging.py +30 -4
  14. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/console_route_ownership.py +1 -0
  15. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/dependabot_producer.py +2 -2
  16. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/execution_executor.py +44 -4
  17. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/execution_host.py +364 -107
  18. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/execution_lifecycle.py +8 -0
  19. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/execution_models.py +1 -0
  20. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/execution_repository.py +91 -2
  21. engineering_platform-2.3.1/src/engineering_platform/installation_relocation.py +212 -0
  22. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/lifecycle_worker.py +5 -2
  23. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/managed_autonomy.py +3 -1
  24. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/parity_context.py +4 -2
  25. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/parity_lifecycle_dispatcher.py +58 -16
  26. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/platform_bootstrap.py +16 -0
  27. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/platform_version.py +1 -1
  28. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/provider_readiness.py +15 -4
  29. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/provider_recovery.py +25 -9
  30. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/providers.py +2 -2
  31. engineering_platform-2.3.1/src/engineering_platform/qualification_runtime.py +189 -0
  32. engineering_platform-2.3.1/src/engineering_platform/schemas/producer-readback-v1.2.schema.json +34 -0
  33. engineering_platform-2.3.1/src/engineering_platform/schemas/producer-readback-v1.schema.json +1 -0
  34. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/server.py +399 -33
  35. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/server_service.py +39 -0
  36. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/storage.py +2 -1
  37. engineering_platform-2.3.1/src/engineering_platform/submission_service.py +805 -0
  38. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/templates/workspace-config.json +18 -4
  39. engineering_platform-2.3.1/src/engineering_platform/version_preparation_delivery.py +437 -0
  40. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform.egg-info/PKG-INFO +1 -1
  41. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform.egg-info/SOURCES.txt +4 -0
  42. engineering_platform-2.2.0/src/engineering_platform/installation_relocation.py +0 -122
  43. engineering_platform-2.2.0/src/engineering_platform/submission_service.py +0 -317
  44. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/README.md +0 -0
  45. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/setup.cfg +0 -0
  46. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/__init__.py +0 -0
  47. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/__main__.py +0 -0
  48. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/agent_trust.py +0 -0
  49. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/assets/dashboard_status_store.mjs +0 -0
  50. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/assets/operations-console/apple-touch-icon-dark.png +0 -0
  51. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/assets/operations-console/apple-touch-icon-light.png +0 -0
  52. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/assets/operations-console/icon-dark.png +0 -0
  53. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/assets/operations-console/icon-light.png +0 -0
  54. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/assets/operations-console/icon-transparent.png +0 -0
  55. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/assets/operations-console/manifest.webmanifest +0 -0
  56. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/central_store_migration.py +0 -0
  57. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/codex_capacity.py +0 -0
  58. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/codex_chat.py +0 -0
  59. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/codex_observability.py +0 -0
  60. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/component_lock.py +0 -0
  61. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/console_presentation.py +0 -0
  62. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/contracts/__init__.py +0 -0
  63. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/contracts/ep_consumer.py +0 -0
  64. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/contracts/models.py +0 -0
  65. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/contracts/projection.py +0 -0
  66. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/dashboard_browser_validation.py +0 -0
  67. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/dashboard_state.py +0 -0
  68. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/dashboard_supervisor.swift +0 -0
  69. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/dashboard_translation.py +0 -0
  70. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/drift_diagnostics.py +0 -0
  71. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/emergency_recovery.py +0 -0
  72. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/engineering_memory.py +0 -0
  73. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/ep_consumer_credentials.py +0 -0
  74. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/evidence_projection.py +0 -0
  75. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/execution_activity.py +0 -0
  76. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/execution_context.py +0 -0
  77. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/execution_errors.py +0 -0
  78. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/execution_evidence.py +0 -0
  79. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/execution_finalization.py +0 -0
  80. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/execution_lease.py +0 -0
  81. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/execution_readiness.py +0 -0
  82. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/execution_reporting.py +0 -0
  83. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/execution_timeout_policy.py +0 -0
  84. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/execution_timing.py +0 -0
  85. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/execution_transaction.py +0 -0
  86. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/external_producer_binding.py +0 -0
  87. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/file_inbox.py +0 -0
  88. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/forensic_attribution.py +0 -0
  89. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/forensic_attribution_v2.py +0 -0
  90. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/forensic_delta.py +0 -0
  91. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/golden_scenario.py +0 -0
  92. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/historical_dashboard_configuration.py +0 -0
  93. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/host_admin.py +0 -0
  94. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/host_preflight.py +0 -0
  95. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/investigation_ledger.py +0 -0
  96. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/legacy_inbox_migration.py +0 -0
  97. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/live_status.py +0 -0
  98. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/local_api.py +0 -0
  99. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/local_api_keychain.py +0 -0
  100. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/local_repository_binding.py +0 -0
  101. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/managed_codex_runtime.py +0 -0
  102. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/platform_admin.py +0 -0
  103. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/platform_api.py +0 -0
  104. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/platform_components.py +0 -0
  105. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/pr_check_repair.py +0 -0
  106. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/pr_evidence_backfill.py +0 -0
  107. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/producer.py +0 -0
  108. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/project_agent.py +0 -0
  109. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/project_agent_service.py +0 -0
  110. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/project_topology.py +0 -0
  111. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/prompt_history.py +0 -0
  112. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/provider_context.py +0 -0
  113. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/provider_context_benchmark.py +0 -0
  114. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/provider_context_scope.py +0 -0
  115. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/provider_interruption.py +0 -0
  116. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/provider_process_identity.py +0 -0
  117. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/provider_usage.py +0 -0
  118. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/qualification.py +0 -0
  119. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/recommendation_handoff.py +0 -0
  120. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/report_analysis.py +0 -0
  121. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/repository_attachment.py +0 -0
  122. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/repository_handoff.py +0 -0
  123. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/resources.py +0 -0
  124. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/reviewer_evidence.py +0 -0
  125. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/schemas/repository-attachment.schema.json +0 -0
  126. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/server_console_services.py +0 -0
  127. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/server_relay.py +0 -0
  128. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/status_model.py +0 -0
  129. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/status_reconciliation.py +0 -0
  130. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/submission_cli.py +0 -0
  131. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/submission_intake.py +0 -0
  132. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/telemetry.py +0 -0
  133. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/validation_identity.py +0 -0
  134. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/validation_profile.py +0 -0
  135. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/workspace_preflight.py +0 -0
  136. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/worktree_provenance.py +0 -0
  137. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform/worktree_tooling.py +0 -0
  138. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform.egg-info/dependency_links.txt +0 -0
  139. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform.egg-info/entry_points.txt +0 -0
  140. {engineering_platform-2.2.0 → engineering_platform-2.3.1}/src/engineering_platform.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: engineering-platform
3
- Version: 2.2.0
3
+ Version: 2.3.1
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.2.0"
7
+ version = "2.3.1"
8
8
  description = "Local-first Engineering Platform execution operations runtime"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -3,7 +3,7 @@
3
3
  "platform": {
4
4
  "id": "engineering-platform",
5
5
  "name": "Engineering Platform",
6
- "version": "2.2.0",
6
+ "version": "2.3.1",
7
7
  "generation": 2,
8
8
  "documentation_namespace": "engineering-platform",
9
9
  "capability_registry_version": 1
@@ -11,8 +11,15 @@
11
11
  "workspace": {
12
12
  "id": "engineering-platform",
13
13
  "name": "Engineering Platform",
14
- "repository": {"provider": "github", "owner": "pcvantol", "name": "engineering-platform", "default_branch": "main"},
15
- "branding": {"dashboard_title": "EP Operations"},
14
+ "repository": {
15
+ "provider": "github",
16
+ "owner": "pcvantol",
17
+ "name": "engineering-platform",
18
+ "default_branch": "main"
19
+ },
20
+ "branding": {
21
+ "dashboard_title": "EP Operations"
22
+ },
16
23
  "workspace_authorization": {
17
24
  "allowed_roots": [],
18
25
  "allowed_repositories": [],
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "bootstrap_contract": "2026.12",
3
3
  "checkpoint_format": 1,
4
- "dashboard_version": "2.2.0",
4
+ "dashboard_version": "2.3.1",
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.2.0",
9
+ "platform_version": "2.3.1",
10
10
  "report_format": 2,
11
- "runner_version": "2.2.0",
11
+ "runner_version": "2.3.1",
12
12
  "status_model": 1,
13
13
  "storage_schema": 41,
14
- "watcher_version": "2.2.0"
14
+ "watcher_version": "2.3.1"
15
15
  }
@@ -135,6 +135,12 @@ class TransactionState:
135
135
  agent_execution_seconds: float | None = None
136
136
  validation_evidence: tuple[dict[str, str], ...] = ()
137
137
  quality_evidence: tuple[dict[str, str], ...] = ()
138
+ # Versioned post-implementation assurance. These records are immutable
139
+ # observations; a later review appends a new record instead of changing an
140
+ # earlier finding into a pass.
141
+ assurance_profile: dict[str, str] | None = None
142
+ assurance_reviews: tuple[dict[str, object], ...] = ()
143
+ assurance_resolutions: tuple[dict[str, str], ...] = ()
138
144
  repair_iterations: int = 0
139
145
  repair_audit: tuple[dict[str, str], ...] = ()
140
146
  local_validation_iterations: int = 0
@@ -172,6 +178,9 @@ class TransactionState:
172
178
  "agent_execution_seconds": None,
173
179
  "validation_evidence": (),
174
180
  "quality_evidence": (),
181
+ "assurance_profile": None,
182
+ "assurance_reviews": (),
183
+ "assurance_resolutions": (),
175
184
  "repair_iterations": 0,
176
185
  "repair_audit": (),
177
186
  "local_validation_iterations": 0,
@@ -194,6 +203,10 @@ class TransactionState:
194
203
  raw = {**raw, "validation_evidence": tuple(raw["validation_evidence"])}
195
204
  if isinstance(raw.get("quality_evidence"), list):
196
205
  raw = {**raw, "quality_evidence": tuple(raw["quality_evidence"])}
206
+ if isinstance(raw.get("assurance_reviews"), list):
207
+ raw = {**raw, "assurance_reviews": tuple(raw["assurance_reviews"])}
208
+ if isinstance(raw.get("assurance_resolutions"), list):
209
+ raw = {**raw, "assurance_resolutions": tuple(raw["assurance_resolutions"])}
197
210
  if isinstance(raw.get("repair_audit"), list):
198
211
  raw = {**raw, "repair_audit": tuple(raw["repair_audit"])}
199
212
  if isinstance(raw.get("local_validation_audit"), list):
@@ -274,17 +287,24 @@ class TransactionState:
274
287
  )
275
288
  ):
276
289
  raise StateError("checkpoint local validation audit is invalid or unsafe")
290
+ repair_audit_fields = audit_fields | {"repair_id", "origin", "input_candidate_sha", "dispatch_id"}
277
291
  if (
278
292
  not isinstance(state.repair_audit, tuple)
279
293
  or len(state.repair_audit) > 3
280
294
  or any(
281
- not isinstance(item, dict) or set(item) != audit_fields
295
+ not isinstance(item, dict) or set(item) not in (audit_fields, repair_audit_fields)
282
296
  or not all(isinstance(value, str) and value and len(value) <= MAX_DIAGNOSTIC_LENGTH and value == redact_diagnostic(value) for value in item.values())
283
297
  or not item["iteration"].isdigit() or int(item["iteration"]) < 1
284
298
  or item["outcome"] not in {"planned", "submitted_for_recheck", "agent_failed", "agent_timed_out"}
285
299
  or (item["commit_sha"] != "not_recorded" and not re.fullmatch(r"[0-9a-f]{40}", item["commit_sha"]))
300
+ or (set(item) == repair_audit_fields and (
301
+ not re.fullmatch(r"repair:[A-Za-z0-9_.:-]+:[1-3]", item["repair_id"])
302
+ or item["origin"] not in {"validation", "quality", "security", "hosted", "finalization"}
303
+ or (item["input_candidate_sha"] != "not_recorded" and not re.fullmatch(r"[0-9a-f]{40}", item["input_candidate_sha"]))
304
+ ))
286
305
  for item in state.repair_audit
287
306
  )
307
+ or len({item.get("repair_id", f"legacy:{item['iteration']}") for item in state.repair_audit}) != len(state.repair_audit)
288
308
  ):
289
309
  raise StateError("checkpoint repair audit is invalid or unsafe")
290
310
  if (
@@ -357,6 +377,64 @@ class TransactionState:
357
377
  )
358
378
  ):
359
379
  raise StateError("checkpoint quality evidence is invalid or unsafe")
380
+ profile_fields = {"version", "digest", "candidate_sha"}
381
+ current_profile_fields = profile_fields | {"criteria_digest"}
382
+ if state.assurance_profile is not None and (
383
+ not isinstance(state.assurance_profile, dict)
384
+ or set(state.assurance_profile) not in (profile_fields, current_profile_fields)
385
+ or not all(isinstance(value, str) and value for value in state.assurance_profile.values())
386
+ or not re.fullmatch(r"sha256:[0-9a-f]{64}", state.assurance_profile["digest"])
387
+ or not re.fullmatch(r"[0-9a-f]{40}", state.assurance_profile["candidate_sha"])
388
+ ):
389
+ raise StateError("checkpoint assurance profile is invalid")
390
+ review_fields = {"reviewer", "status", "candidate_sha", "profile_digest", "invocation_id", "findings"}
391
+ current_review_fields = review_fields | {"contract_version", "started_at", "completed_at"}
392
+ finding_fields = {"id", "fingerprint", "category", "criterion", "observation", "severity", "confidence", "blocking", "disposition"}
393
+ current_finding_fields = finding_fields | {"evidence_ref"}
394
+ if (
395
+ not isinstance(state.assurance_reviews, tuple)
396
+ or len(state.assurance_reviews) > 16
397
+ or any(
398
+ not isinstance(review, dict) or set(review) not in (review_fields, current_review_fields)
399
+ or review.get("reviewer") not in {"quality", "security"}
400
+ or review.get("status") not in {"PASS", "FAIL", "UNRESOLVED"}
401
+ or not isinstance(review.get("candidate_sha"), str) or not re.fullmatch(r"[0-9a-f]{40}", review["candidate_sha"])
402
+ or not isinstance(review.get("profile_digest"), str) or not re.fullmatch(r"sha256:[0-9a-f]{64}", review["profile_digest"])
403
+ or not isinstance(review.get("invocation_id"), str) or not review["invocation_id"]
404
+ or not isinstance(review.get("findings"), list) or len(review["findings"]) > 12
405
+ or any(
406
+ not isinstance(finding, dict) or set(finding) not in (finding_fields, current_finding_fields)
407
+ or not all(isinstance(value, str) and value and len(value) <= 240 and value == redact_diagnostic(value, limit=240) for key, value in finding.items() if key != "blocking")
408
+ or finding.get("severity") not in {"LOW", "MEDIUM", "HIGH", "CRITICAL"}
409
+ or finding.get("confidence") not in {"LOW", "MEDIUM", "HIGH"}
410
+ or finding.get("disposition") not in {"OPEN", "RESOLVED", "REJECTED", "NON_BLOCKING"}
411
+ or not isinstance(finding.get("blocking"), bool)
412
+ or ("evidence_ref" in finding and (not isinstance(finding["evidence_ref"], str) or not finding["evidence_ref"]))
413
+ for finding in review["findings"]
414
+ )
415
+ or (set(review) == current_review_fields and (
416
+ review.get("contract_version") != "1.0"
417
+ or not isinstance(review.get("started_at"), str)
418
+ or not isinstance(review.get("completed_at"), str)
419
+ ))
420
+ for review in state.assurance_reviews
421
+ )
422
+ ):
423
+ raise StateError("checkpoint assurance review evidence is invalid")
424
+ resolution_fields = {"finding_id", "disposition", "resolution_ref", "candidate_sha"}
425
+ if (
426
+ not isinstance(state.assurance_resolutions, tuple)
427
+ or len(state.assurance_resolutions) > 128
428
+ or any(
429
+ not isinstance(item, dict) or set(item) != resolution_fields
430
+ or not all(isinstance(value, str) and value and len(value) <= MAX_DIAGNOSTIC_LENGTH and value == redact_diagnostic(value) for value in item.values())
431
+ or item["disposition"] not in {"RESOLVED", "REJECTED", "RISK_ACCEPTED"}
432
+ or not re.fullmatch(r"[0-9a-f]{40}", item["candidate_sha"])
433
+ for item in state.assurance_resolutions
434
+ )
435
+ or len({item["finding_id"] for item in state.assurance_resolutions}) != len(state.assurance_resolutions)
436
+ ):
437
+ raise StateError("checkpoint assurance resolutions are invalid or unsafe")
360
438
  if not isinstance(state.terminal, bool) or state.terminal != (state.phase in {"COMPLETE", "BLOCKED", "FAILED"}):
361
439
  raise StateError("checkpoint terminal flag conflicts with phase")
362
440
  return state
@@ -332,6 +332,7 @@ html[data-theme="light"] #promptHistory .execution-history-action{background:#ff
332
332
  .confirmation-modal h2[data-modal-glyph="question"]::before{align-items:center;border:2px solid currentColor;border-radius:50%;box-sizing:border-box;display:inline-flex;height:var(--modal-title-glyph-size);justify-content:center;width:var(--modal-title-glyph-size);vertical-align:-.12em}
333
333
  .confirmation-modal h2[data-modal-glyph="question"]::before{content:"?"}
334
334
  .confirmation-modal h2[data-modal-glyph="warning"]::before{content:"⚠"}
335
+ .installation-relocation-modal h2[data-modal-glyph="warning"]::before{content:"!" !important}
335
336
  .report-view-modal__title[data-modal-glyph="analysis"]::before{content:"✦"}
336
337
  :is(.component-modal h2,.confirmation-modal h2,.report-view-modal__title,.prompt-detail-modal__header h2,.prompt-chat-modal__header h2){font-size:clamp(18px,2vw,20px);line-height:1.2}
337
338
 
@@ -850,6 +851,7 @@ html[data-theme="light"] .dashboard-modal-shell :is(.label,.field>.label){color:
850
851
  actions therefore use the shared orange action language rather than blue. */
851
852
  #operatorMergeWaitModal .dashboard-modal-shell__action{text-decoration:none}
852
853
  #confirmationModal.dashboard-modal-shell--destructive .dashboard-modal-shell__action--destructive{background:#3a2028!important;border-color:#ff718f!important;color:#ffd9e1!important}#confirmationModal.dashboard-modal-shell--destructive .dashboard-modal-shell__action--destructive:hover:not(:disabled){background:#ff718f!important;border-color:#ff718f!important;color:#23131a!important}html[data-theme="light"] #confirmationModal.dashboard-modal-shell--destructive .dashboard-modal-shell__action--destructive{background:#fff1f4!important;border-color:#ff718f!important;color:#b32649!important}
854
+ #centralDataImportWarningModal .dashboard-modal-shell__action--destructive{background:#3a2028!important;border-color:#ff718f!important;color:#ffd9e1!important}#centralDataImportWarningModal .dashboard-modal-shell__action--destructive:hover:not(:disabled){background:#ff718f!important;border-color:#ff718f!important;color:#23131a!important}html[data-theme="light"] #centralDataImportWarningModal .dashboard-modal-shell__action--destructive{background:#fff1f4!important;border-color:#ff718f!important;color:#b32649!important}
853
855
  #operatorMergeWaitModal .dashboard-modal-shell__action{border-color:#f0b66a!important;color:#fff0dc!important}#operatorMergeWaitModal .dashboard-modal-shell__action--primary{background:#3b281b!important;border-color:#f0b66a!important;color:#fff0dc!important}#operatorMergeWaitModal .dashboard-modal-shell__action--destructive{background:#3a2028!important;border-color:#ff718f!important;color:#ffd9e1!important}#operatorMergeWaitModal .dashboard-modal-shell__action--primary:hover:not(:disabled){background:var(--house-style)!important;border-color:var(--house-style)!important;color:var(--button-ink)!important}#operatorMergeWaitModal .dashboard-modal-shell__action--destructive:hover:not(:disabled){background:#ff718f!important;border-color:#ff718f!important;color:#23131a!important}html[data-theme="light"] #operatorMergeWaitModal .dashboard-modal-shell__action{background:#fff!important;border-color:#d68b23!important;color:#643a13!important}html[data-theme="light"] #operatorMergeWaitModal .dashboard-modal-shell__action--primary{background:#fff8ef!important}html[data-theme="light"] #operatorMergeWaitModal .dashboard-modal-shell__action--destructive{background:#fff1f4!important;border-color:#ff718f!important;color:#b32649!important}
854
856
  /* Every modal uses a header tint derived from its own accent. The component
855
857
  modal retains the same structure, with extra space below its ruler. */
@@ -869,7 +871,7 @@ html:not([data-theme="light"]) .dashboard-modal-shell{--modal-header-surface:col
869
871
  .predecessor-retry::before{content:"↻";font:700 17px/1 system-ui;margin-right:8px;vertical-align:-1px}
870
872
  .execution-dismiss::before{content:"⊘"}
871
873
  #componentLogs .log-table{table-layout:auto;width:100%}#componentLogs .log-table th:last-child,#componentLogs .log-table td:last-child{width:100%}
872
- #queueList .queue-item{grid-template-columns:1.25rem minmax(0,1fr) auto}.queue-defer{background:#3b281b;border:1px solid #f0b66a;border-radius:8px;color:#fff0dc;font:600 12px system-ui;min-height:32px;padding:5px 9px}.queue-defer:hover:not(:disabled){background:var(--house-style)!important;border-color:var(--house-style)!important;color:#201812!important}.queue-defer:disabled{cursor:wait;opacity:.7}html[data-theme="light"] .queue-defer{background:#fff8ef;border-color:#d68b23;color:#643a13}html[data-theme="light"] .queue-defer:hover:not(:disabled){background:var(--house-style)!important;border-color:var(--house-style)!important;color:#201812!important}
874
+ #queueList .queue-item{grid-template-columns:1.25rem minmax(0,1fr) auto}.queue-item__actions{align-items:flex-end;display:flex;flex-direction:column;gap:6px;justify-self:end}.queue-defer{background:#3b281b;border:1px solid #f0b66a;border-radius:8px;color:#fff0dc;font:600 12px system-ui;min-height:32px;padding:5px 9px}.queue-defer:hover:not(:disabled){background:var(--house-style)!important;border-color:var(--house-style)!important;color:#201812!important}.queue-defer:disabled{cursor:wait;opacity:.7}.queue-defer--destructive{background:#3a2028;border-color:#ff718f;color:#ffd9e1}.queue-defer--destructive:hover:not(:disabled){background:#ff718f!important;border-color:#ff718f!important;color:#23131a!important}html[data-theme="light"] .queue-defer{background:#fff8ef;border-color:#d68b23;color:#643a13}html[data-theme="light"] .queue-defer:hover:not(:disabled){background:var(--house-style)!important;border-color:var(--house-style)!important;color:#201812!important}html[data-theme="light"] .queue-defer--destructive{background:#fff1f4;border-color:#ff718f;color:#b32649}html[data-theme="light"] .queue-defer--destructive:hover:not(:disabled){background:#ff718f!important;border-color:#ff718f!important;color:#23131a!important}
873
875
 
874
876
  /* Shared semantic action variants prevent individual surfaces from drifting. */
875
877
  .dashboard-action{align-items:center;border:1px solid;border-radius:50%;box-sizing:border-box;display:inline-flex;font:18px/1 system-ui;height:32px;justify-content:center;min-height:32px;min-width:32px;padding:0;width:32px}
@@ -970,7 +972,7 @@ html[data-theme="light"] ::-webkit-scrollbar-thumb:hover{background:#0a6b9d}
970
972
  /* Keep the input contract last: individual cards may colour their content,
971
973
  but they must never replace the selected-control border. */
972
974
  :where(input,select,textarea):is(:focus,:focus-visible){border-color:var(--dashboard-input-focus-border)!important;outline:var(--dashboard-focus-outline-width) solid var(--dashboard-input-focus-border)!important;outline-offset:var(--dashboard-focus-outline-offset)!important;box-shadow:0 0 0 var(--dashboard-focus-ring-size) var(--dashboard-input-focus-ring)!important}
973
- :where(button,summary,[role="button"],[tabindex]):not(#engineering-dashboard-content):not(:where(.dashboard-modal-shell,.component-log-row,.prompt-history-row,.telemetry-row,.execution-lifecycle__node,.theme-toggle,.section-state-toggle,.auto-refresh-toggle,.log-sortable,.log-table-wrap,.telemetry-scroll,.telemetry-detail-table-scroll)):is(:focus,:focus-visible){outline:var(--dashboard-focus-outline-width) solid var(--dashboard-input-focus-border)!important;outline-offset:var(--dashboard-focus-outline-offset)!important;box-shadow:0 0 0 var(--dashboard-focus-ring-size) var(--dashboard-input-focus-ring)!important}
975
+ :where(button,summary,[role="button"],[tabindex]):not(#engineering-dashboard-content):not(:where(.dashboard-modal-shell,.component-log-row,.prompt-history-row,.telemetry-row,.execution-lifecycle__node,.local-folder-link,.theme-toggle,.section-state-toggle,.auto-refresh-toggle,.log-sortable,.log-table-wrap,.telemetry-scroll,.telemetry-detail-table-scroll)):is(:focus,:focus-visible){outline:var(--dashboard-focus-outline-width) solid var(--dashboard-input-focus-border)!important;outline-offset:var(--dashboard-focus-outline-offset)!important;box-shadow:0 0 0 var(--dashboard-focus-ring-size) var(--dashboard-input-focus-ring)!important}
974
976
  /* Every dialog owns a full-viewport, safe-area-aware shell. The visible panel
975
977
  is therefore always separated from iPhone edges, including modal families
976
978
  added after the original report/detail implementations. */
@@ -994,8 +996,14 @@ html[data-theme="light"] ::-webkit-scrollbar-thumb:hover{background:#0a6b9d}
994
996
  /* Relocation and component actions keep their position while optional actions change. */
995
997
  .component-modal__actions{display:grid;gap:10px;grid-template-columns:repeat(2,minmax(0,1fr));margin-top:18px}.component-modal__actions .component-modal__restart{margin:0;min-height:44px;min-width:0;width:100%}.component-modal__actions .dashboard-modal-shell__action{grid-column:2;margin:0;min-width:0;width:100%}.installation-relocation-modal{padding:max(16px,env(safe-area-inset-top)) max(16px,env(safe-area-inset-right)) max(16px,env(safe-area-inset-bottom)) max(16px,env(safe-area-inset-left))!important}.installation-relocation-modal .dashboard-modal-shell__panel{display:grid;gap:18px;overflow:hidden}.installation-relocation-modal .dashboard-modal-shell__panel>p{margin:0}.installation-relocation-modal__locations{display:grid;gap:12px;margin:0}.installation-relocation-modal__locations div{display:grid;gap:4px}.installation-relocation-modal__locations dt{color:var(--modal-secondary-accent);font-size:12px;font-weight:700;letter-spacing:.05em;text-transform:uppercase}.installation-relocation-modal__locations dd{margin:0;overflow-wrap:anywhere}.installation-relocation-modal .dashboard-modal-shell__actions{justify-content:flex-start;margin-top:0}.installation-relocation-modal .dashboard-modal-shell__action{min-width:10rem}.configuration-central-database__actions{align-items:center;display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end}
996
998
  .footer__version-word{font-weight:400;text-transform:none}
999
+ #centralDataImportModal{--modal-panel-max-width:1100px}
1000
+ #centralDataImportStatus.configuration-central-data-import__error{color:#ff718f;font-weight:600}
1001
+ #centralDataImport::before{content:"↑"}
1002
+ #centralDataImportModal .dashboard-modal-shell__header h2::before{content:"↑"}
1003
+ #centralDatabaseRelocateModal .dashboard-modal-shell__header h2::before{content:"↗"}
1004
+ .installation-relocation-modal .dashboard-modal-shell__action:disabled{background:#35353d!important;border-color:#686873!important;color:#aaaab4!important;cursor:not-allowed}
997
1005
  .installation-relocation-modal .dashboard-modal-shell__header{grid-template-columns:minmax(0,1fr) auto}.installation-relocation-modal .dashboard-modal-shell__header h2::before{content:"⚙︎";display:inline-block;font-size:20px;line-height:1;margin-right:.42em;vertical-align:-.04em}
998
- .configuration-central-database__location-value{align-items:center;display:flex;flex-wrap:wrap;gap:10px}.configuration-central-database__location-link{flex:1 1 28rem}.configuration-central-database__export,.configuration-central-database__relocate{align-items:center;background-color:color-mix(in srgb,currentColor 14%,var(--dashboard-button-fill-surface));border:1px solid var(--house-style);border-radius:7px;color:var(--house-style);cursor:pointer;display:inline-flex;flex:none;font:600 13px/1 system-ui;gap:7px;min-height:34px;padding:7px 10px;text-decoration:none;white-space:nowrap}.configuration-central-database__export::before,.configuration-central-database__relocate::before{font-size:16px;font-weight:700;line-height:1}.configuration-central-database__export::before{content:"↓"}.configuration-central-database__relocate::before{content:"↗"}.configuration-central-database__export:hover:not(:disabled),.configuration-central-database__relocate:hover:not(:disabled){background:var(--house-style);color:var(--button-ink)}@media (max-width:620px){.configuration-central-database__export,.configuration-central-database__relocate{flex-basis:100%;justify-content:center}.configuration-central-database__location-link{flex-basis:100%}}
1006
+ .configuration-central-database__location-value{align-items:center;display:flex;flex-wrap:wrap;gap:10px}.configuration-central-database__location-link{flex:1 1 28rem}.configuration-central-database__export,.configuration-central-database__relocate{align-items:center;background-color:color-mix(in srgb,currentColor 14%,var(--dashboard-button-fill-surface));border:1px solid var(--house-style);border-radius:7px;box-sizing:border-box;color:var(--house-style);cursor:pointer;display:inline-flex;flex:none;font:600 13px/1 system-ui;gap:7px;min-height:34px;padding:7px 10px;text-decoration:none;white-space:nowrap}.configuration-central-database__export::before,.configuration-central-database__relocate::before{font-size:16px;font-weight:700;line-height:1}.configuration-central-database__export::before{content:"↓"}.configuration-central-database__relocate::before{content:"↗"}.configuration-central-database__export:hover:not(:disabled),.configuration-central-database__relocate:hover:not(:disabled){background:var(--house-style);color:var(--button-ink)}@media (max-width:620px){.configuration-central-database__export,.configuration-central-database__relocate{flex-basis:100%;justify-content:center}.configuration-central-database__location-link{flex-basis:100%}}
999
1007
  .configuration-card :is(.configuration-provider-status h2,.configuration-validation-environment h3,.configuration-central-database__header h2,.configuration-server-settings h2,.configuration-readonly-settings h2,.configuration-timeout-policy h2,.configuration-controls h2,.configuration-dashboard-settings h2,.configuration-provider-login-status h2){font-size:16px;line-height:1.25}
1000
1008
  /* A stored run-scoped conversation gets a filled, heavier history glyph. */
1001
1009
  .prompt-history-chat--recorded{font-weight:800}
@@ -1036,7 +1044,7 @@ dialog.dashboard-modal-shell,dialog.dashboard-modal-shell:is(:focus,:focus-visib
1036
1044
  every visible interactive focus treatment in the house-style orange,
1037
1045
  including anchor actions inside dialogs. Deliberately non-focusing shells,
1038
1046
  table-row selection surfaces and lifecycle geometry remain excluded above. */
1039
- :where(a[href],button,input,select,textarea,[role="button"],[tabindex]):not(:where(.dashboard-modal-shell,.component-log-row,.prompt-history-row,.telemetry-row,.execution-lifecycle__node,.theme-toggle,.section-state-toggle,.auto-refresh-toggle,.log-sortable,.log-table-wrap,.telemetry-scroll,.telemetry-detail-table-scroll)):is(:focus,:focus-visible){border-color:var(--house-style)!important;outline-color:var(--house-style)!important;box-shadow:0 0 0 var(--dashboard-focus-ring-size) var(--dashboard-input-focus-ring)!important}
1047
+ :where(a[href],button,input,select,textarea,[role="button"],[tabindex]):not(:where(.dashboard-modal-shell,.component-log-row,.prompt-history-row,.telemetry-row,.execution-lifecycle__node,.local-folder-link,.theme-toggle,.section-state-toggle,.auto-refresh-toggle,.log-sortable,.log-table-wrap,.telemetry-scroll,.telemetry-detail-table-scroll)):is(:focus,:focus-visible){border-color:var(--house-style)!important;outline-color:var(--house-style)!important;box-shadow:0 0 0 var(--dashboard-focus-ring-size) var(--dashboard-input-focus-ring)!important}
1040
1048
  /* Table scrollers are keyboard-scroll targets, not selected controls. Their
1041
1049
  outer surface never receives a focus ring; only independently interactive
1042
1050
  sortable headers retain their contained focus edge. */
@@ -1093,7 +1101,7 @@ html[data-theme="light"] .prompt-detail-modal__content .execution-lifecycle--his
1093
1101
  .workspace-worktrees__active{color:#69e6ad;display:inline-block;font:700 14px/1 system-ui;margin-right:7px;vertical-align:1px}html[data-theme="light"] .workspace-worktrees__active{color:#187843}
1094
1102
  /* Local paths remain text controls. Their only action is copying the displayed
1095
1103
  path, so the dashboard neither opens Finder nor follows file:// URLs. */
1096
- .local-folder-link{-webkit-appearance:none;appearance:none;background:transparent!important;border:0;color:var(--modal-secondary-accent)!important;cursor:pointer;font:inherit;overflow-wrap:anywhere;padding:0;text-align:left;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px}.local-folder-link:is(:hover,:active,:focus,:focus-visible):not(:disabled){background:transparent!important;box-shadow:none!important;color:var(--modal-secondary-accent)!important;text-decoration-thickness:2px}.local-folder-link:focus-visible{outline:2px solid var(--category-color);outline-offset:3px}.local-folder-link:disabled{cursor:default;opacity:1;text-decoration:none}.workspace-database__content .local-folder-link,.rate-limit-provider-path .local-folder-link{display:block;min-width:0;white-space:pre-wrap}
1104
+ .local-folder-link{-webkit-appearance:none;appearance:none;background:transparent!important;border:0;color:var(--modal-secondary-accent)!important;cursor:pointer;font:inherit;overflow-wrap:anywhere;padding:0;text-align:left;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px}.local-folder-link:is(:hover,:active,:focus,:focus-visible):not(:disabled){background:transparent!important;box-shadow:none!important;color:var(--modal-secondary-accent)!important;outline:0!important;outline-offset:0!important;text-decoration-thickness:2px}.local-folder-link:disabled{cursor:default;opacity:1;text-decoration:none}.workspace-database__content .local-folder-link,.rate-limit-provider-path .local-folder-link{display:block;min-width:0;white-space:pre-wrap}
1097
1105
  .runtime-directory-link{font-family:ui-monospace,SFMono-Regular,Menlo,monospace!important}
1098
1106
 
1099
1107
  /* The Inbox location chooser inherits the exact indigo accent of its queue. */
@@ -1257,7 +1265,7 @@ html[data-theme="light"] #componentLogControls :is(input[type="date"],input[type
1257
1265
  /* Controls whose visible shape is a pseudo-element or a native checkbox have
1258
1266
  no normal border to recolour, so receive the same one-pixel edge directly. */
1259
1267
  :is(.dashboard-titlebar .theme-toggle,.dashboard-titlebar .section-state-toggle):is(:focus,:focus-visible)::before,
1260
- :is(.dashboard-health__button,.auto-refresh-toggle input,.local-folder-link):is(:focus,:focus-visible){
1268
+ :is(.dashboard-health__button,.auto-refresh-toggle input):is(:focus,:focus-visible){
1261
1269
  box-shadow:none!important;
1262
1270
  outline:1px solid var(--house-style)!important;
1263
1271
  outline-offset:0!important;
@@ -1290,7 +1298,11 @@ html[data-theme="light"] #componentLogControls :is(input[type="date"],input[type
1290
1298
  area. Do not add a later global bubble scroller: it would force short
1291
1299
  messages to reserve scrollbar chrome and defeat the 2/3 area limit. */
1292
1300
  @media (max-width:620px){.dashboard-status-banner{position:static}}
1293
- .platform-attention{background:#5b4316;border-bottom:1px solid #f4c34f;color:#fff4d6;margin:0 0 18px}.platform-attention[hidden]{display:none}.platform-attention>summary{align-items:center;cursor:pointer;display:flex;gap:9px;list-style:none;padding:10px 16px}.platform-attention>summary::-webkit-details-marker{display:none}.platform-attention>summary::after{content:"▸";margin-left:auto}.platform-attention[open]>summary::after{content:"▾"}.platform-attention>summary strong{color:#ffd768}.platform-attention>summary span{background:#f4c34f;border-radius:999px;color:#2a2415;font-size:12px;font-weight:800;min-width:1.4rem;padding:2px 5px;text-align:center}.platform-attention__items{display:grid;gap:8px;padding:0 12px 12px}.platform-attention__items .dashboard-status-banner{margin:0;position:static}.platform-attention__items .dashboard-status-banner--provider-readiness{align-items:flex-start}.platform-attention__items .provider-readiness-banner__action{align-self:center}
1301
+ .platform-attention{background:#5b4316;border-bottom:1px solid #f4c34f;color:#fff4d6;margin:0 0 18px}.platform-attention[hidden]{display:none}.platform-attention>summary{align-items:center;cursor:pointer;display:flex;gap:9px;list-style:none;padding:10px 16px}.platform-attention>summary::-webkit-details-marker{display:none}.platform-attention>summary::after{content:"▸";font-size:24px;font-weight:700;line-height:1;margin-left:auto}.platform-attention[open]>summary::after{content:"▾"}.platform-attention>summary strong{color:#ffd768}.platform-attention>summary span{background:#f4c34f;border-radius:999px;color:#2a2415;font-size:12px;font-weight:800;min-width:1.4rem;padding:2px 5px;text-align:center}.platform-attention__items{display:grid;gap:8px;padding:0 12px 12px}.platform-attention__items .dashboard-status-banner{margin:0;position:static}.platform-attention__items .dashboard-status-banner--provider-readiness{align-items:flex-start}.platform-attention__items .provider-readiness-banner__action{align-self:center}
1302
+
1303
+ /* An expanded provider failure is its own red banner, not content inside an
1304
+ amber frame. Keep the amber summary, but leave the failure card unframed. */
1305
+ .platform-attention[open]{background:transparent;border:0}.platform-attention[open]>summary{background:#5b4316;border-bottom:1px solid #f4c34f}.platform-attention[open] .platform-attention__items{padding:8px 0 0}
1294
1306
 
1295
1307
  /* Buttons are controls, not text links: every visible action gets a modest
1296
1308
  fill derived from its own ink/border colour. Semantic local-file links and