engineering-platform 2.2.0__py3-none-any.whl

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 (130) hide show
  1. engineering_platform/ENGINEERING_PLATFORM_CONFIG.json +32 -0
  2. engineering_platform/ENGINEERING_PLATFORM_VERSION.json +15 -0
  3. engineering_platform/__init__.py +1 -0
  4. engineering_platform/__main__.py +7 -0
  5. engineering_platform/agent_state.py +530 -0
  6. engineering_platform/agent_trust.py +174 -0
  7. engineering_platform/assets/dashboard.css +1317 -0
  8. engineering_platform/assets/dashboard.js +8534 -0
  9. engineering_platform/assets/dashboard_locales.mjs +4049 -0
  10. engineering_platform/assets/dashboard_status_store.mjs +41 -0
  11. engineering_platform/assets/operations-console/apple-touch-icon-dark.png +0 -0
  12. engineering_platform/assets/operations-console/apple-touch-icon-light.png +0 -0
  13. engineering_platform/assets/operations-console/icon-dark.png +0 -0
  14. engineering_platform/assets/operations-console/icon-light.png +0 -0
  15. engineering_platform/assets/operations-console/icon-transparent.png +0 -0
  16. engineering_platform/assets/operations-console/manifest.webmanifest +11 -0
  17. engineering_platform/capability_preflight.py +285 -0
  18. engineering_platform/capability_review.py +261 -0
  19. engineering_platform/central_data_transfer.py +195 -0
  20. engineering_platform/central_database.py +245 -0
  21. engineering_platform/central_store_migration.py +1672 -0
  22. engineering_platform/codex_capacity.py +81 -0
  23. engineering_platform/codex_chat.py +226 -0
  24. engineering_platform/codex_observability.py +153 -0
  25. engineering_platform/component_lock.py +40 -0
  26. engineering_platform/component_logging.py +420 -0
  27. engineering_platform/console_presentation.py +14 -0
  28. engineering_platform/console_route_ownership.py +83 -0
  29. engineering_platform/contracts/__init__.py +38 -0
  30. engineering_platform/contracts/ep_consumer.py +391 -0
  31. engineering_platform/contracts/models.py +105 -0
  32. engineering_platform/contracts/projection.py +401 -0
  33. engineering_platform/dashboard_browser_validation.py +206 -0
  34. engineering_platform/dashboard_state.py +630 -0
  35. engineering_platform/dashboard_supervisor.swift +105 -0
  36. engineering_platform/dashboard_translation.py +129 -0
  37. engineering_platform/dependabot_producer.py +349 -0
  38. engineering_platform/drift_diagnostics.py +144 -0
  39. engineering_platform/emergency_recovery.py +268 -0
  40. engineering_platform/engineering_memory.py +139 -0
  41. engineering_platform/ep_consumer_credentials.py +473 -0
  42. engineering_platform/evidence_projection.py +213 -0
  43. engineering_platform/execution_activity.py +218 -0
  44. engineering_platform/execution_context.py +132 -0
  45. engineering_platform/execution_errors.py +42 -0
  46. engineering_platform/execution_evidence.py +24 -0
  47. engineering_platform/execution_executor.py +730 -0
  48. engineering_platform/execution_finalization.py +44 -0
  49. engineering_platform/execution_host.py +3306 -0
  50. engineering_platform/execution_lease.py +365 -0
  51. engineering_platform/execution_lifecycle.py +447 -0
  52. engineering_platform/execution_models.py +43 -0
  53. engineering_platform/execution_readiness.py +166 -0
  54. engineering_platform/execution_reporting.py +1607 -0
  55. engineering_platform/execution_repository.py +253 -0
  56. engineering_platform/execution_timeout_policy.py +56 -0
  57. engineering_platform/execution_timing.py +440 -0
  58. engineering_platform/execution_transaction.py +28 -0
  59. engineering_platform/external_producer_binding.py +235 -0
  60. engineering_platform/file_inbox.py +249 -0
  61. engineering_platform/forensic_attribution.py +338 -0
  62. engineering_platform/forensic_attribution_v2.py +134 -0
  63. engineering_platform/forensic_delta.py +299 -0
  64. engineering_platform/golden_scenario.py +63 -0
  65. engineering_platform/historical_dashboard_configuration.py +171 -0
  66. engineering_platform/host_admin.py +199 -0
  67. engineering_platform/host_preflight.py +231 -0
  68. engineering_platform/installation_relocation.py +122 -0
  69. engineering_platform/investigation_ledger.py +89 -0
  70. engineering_platform/legacy_inbox_migration.py +79 -0
  71. engineering_platform/lifecycle_worker.py +223 -0
  72. engineering_platform/live_status.py +267 -0
  73. engineering_platform/local_api.py +209 -0
  74. engineering_platform/local_api_keychain.py +51 -0
  75. engineering_platform/local_repository_binding.py +138 -0
  76. engineering_platform/managed_autonomy.py +509 -0
  77. engineering_platform/managed_codex_runtime.py +105 -0
  78. engineering_platform/parity_context.py +203 -0
  79. engineering_platform/parity_lifecycle_dispatcher.py +488 -0
  80. engineering_platform/platform_admin.py +13 -0
  81. engineering_platform/platform_api.py +428 -0
  82. engineering_platform/platform_bootstrap.py +385 -0
  83. engineering_platform/platform_components.py +65 -0
  84. engineering_platform/platform_version.py +171 -0
  85. engineering_platform/pr_check_repair.py +276 -0
  86. engineering_platform/pr_evidence_backfill.py +278 -0
  87. engineering_platform/producer.py +209 -0
  88. engineering_platform/project_agent.py +366 -0
  89. engineering_platform/project_agent_service.py +244 -0
  90. engineering_platform/project_topology.py +126 -0
  91. engineering_platform/prompt_history.py +591 -0
  92. engineering_platform/provider_context.py +136 -0
  93. engineering_platform/provider_context_benchmark.py +41 -0
  94. engineering_platform/provider_context_scope.py +90 -0
  95. engineering_platform/provider_interruption.py +168 -0
  96. engineering_platform/provider_process_identity.py +80 -0
  97. engineering_platform/provider_readiness.py +138 -0
  98. engineering_platform/provider_recovery.py +647 -0
  99. engineering_platform/provider_usage.py +497 -0
  100. engineering_platform/providers.py +471 -0
  101. engineering_platform/qualification.py +220 -0
  102. engineering_platform/recommendation_handoff.py +238 -0
  103. engineering_platform/report_analysis.py +193 -0
  104. engineering_platform/repository_attachment.py +171 -0
  105. engineering_platform/repository_handoff.py +95 -0
  106. engineering_platform/resources.py +38 -0
  107. engineering_platform/reviewer_evidence.py +70 -0
  108. engineering_platform/schemas/repository-attachment.schema.json +61 -0
  109. engineering_platform/server.py +3679 -0
  110. engineering_platform/server_console_services.py +2024 -0
  111. engineering_platform/server_relay.py +172 -0
  112. engineering_platform/server_service.py +122 -0
  113. engineering_platform/status_model.py +135 -0
  114. engineering_platform/status_reconciliation.py +34 -0
  115. engineering_platform/storage.py +2440 -0
  116. engineering_platform/submission_cli.py +77 -0
  117. engineering_platform/submission_intake.py +45 -0
  118. engineering_platform/submission_service.py +317 -0
  119. engineering_platform/telemetry.py +951 -0
  120. engineering_platform/templates/workspace-config.json +25 -0
  121. engineering_platform/validation_identity.py +50 -0
  122. engineering_platform/validation_profile.py +211 -0
  123. engineering_platform/workspace_preflight.py +263 -0
  124. engineering_platform/worktree_provenance.py +147 -0
  125. engineering_platform/worktree_tooling.py +18 -0
  126. engineering_platform-2.2.0.dist-info/METADATA +18 -0
  127. engineering_platform-2.2.0.dist-info/RECORD +130 -0
  128. engineering_platform-2.2.0.dist-info/WHEEL +5 -0
  129. engineering_platform-2.2.0.dist-info/entry_points.txt +6 -0
  130. engineering_platform-2.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,951 @@
1
+ """Best-effort, local Execution Host telemetry.
2
+
3
+ Telemetry is operational evidence only. It is deliberately separate from
4
+ transaction checkpoints and cannot change an engineering outcome.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import dataclass
10
+ from datetime import datetime, timedelta, timezone
11
+ import json
12
+ from math import sqrt
13
+ from pathlib import Path
14
+ import sqlite3
15
+ from threading import Lock, Thread, current_thread
16
+ from time import monotonic
17
+ from typing import Callable, Literal
18
+ from statistics import mean, median
19
+
20
+ from .storage import open_storage
21
+ from .producer import ProducerMetadata
22
+ from .execution_timing import timing_summary
23
+
24
+
25
+ TERMINAL_STATES = frozenset({"COMPLETE", "BLOCKED", "FAILED"})
26
+ TELEMETRY_OUTBOX_SOURCES = frozenset({"LIVE_TERMINAL", "RECOVERY", "BACKFILL"})
27
+ _PENDING_WORKERS: set[Thread] = set()
28
+ _PENDING_WORKERS_LOCK = Lock()
29
+
30
+
31
+ @dataclass(frozen=True)
32
+ class ExecutionTelemetry:
33
+ run_id: str
34
+ arrived_at: datetime
35
+ execution_started_at: datetime
36
+ execution_finished_at: datetime
37
+ terminal_state: str
38
+ execution_seconds: float | None
39
+ input_tokens: int | None
40
+ output_tokens: int | None
41
+ total_tokens: int | None
42
+ execution_mode: str
43
+ workspace: str
44
+ repository: str
45
+ execution_host_version: str
46
+ retry_of: str | None = None
47
+ original_run_id: str | None = None
48
+ retry_generation: int | None = None
49
+ retry_timestamp: str | None = None
50
+ prompt_characters: int | None = None
51
+ runtime_provider: str | None = None
52
+ runtime_model: str | None = None
53
+ reasoning_profile: str | None = None
54
+ configuration_profile: str | None = None
55
+ execution_metadata: dict[str, int] | None = None
56
+ producer: ProducerMetadata = ProducerMetadata()
57
+
58
+
59
+ def _utc(value: datetime) -> datetime:
60
+ return value.astimezone(timezone.utc) if value.tzinfo else value.replace(tzinfo=timezone.utc)
61
+
62
+
63
+ def _timestamp(value: datetime) -> str:
64
+ return _utc(value).isoformat()
65
+
66
+
67
+ def _integer(value: object) -> int | None:
68
+ return value if isinstance(value, int) and not isinstance(value, bool) and value >= 0 else None
69
+
70
+
71
+ def _runtime_value(value: object) -> str | None:
72
+ """Keep a bounded, display-safe runtime profile value for local aggregation."""
73
+ if not isinstance(value, str):
74
+ return None
75
+ normalized = value.strip()
76
+ if not normalized or normalized.casefold() in {"not reported", "unavailable"}:
77
+ return None
78
+ return normalized[:120]
79
+
80
+
81
+ def _execution_metadata(value: object) -> str:
82
+ if not isinstance(value, dict):
83
+ return "{}"
84
+ safe = {
85
+ key: item
86
+ for key in ("modified", "created", "deleted", "codex_commands_executed")
87
+ for item in (value.get(key),)
88
+ if isinstance(item, int) and not isinstance(item, bool) and 0 <= item <= 1_000_000
89
+ }
90
+ return json.dumps(safe, separators=(",", ":"), sort_keys=True)
91
+
92
+
93
+ def _payload(telemetry: ExecutionTelemetry) -> str:
94
+ """Serialize only bounded terminal projection data for durable replay."""
95
+ return json.dumps({
96
+ "run_id": telemetry.run_id,
97
+ "arrived_at": _timestamp(telemetry.arrived_at),
98
+ "execution_started_at": _timestamp(telemetry.execution_started_at),
99
+ "execution_finished_at": _timestamp(telemetry.execution_finished_at),
100
+ "terminal_state": telemetry.terminal_state,
101
+ "execution_seconds": telemetry.execution_seconds,
102
+ "input_tokens": _integer(telemetry.input_tokens),
103
+ "output_tokens": _integer(telemetry.output_tokens),
104
+ "total_tokens": _integer(telemetry.total_tokens),
105
+ "execution_mode": telemetry.execution_mode,
106
+ "workspace": telemetry.workspace,
107
+ "repository": telemetry.repository,
108
+ "execution_host_version": telemetry.execution_host_version,
109
+ "retry_of": telemetry.retry_of,
110
+ "original_run_id": telemetry.original_run_id,
111
+ "retry_generation": telemetry.retry_generation,
112
+ "retry_timestamp": telemetry.retry_timestamp,
113
+ "prompt_characters": _integer(telemetry.prompt_characters),
114
+ "runtime_provider": _runtime_value(telemetry.runtime_provider),
115
+ "runtime_model": _runtime_value(telemetry.runtime_model),
116
+ "reasoning_profile": _runtime_value(telemetry.reasoning_profile),
117
+ "configuration_profile": _runtime_value(telemetry.configuration_profile),
118
+ "execution_metadata": json.loads(_execution_metadata(telemetry.execution_metadata)),
119
+ "producer": {
120
+ "producer_id": telemetry.producer.producer_id,
121
+ "producer_type": telemetry.producer.producer_type,
122
+ "producer_version": telemetry.producer.producer_version,
123
+ "correlation_id": telemetry.producer.correlation_id,
124
+ "mission_id": telemetry.producer.mission_id,
125
+ "engineering_action_id": telemetry.producer.engineering_action_id,
126
+ "execution_constraint_version": telemetry.producer.execution_constraint_version,
127
+ },
128
+ }, separators=(",", ":"), sort_keys=True)
129
+
130
+
131
+ def _datetime(value: object) -> datetime:
132
+ if not isinstance(value, str):
133
+ raise ValueError("terminal telemetry timestamp is invalid")
134
+ try:
135
+ return _utc(datetime.fromisoformat(value.replace("Z", "+00:00")))
136
+ except ValueError as error:
137
+ raise ValueError("terminal telemetry timestamp is invalid") from error
138
+
139
+
140
+ def _from_payload(raw: object) -> ExecutionTelemetry:
141
+ if not isinstance(raw, dict):
142
+ raise ValueError("terminal telemetry payload is invalid")
143
+ producer = raw.get("producer")
144
+ if not isinstance(producer, dict):
145
+ raise ValueError("terminal telemetry producer is invalid")
146
+ required = ("run_id", "terminal_state", "execution_mode", "workspace", "repository", "execution_host_version")
147
+ if any(not isinstance(raw.get(key), str) or not raw[key] for key in required):
148
+ raise ValueError("terminal telemetry identity is invalid")
149
+ if raw["terminal_state"] not in TERMINAL_STATES or raw["execution_mode"] not in {"MANAGED", "GENESIS"}:
150
+ raise ValueError("terminal telemetry lifecycle is invalid")
151
+ return ExecutionTelemetry(
152
+ run_id=raw["run_id"], arrived_at=_datetime(raw.get("arrived_at")),
153
+ execution_started_at=_datetime(raw.get("execution_started_at")),
154
+ execution_finished_at=_datetime(raw.get("execution_finished_at")),
155
+ terminal_state=raw["terminal_state"], execution_seconds=raw.get("execution_seconds"),
156
+ input_tokens=_integer(raw.get("input_tokens")), output_tokens=_integer(raw.get("output_tokens")),
157
+ total_tokens=_integer(raw.get("total_tokens")), execution_mode=raw["execution_mode"],
158
+ workspace=raw["workspace"], repository=raw["repository"],
159
+ execution_host_version=raw["execution_host_version"], retry_of=raw.get("retry_of"),
160
+ original_run_id=raw.get("original_run_id"), retry_generation=raw.get("retry_generation"),
161
+ retry_timestamp=raw.get("retry_timestamp"), prompt_characters=_integer(raw.get("prompt_characters")),
162
+ runtime_provider=_runtime_value(raw.get("runtime_provider")), runtime_model=_runtime_value(raw.get("runtime_model")),
163
+ reasoning_profile=_runtime_value(raw.get("reasoning_profile")), configuration_profile=_runtime_value(raw.get("configuration_profile")),
164
+ execution_metadata=raw.get("execution_metadata") if isinstance(raw.get("execution_metadata"), dict) else None,
165
+ producer=ProducerMetadata(**{key: producer.get(key) for key in ProducerMetadata.__dataclass_fields__}),
166
+ )
167
+
168
+
169
+ def queue_terminal_telemetry(
170
+ root: Path, telemetry: ExecutionTelemetry, *, source: Literal["LIVE_TERMINAL", "RECOVERY", "BACKFILL"] = "LIVE_TERMINAL",
171
+ central_database: Path | None = None,
172
+ ) -> bool:
173
+ """Synchronously record a terminal telemetry intent before projection work.
174
+
175
+ Repeating the exact request is safe. A contradictory payload for the same
176
+ run is rejected rather than silently replacing terminal evidence.
177
+ """
178
+ if telemetry.terminal_state not in TERMINAL_STATES or source not in TELEMETRY_OUTBOX_SOURCES:
179
+ raise ValueError("terminal telemetry outbox input is invalid")
180
+ payload = _payload(telemetry)
181
+ # Reject malformed live telemetry before it can become a retry loop.
182
+ _from_payload(json.loads(payload))
183
+ if central_database is None:
184
+ connection = open_storage(root, create=False)
185
+ else:
186
+ connection = sqlite3.connect(central_database.resolve(), isolation_level=None)
187
+ connection.execute("PRAGMA foreign_keys=ON")
188
+ try:
189
+ with connection:
190
+ existing = connection.execute(
191
+ "SELECT payload FROM terminal_telemetry_outbox WHERE run_id=?", (telemetry.run_id,)
192
+ ).fetchone()
193
+ if existing is not None:
194
+ if existing[0] != payload:
195
+ raise ValueError("terminal telemetry outbox conflicts with existing run evidence")
196
+ return False
197
+ connection.execute(
198
+ "INSERT INTO terminal_telemetry_outbox(run_id,payload,source,state,created_at) VALUES(?,?,?,'PENDING',?)",
199
+ (telemetry.run_id, payload, source, datetime.now(timezone.utc).isoformat()),
200
+ )
201
+ finally:
202
+ connection.close()
203
+ return True
204
+
205
+
206
+ def materialize_pending_terminal_telemetry(root: Path, *, run_id: str | None = None, limit: int = 25,
207
+ central_database: Path | None = None) -> dict[str, int]:
208
+ """Idempotently materialize durable intents; failures remain retryable."""
209
+ if limit < 1 or limit > 250:
210
+ raise ValueError("terminal telemetry recovery limit is invalid")
211
+ if central_database is None:
212
+ connection = open_storage(root, create=False)
213
+ else:
214
+ connection = sqlite3.connect(central_database.resolve(), isolation_level=None)
215
+ connection.execute("PRAGMA foreign_keys=ON")
216
+ try:
217
+ query = "SELECT run_id,payload FROM terminal_telemetry_outbox WHERE state IN ('PENDING','FAILED_RETRYABLE')"
218
+ parameters: tuple[object, ...] = ()
219
+ if run_id is not None:
220
+ query += " AND run_id=?"
221
+ parameters = (run_id,)
222
+ rows = connection.execute(query + " ORDER BY created_at,run_id LIMIT ?", parameters + (limit,)).fetchall()
223
+ finally:
224
+ connection.close()
225
+ result = {"processed": 0, "failed": 0, "pending": len(rows)}
226
+ for queued_run_id, payload in rows:
227
+ try:
228
+ telemetry = _from_payload(json.loads(payload))
229
+ if telemetry.run_id != queued_run_id:
230
+ raise ValueError("terminal telemetry outbox run identity is invalid")
231
+ persist_execution(root, telemetry, create=False, central_database=central_database)
232
+ connection = (open_storage(root, create=False) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None))
233
+ try:
234
+ with connection:
235
+ connection.execute(
236
+ "UPDATE terminal_telemetry_outbox SET state='PROCESSED',attempt_count=attempt_count+1,"
237
+ "last_error=NULL,processed_at=? WHERE run_id=?",
238
+ (datetime.now(timezone.utc).isoformat(), queued_run_id),
239
+ )
240
+ finally:
241
+ connection.close()
242
+ result["processed"] += 1
243
+ except Exception as error:
244
+ connection = (open_storage(root, create=False) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None))
245
+ try:
246
+ with connection:
247
+ connection.execute(
248
+ "UPDATE terminal_telemetry_outbox SET state='FAILED_RETRYABLE',attempt_count=attempt_count+1,last_error=? WHERE run_id=?",
249
+ (str(error)[:500], queued_run_id),
250
+ )
251
+ finally:
252
+ connection.close()
253
+ result["failed"] += 1
254
+ return result
255
+
256
+
257
+ def _recovery_telemetry(root: Path, run_id: str, *, central_database: Path | None = None) -> ExecutionTelemetry:
258
+ """Reconstruct a projection only from structured terminal evidence.
259
+
260
+ This intentionally does not read report prose, infer tokens, or fabricate
261
+ duration values. Missing optional evidence remains unknown.
262
+ """
263
+ connection = open_storage(root, create=False) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None)
264
+ try:
265
+ transaction = connection.execute(
266
+ "SELECT payload,phase FROM engineering_transactions WHERE run_id=?", (run_id,)
267
+ ).fetchone()
268
+ history = connection.execute(
269
+ "SELECT terminal_state,executed_at,execution_metadata FROM prompt_execution_history WHERE run_id=?", (run_id,)
270
+ ).fetchone()
271
+ spans = connection.execute(
272
+ "SELECT phase_name,started_at,completed_at FROM execution_phase_spans "
273
+ "WHERE run_id=? AND phase_name IN ('QUEUE_WAIT','TOTAL_EXECUTION') AND outcome='COMPLETE' ORDER BY ordinal",
274
+ (run_id,),
275
+ ).fetchall()
276
+ finally:
277
+ connection.close()
278
+ if transaction is None or history is None:
279
+ raise ValueError("canonical terminal evidence is incomplete")
280
+ payload_text, phase = transaction
281
+ terminal_state, executed_at, metadata_text = history
282
+ if phase not in TERMINAL_STATES or terminal_state != phase:
283
+ raise ValueError("canonical terminal evidence is contradictory")
284
+ try:
285
+ checkpoint = json.loads(payload_text)
286
+ metadata = json.loads(metadata_text) if isinstance(metadata_text, str) else {}
287
+ except json.JSONDecodeError as error:
288
+ raise ValueError("canonical terminal evidence is invalid") from error
289
+ if not isinstance(checkpoint, dict) or not isinstance(metadata, dict):
290
+ raise ValueError("canonical terminal evidence is invalid")
291
+ total = next((row for row in spans if row[0] == "TOTAL_EXECUTION" and row[1] and row[2]), None)
292
+ if total is None:
293
+ raise ValueError("canonical terminal timing evidence is unavailable")
294
+ queue = next((row for row in spans if row[0] == "QUEUE_WAIT" and row[1]), None)
295
+ execution_started_at = _datetime(total[1])
296
+ arrived_at = _datetime(queue[1]) if queue is not None else execution_started_at
297
+ finished_at = _datetime(executed_at)
298
+ if finished_at < execution_started_at:
299
+ raise ValueError("canonical terminal timestamps are contradictory")
300
+ repository = checkpoint.get("repository")
301
+ mode = checkpoint.get("execution_mode")
302
+ if not isinstance(repository, str) or not repository or mode not in {"MANAGED", "GENESIS"}:
303
+ raise ValueError("canonical terminal identity is unavailable")
304
+ seconds = checkpoint.get("agent_execution_seconds")
305
+ if not isinstance(seconds, (int, float)) or isinstance(seconds, bool) or seconds < 0:
306
+ seconds = None
307
+ return ExecutionTelemetry(
308
+ run_id=run_id, arrived_at=arrived_at, execution_started_at=execution_started_at,
309
+ execution_finished_at=finished_at, terminal_state=terminal_state,
310
+ execution_seconds=float(seconds) if seconds is not None else None,
311
+ input_tokens=None, output_tokens=None, total_tokens=None, execution_mode=mode,
312
+ workspace=root.resolve().name, repository=repository, execution_host_version="unknown",
313
+ execution_metadata=metadata,
314
+ )
315
+
316
+
317
+ def recover_terminal_telemetry(root: Path, run_id: str, *, source: Literal["RECOVERY", "BACKFILL"] = "RECOVERY", central_database: Path | None = None) -> str:
318
+ """Perform one governed recovery from canonical terminal evidence.
319
+
320
+ Existing telemetry is left untouched. The result records the operational
321
+ path so callers can audit whether a run was live, recovered, or backfilled.
322
+ """
323
+ connection = open_storage(root, create=False) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None)
324
+ try:
325
+ if connection.execute("SELECT 1 FROM execution_runs WHERE run_id=?", (run_id,)).fetchone() is not None:
326
+ return "already_materialized"
327
+ finally:
328
+ connection.close()
329
+ telemetry = _recovery_telemetry(root, run_id, central_database=central_database)
330
+ queue_terminal_telemetry(root, telemetry, source=source, central_database=central_database)
331
+ result = materialize_pending_terminal_telemetry(root, run_id=run_id, limit=1, central_database=central_database)
332
+ if result["failed"]:
333
+ raise ValueError("terminal telemetry recovery remains retryable")
334
+ return "recovered" if result["processed"] else "already_queued"
335
+
336
+
337
+ def recover_missing_terminal_telemetry(root: Path, *, limit: int = 25, central_database: Path | None = None) -> dict[str, int]:
338
+ """Boundedly repair only missing projections with complete source evidence."""
339
+ if limit < 1 or limit > 250:
340
+ raise ValueError("terminal telemetry recovery limit is invalid")
341
+ connection = open_storage(root, create=False) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None)
342
+ try:
343
+ rows = connection.execute(
344
+ "SELECT history.run_id FROM prompt_execution_history AS history "
345
+ "LEFT JOIN execution_runs AS runs ON runs.run_id=history.run_id "
346
+ "JOIN engineering_transactions AS transaction_row "
347
+ "ON transaction_row.run_id=history.run_id AND transaction_row.phase=history.terminal_state "
348
+ "WHERE history.terminal_state IN ('COMPLETE','BLOCKED','FAILED') AND runs.run_id IS NULL "
349
+ "AND EXISTS (SELECT 1 FROM execution_phase_spans AS spans "
350
+ "WHERE spans.run_id=history.run_id AND spans.phase_name='TOTAL_EXECUTION' "
351
+ "AND spans.outcome='COMPLETE' AND spans.started_at IS NOT NULL AND spans.completed_at IS NOT NULL) "
352
+ "ORDER BY history.executed_at,history.run_id LIMIT ?", (limit,)
353
+ ).fetchall()
354
+ finally:
355
+ connection.close()
356
+ result = {"recovered": 0, "failed": 0, "candidates": len(rows)}
357
+ for (run_id,) in rows:
358
+ try:
359
+ if recover_terminal_telemetry(root, run_id, central_database=central_database) == "recovered":
360
+ result["recovered"] += 1
361
+ except Exception:
362
+ # The detailed, redacted failure remains in the outbox when it was
363
+ # queueable; no incomplete source is ever converted to telemetry.
364
+ result["failed"] += 1
365
+ return result
366
+
367
+
368
+ def persist_execution(
369
+ root: Path,
370
+ telemetry: ExecutionTelemetry,
371
+ *,
372
+ create: bool = True,
373
+ background: bool = False,
374
+ central_database: Path | None = None,
375
+ ) -> None:
376
+ """Persist one immutable run projection and refresh its daily aggregate."""
377
+ if telemetry.terminal_state not in TERMINAL_STATES:
378
+ raise ValueError("telemetry requires a terminal state")
379
+ arrived, started, finished = map(_utc, (telemetry.arrived_at, telemetry.execution_started_at, telemetry.execution_finished_at))
380
+ queue_wait = max(0.0, (started - arrived).total_seconds())
381
+ total_execution_seconds = max(0.0, (finished - arrived).total_seconds())
382
+ execution_seconds = telemetry.execution_seconds
383
+ if execution_seconds is not None and (isinstance(execution_seconds, bool) or execution_seconds < 0):
384
+ raise ValueError("telemetry execution duration is invalid")
385
+ execution_date = finished.date().isoformat()
386
+ if central_database is None:
387
+ connection = open_storage(root, create=create, journal_mode="MEMORY" if background else "DELETE")
388
+ else:
389
+ database = central_database.resolve()
390
+ if not database.is_file():
391
+ raise ValueError("CENTRAL telemetry database is unavailable")
392
+ connection = sqlite3.connect(database, isolation_level=None)
393
+ connection.execute("PRAGMA foreign_keys=ON")
394
+ try:
395
+ # One projection transaction: a crash can leave the durable outbox
396
+ # pending, but never a half-refreshed run/daily aggregate pair.
397
+ with connection:
398
+ connection.execute(
399
+ """
400
+ INSERT OR IGNORE INTO execution_runs(
401
+ run_id, execution_date, arrived_at, execution_started_at, execution_finished_at,
402
+ queue_wait_seconds, execution_seconds, total_execution_seconds, terminal_state, input_tokens, output_tokens,
403
+ total_tokens, execution_mode, workspace, repository, execution_host_version, retry_of,
404
+ original_run_id, retry_generation, retry_timestamp, prompt_characters,
405
+ runtime_provider, runtime_model, reasoning_profile, configuration_profile
406
+ , producer_id, producer_type, producer_version, correlation_id, mission_id,
407
+ engineering_action_id, execution_constraint_version, execution_metadata
408
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
409
+ """,
410
+ (
411
+ telemetry.run_id,
412
+ execution_date,
413
+ _timestamp(arrived),
414
+ _timestamp(started),
415
+ _timestamp(finished),
416
+ queue_wait,
417
+ execution_seconds,
418
+ total_execution_seconds,
419
+ telemetry.terminal_state,
420
+ _integer(telemetry.input_tokens),
421
+ _integer(telemetry.output_tokens),
422
+ _integer(telemetry.total_tokens),
423
+ telemetry.execution_mode,
424
+ telemetry.workspace,
425
+ telemetry.repository,
426
+ telemetry.execution_host_version,
427
+ telemetry.retry_of,
428
+ telemetry.original_run_id,
429
+ telemetry.retry_generation,
430
+ telemetry.retry_timestamp,
431
+ _integer(telemetry.prompt_characters),
432
+ _runtime_value(telemetry.runtime_provider),
433
+ _runtime_value(telemetry.runtime_model),
434
+ _runtime_value(telemetry.reasoning_profile),
435
+ _runtime_value(telemetry.configuration_profile),
436
+ telemetry.producer.producer_id,
437
+ telemetry.producer.producer_type,
438
+ telemetry.producer.producer_version,
439
+ telemetry.producer.correlation_id,
440
+ telemetry.producer.mission_id,
441
+ telemetry.producer.engineering_action_id,
442
+ telemetry.producer.execution_constraint_version,
443
+ _execution_metadata(telemetry.execution_metadata),
444
+ ),
445
+ )
446
+ connection.execute(
447
+ """INSERT OR IGNORE INTO execution_receipts(
448
+ run_id, producer_id, producer_type, producer_version, mission_id,
449
+ engineering_action_id, correlation_id, execution_constraint_version,
450
+ execution_host, execution_host_version, receipt_timestamp, execution_outcome
451
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
452
+ (
453
+ telemetry.run_id, telemetry.producer.producer_id, telemetry.producer.producer_type,
454
+ telemetry.producer.producer_version, telemetry.producer.mission_id,
455
+ telemetry.producer.engineering_action_id, telemetry.producer.correlation_id,
456
+ telemetry.producer.execution_constraint_version, "Engineering Platform",
457
+ telemetry.execution_host_version, _timestamp(finished), telemetry.terminal_state,
458
+ ),
459
+ )
460
+ # Admission reserves the immutable submission-to-run binding in
461
+ # execution_submission_links before any lifecycle work begins.
462
+ # The legacy FK column cannot be populated until this terminal
463
+ # execution_runs row exists, so complete it in this same atomic
464
+ # projection transaction without rewriting historical rows.
465
+ bound = connection.execute(
466
+ "SELECT submission_id FROM execution_submission_links WHERE run_id=?",
467
+ (telemetry.run_id,),
468
+ ).fetchone()
469
+ if bound is not None:
470
+ current = connection.execute(
471
+ "SELECT execution_run_id FROM execution_submissions WHERE submission_id=?",
472
+ (bound[0],),
473
+ ).fetchone()
474
+ if current is None or current[0] not in (None, telemetry.run_id):
475
+ raise ValueError("Execution submission run binding is inconsistent.")
476
+ connection.execute(
477
+ "UPDATE execution_submissions SET execution_run_id=? "
478
+ "WHERE submission_id=? AND execution_run_id IS NULL",
479
+ (telemetry.run_id, bound[0]),
480
+ )
481
+ connection.execute(
482
+ """
483
+ INSERT OR REPLACE INTO daily_execution_statistics(
484
+ execution_date, workspace, repository, execution_mode, prompt_count,
485
+ complete_count, blocked_count, failed_count, average_execution_seconds, average_total_execution_seconds,
486
+ average_queue_wait_seconds, input_tokens, output_tokens, total_tokens
487
+ )
488
+ SELECT execution_date, workspace, repository, execution_mode,
489
+ COUNT(*),
490
+ SUM(terminal_state = 'COMPLETE'), SUM(terminal_state = 'BLOCKED'), SUM(terminal_state = 'FAILED'),
491
+ AVG(execution_seconds), AVG(total_execution_seconds), AVG(queue_wait_seconds),
492
+ SUM(input_tokens), SUM(output_tokens), SUM(total_tokens)
493
+ FROM execution_runs
494
+ WHERE execution_date = ? AND workspace = ? AND repository = ? AND execution_mode = ?
495
+ GROUP BY execution_date, workspace, repository, execution_mode
496
+ """,
497
+ (execution_date, telemetry.workspace, telemetry.repository, telemetry.execution_mode),
498
+ )
499
+ finally:
500
+ connection.close()
501
+
502
+
503
+ def persist_execution_async(
504
+ root: Path, telemetry: ExecutionTelemetry, *, on_error: Callable[[Exception], None] | None = None
505
+ ) -> Thread:
506
+ """Schedule telemetry without ever delaying or failing engineering delivery."""
507
+ def persist() -> None:
508
+ try:
509
+ # The inbox watcher has already established the canonical workspace
510
+ # before telemetry is scheduled. A delayed best-effort worker must
511
+ # never recreate that workspace after its owner has gone away.
512
+ persist_execution(root, telemetry, create=False, background=True)
513
+ except Exception as error: # Best-effort boundary; caller logs only.
514
+ if on_error is not None:
515
+ on_error(error)
516
+ finally:
517
+ with _PENDING_WORKERS_LOCK:
518
+ _PENDING_WORKERS.discard(current_thread())
519
+
520
+ worker = Thread(target=persist, name=f"ep-telemetry-{telemetry.run_id}", daemon=True)
521
+ with _PENDING_WORKERS_LOCK:
522
+ _PENDING_WORKERS.add(worker)
523
+ worker.start()
524
+ return worker
525
+
526
+
527
+ def wait_for_pending_telemetry(*, timeout: float = 5.0) -> None:
528
+ """Wait for scheduled best-effort writes when a host is shutting down.
529
+
530
+ The watcher never calls this on its normal prompt-delivery path. It gives
531
+ callers that own a temporary workspace a deterministic way to close it
532
+ only after the explicitly asynchronous telemetry writer is finished.
533
+ """
534
+ deadline = monotonic() + max(0.0, timeout)
535
+ while True:
536
+ with _PENDING_WORKERS_LOCK:
537
+ workers = tuple(_PENDING_WORKERS)
538
+ if not workers:
539
+ return
540
+ remaining = deadline - monotonic()
541
+ if remaining <= 0:
542
+ return
543
+ for worker in workers:
544
+ worker.join(timeout=remaining)
545
+
546
+
547
+ def daily_statistics(root: Path, *, days: int = 90) -> list[dict[str, object]]:
548
+ """Return generic daily aggregates, newest day first, for the private dashboard."""
549
+ if not 1 <= days <= 360:
550
+ raise ValueError("telemetry days must be between 1 and 360")
551
+ connection = open_storage(root)
552
+ try:
553
+ rows = connection.execute(
554
+ """
555
+ SELECT execution_date, SUM(prompt_count), SUM(complete_count), SUM(blocked_count),
556
+ SUM(failed_count), AVG(average_execution_seconds), AVG(average_total_execution_seconds), AVG(average_queue_wait_seconds),
557
+ SUM(input_tokens), SUM(output_tokens), SUM(total_tokens)
558
+ FROM daily_execution_statistics
559
+ GROUP BY execution_date
560
+ ORDER BY execution_date DESC
561
+ LIMIT ?
562
+ """,
563
+ (days,),
564
+ ).fetchall()
565
+ finally:
566
+ connection.close()
567
+ keys = (
568
+ "date", "prompt_count", "complete_count", "blocked_count", "failed_count",
569
+ "average_execution_seconds", "average_total_execution_seconds", "average_queue_wait_seconds", "input_tokens",
570
+ "output_tokens", "total_tokens",
571
+ )
572
+ result = [dict(zip(keys, row, strict=True)) for row in rows]
573
+ # Phase detail remains on demand. Keep the legacy trend shape stable
574
+ # without expanding the ninety-day refresh into per-day run projections.
575
+ for row in result:
576
+ row["average_provider_execution_seconds"] = None
577
+ row["average_validation_seconds"] = None
578
+ return result
579
+
580
+
581
+ def prune_telemetry(root: Path, retention_days: int) -> dict[str, int]:
582
+ """Remove expired rebuildable telemetry projections without deleting evidence."""
583
+ if retention_days not in {30, 60, 90, 120, 180, 360}:
584
+ raise ValueError("telemetry retention must be an approved interval")
585
+ cutoff = (datetime.now(timezone.utc) - timedelta(days=retention_days)).date().isoformat()
586
+ connection = open_storage(root)
587
+ try:
588
+ with connection:
589
+ daily_rows = connection.execute(
590
+ "DELETE FROM daily_execution_statistics WHERE execution_date < ?", (cutoff,)
591
+ ).rowcount
592
+ run_rows = connection.execute(
593
+ "DELETE FROM execution_runs WHERE execution_date < ?", (cutoff,)
594
+ ).rowcount
595
+ finally:
596
+ connection.close()
597
+ return {
598
+ "daily_statistics": max(0, int(daily_rows or 0)),
599
+ "execution_runs": max(0, int(run_rows or 0)),
600
+ }
601
+
602
+
603
+ def clear_telemetry(root: Path) -> dict[str, int]:
604
+ """Clear rebuildable telemetry projections without touching execution evidence.
605
+
606
+ Execution receipts, reports, prompt history and lifecycle checkpoints are
607
+ evidence records. They are intentionally outside this operator control;
608
+ only the local telemetry projections displayed by the dashboard are reset.
609
+ """
610
+ connection = open_storage(root)
611
+ try:
612
+ with connection:
613
+ daily_rows = connection.execute("DELETE FROM daily_execution_statistics").rowcount
614
+ run_rows = connection.execute("DELETE FROM execution_runs").rowcount
615
+ finally:
616
+ connection.close()
617
+ return {
618
+ "daily_statistics": max(0, int(daily_rows or 0)),
619
+ "execution_runs": max(0, int(run_rows or 0)),
620
+ }
621
+
622
+
623
+ _DASHBOARD_PHASES = (
624
+ "QUEUE_WAIT", "SUBMISSION_CLAIM", "INITIALIZATION", "HOST_PREFLIGHT",
625
+ "WORKSPACE_PREFLIGHT", "CAPABILITY_PREFLIGHT", "EXECUTION_PREPARATION",
626
+ "PROVIDER_EXECUTION", "VALIDATION", "QUALITY_CONTROL", "REPAIR", "REPOSITORY_FINALIZATION",
627
+ "PR_OR_MERGE", "FINALIZATION", "REPORT_GENERATION", "EVIDENCE_PERSISTENCE",
628
+ "REPOSITORY_CLEANUP", "RECONCILIATION", "EXTERNAL_CI_WAIT",
629
+ )
630
+
631
+ # These are timing categories, not lifecycle authority. They let the
632
+ # advisory estimator reuse persisted phase evidence without altering the
633
+ # runner's state machine or inventing a path from incomplete history.
634
+ _LIFECYCLE_STEP_PHASES = {
635
+ "INITIALIZE": frozenset({"INITIALIZATION", "HOST_PREFLIGHT", "WORKSPACE_PREFLIGHT", "CAPABILITY_PREFLIGHT"}),
636
+ "EXECUTE_AGENT": frozenset({"EXECUTION_PREPARATION", "PROVIDER_EXECUTION", "VALIDATION"}),
637
+ "QUALITY_CONTROL_AGENT": frozenset({"QUALITY_CONTROL"}),
638
+ "REPAIR_AGENT": frozenset({"REPAIR"}),
639
+ "FINALIZE_AGENT": frozenset({"REPOSITORY_FINALIZATION", "FINALIZATION", "REPORT_GENERATION", "EVIDENCE_PERSISTENCE", "RECONCILIATION"}),
640
+ "REPOSITORY_CLEANUP": frozenset({"REPOSITORY_CLEANUP"}),
641
+ }
642
+ _MANAGED_ESTIMATE_PATH = ("INITIALIZE", "EXECUTE_AGENT", "QUALITY_CONTROL_AGENT", "REPAIR_AGENT", "WAIT_FOR_OPERATOR_MERGE", "FINALIZE_AGENT", "REPOSITORY_CLEANUP")
643
+ _GENESIS_ESTIMATE_PATH = ("INITIALIZE", "EXECUTE_AGENT", "REPAIR_AGENT", "FINALIZE_AGENT", "REPOSITORY_CLEANUP")
644
+
645
+
646
+ def _remaining_steps(current_phase: object, execution_mode: object) -> tuple[str, ...]:
647
+ if not isinstance(current_phase, str):
648
+ return ()
649
+ path = _GENESIS_ESTIMATE_PATH if execution_mode == "GENESIS" else _MANAGED_ESTIMATE_PATH
650
+ if current_phase not in path:
651
+ return ()
652
+ remaining = tuple(step for step in path[path.index(current_phase):] if step in _LIFECYCLE_STEP_PHASES)
653
+ # Repair is conditional. Include it only after the runner has actually
654
+ # entered repair; otherwise a healthy run is not priced as a repair run.
655
+ return remaining if current_phase == "REPAIR_AGENT" else tuple(step for step in remaining if step != "REPAIR_AGENT")
656
+
657
+
658
+ def _phase_step_durations(root: Path, run_id: str) -> dict[str, float]:
659
+ """Return non-overlapping visible-step durations for one completed run."""
660
+ phase_to_step = {phase: step for step, phases in _LIFECYCLE_STEP_PHASES.items() for phase in phases}
661
+ connection = open_storage(root)
662
+ try:
663
+ rows = connection.execute(
664
+ "SELECT phase_id,phase_name,parent_phase_id,duration_ms,outcome FROM execution_phase_spans WHERE run_id=? ORDER BY ordinal",
665
+ (run_id,),
666
+ ).fetchall()
667
+ finally:
668
+ connection.close()
669
+ by_id = {str(phase_id): (str(name), parent) for phase_id, name, parent, _, _ in rows}
670
+ totals: dict[str, float] = {}
671
+ for phase_id, name, parent, duration, outcome in rows:
672
+ if outcome == "ACTIVE" or not isinstance(duration, int) or duration < 0:
673
+ continue
674
+ step = phase_to_step.get(str(name))
675
+ if step is None:
676
+ continue
677
+ ancestor = parent
678
+ while isinstance(ancestor, str) and ancestor in by_id:
679
+ ancestor_name, ancestor = by_id[ancestor]
680
+ if phase_to_step.get(ancestor_name) == step:
681
+ break
682
+ else:
683
+ totals[step] = totals.get(step, 0.0) + duration / 1000
684
+ return totals
685
+
686
+
687
+ def _active_phase_elapsed_seconds(root: Path, run_id: object, current_phase: object, now: datetime) -> float:
688
+ if not isinstance(run_id, str) or not run_id or not isinstance(current_phase, str):
689
+ return 0.0
690
+ phase_names = _LIFECYCLE_STEP_PHASES.get(current_phase, frozenset())
691
+ if not phase_names:
692
+ return 0.0
693
+ connection = open_storage(root)
694
+ try:
695
+ rows = connection.execute(
696
+ "SELECT phase_name,started_at FROM execution_phase_spans WHERE run_id=? AND outcome='ACTIVE' ORDER BY ordinal DESC",
697
+ (run_id,),
698
+ ).fetchall()
699
+ finally:
700
+ connection.close()
701
+ for phase_name, started_at in rows:
702
+ if phase_name not in phase_names or not isinstance(started_at, str):
703
+ continue
704
+ try:
705
+ started = datetime.fromisoformat(started_at.replace("Z", "+00:00"))
706
+ except ValueError:
707
+ continue
708
+ return max(0.0, (_utc(now) - _utc(started)).total_seconds())
709
+ return 0.0
710
+
711
+
712
+ def daily_timing_detail(root: Path, execution_date: str) -> dict[str, object]:
713
+ """Return a bounded, read-only UTC-day timing projection for the dashboard.
714
+
715
+ This deliberately composes ``timing_summary`` per persisted run, keeping
716
+ the browser a renderer and preserving the canonical non-double-counting
717
+ timing rules. ``execution_date`` is the UTC terminal-date already used by
718
+ the seven-day trend projection; the client only formats that stable value
719
+ in the local-user date style.
720
+ """
721
+ try:
722
+ datetime.strptime(execution_date, "%Y-%m-%d")
723
+ except (TypeError, ValueError):
724
+ raise ValueError("execution date is invalid") from None
725
+ connection = open_storage(root)
726
+ try:
727
+ rows = connection.execute(
728
+ """SELECT run_id, execution_started_at, terminal_state, total_execution_seconds,
729
+ queue_wait_seconds, runtime_provider, runtime_model, reasoning_profile,
730
+ producer_type, repository
731
+ FROM execution_runs WHERE execution_date=?
732
+ ORDER BY execution_started_at DESC LIMIT 250""",
733
+ (execution_date,),
734
+ ).fetchall()
735
+ finally:
736
+ connection.close()
737
+ run_rows: list[dict[str, object]] = []
738
+ summaries: list[dict[str, object]] = []
739
+ for row in rows:
740
+ run_id = str(row[0])
741
+ summary = timing_summary(root, run_id)
742
+ summaries.append(summary)
743
+ phase_available = bool(summary.get("phase_telemetry_available"))
744
+ total = summary.get("total_wall_time_ms") if phase_available else (
745
+ round(float(row[3]) * 1000) if isinstance(row[3], (int, float)) else None
746
+ )
747
+ def measured(name: str) -> int | None:
748
+ value = summary.get(name)
749
+ return int(value) if phase_available and isinstance(value, int) else None
750
+ run_rows.append({
751
+ "run_id": run_id, "started_at": row[1], "status": row[2],
752
+ "total_duration_ms": total, "queue_wait_ms": measured("queue_wait_time_ms"),
753
+ "provider_duration_ms": measured("provider_execution_time_ms"),
754
+ "validation_duration_ms": measured("validation_time_ms"),
755
+ "external_wait_ms": measured("external_wait_time_ms"),
756
+ "report_generation_ms": measured("report_generation_time_ms"),
757
+ "evidence_persistence_ms": measured("evidence_persistence_time_ms"),
758
+ "largest_phase": summary.get("longest_phase") if phase_available else None,
759
+ "producer_type": row[8], "repository": row[9], "provider": row[5],
760
+ "model": row[6], "reasoning_profile": row[7],
761
+ "phase_telemetry": "RECORDED" if phase_available else "NOT_RECORDED",
762
+ })
763
+ def values(key: str) -> list[int]:
764
+ return [int(item[key]) for item in summaries if isinstance(item.get(key), int)]
765
+ def aggregate(items: list[int]) -> dict[str, int] | None:
766
+ return {"average_ms": round(mean(items)), "median_ms": round(median(items)), "total_ms": sum(items), "runs": len(items)} if items else None
767
+ totals = [int(row["total_duration_ms"]) for row in run_rows if isinstance(row["total_duration_ms"], int)]
768
+ summary = {
769
+ "executions": len(run_rows),
770
+ "completed": sum(row["status"] == "COMPLETE" for row in run_rows),
771
+ "blocked": sum(row["status"] == "BLOCKED" for row in run_rows),
772
+ "failed": sum(row["status"] == "FAILED" for row in run_rows),
773
+ "total_wall_time": aggregate(totals),
774
+ "active_processing_time": aggregate(values("active_ep_processing_time_ms")),
775
+ "queue_wait": aggregate(values("queue_wait_time_ms")),
776
+ "provider_execution": aggregate(values("provider_execution_time_ms")),
777
+ "validation": aggregate(values("validation_time_ms")),
778
+ "external_wait": aggregate(values("external_wait_time_ms")),
779
+ "overhead": aggregate(values("overhead_time_ms")),
780
+ "report_generation": aggregate(values("report_generation_time_ms")),
781
+ "evidence_persistence": aggregate(values("evidence_persistence_time_ms")),
782
+ }
783
+ phase_values: dict[str, list[int]] = {phase: [] for phase in _DASHBOARD_PHASES}
784
+ phase_share_values: dict[str, list[int]] = {phase: [] for phase in _DASHBOARD_PHASES}
785
+ for item in summaries:
786
+ # Use the shared run-level category projection. The dashboard must
787
+ # not reconstruct a competing aggregate from raw spans.
788
+ aggregates = item.get("phase_aggregates", ())
789
+ if isinstance(aggregates, list):
790
+ for aggregate_row in aggregates:
791
+ if not isinstance(aggregate_row, dict):
792
+ continue
793
+ phase, value = aggregate_row.get("phase"), aggregate_row.get("duration_ms")
794
+ if isinstance(phase, str) and phase in phase_values and isinstance(value, int):
795
+ phase_values[phase].append(value)
796
+ shares = item.get("phase_share_durations_ms", {})
797
+ if isinstance(shares, dict):
798
+ for phase, value in shares.items():
799
+ if isinstance(phase, str) and phase in phase_share_values and isinstance(value, int):
800
+ phase_share_values[phase].append(value)
801
+ phase_wall_time = sum(
802
+ int(item["total_wall_time_ms"])
803
+ for item in summaries
804
+ if item.get("phase_telemetry_available") and isinstance(item.get("total_wall_time_ms"), int)
805
+ )
806
+ phase_rows = [
807
+ dict(
808
+ {"phase": phase},
809
+ **aggregate(items),
810
+ # Raw durations retain nested and stale-span audit evidence. The
811
+ # percentage uses only each category's overlap with the run's
812
+ # TOTAL_EXECUTION envelope, so a single category cannot exceed
813
+ # 100% of total wall time.
814
+ share_percent=round(sum(phase_share_values[phase]) * 100 / phase_wall_time, 3) if phase_wall_time else None,
815
+ )
816
+ for phase, items in phase_values.items() if aggregate(items)
817
+ ]
818
+ consumers = sorted(phase_rows, key=lambda item: (-int(item["total_ms"]), str(item["phase"])))[:3]
819
+ canonical_share_keys = {
820
+ "queue_wait": "queue_share_percent",
821
+ "provider_execution": "provider_share_percent",
822
+ "validation": "validation_share_percent",
823
+ "external_wait": "external_wait_share_percent",
824
+ "overhead": "overhead_share_percent",
825
+ }
826
+ return {
827
+ "date": execution_date, "timezone": "UTC", "summary": summary, "phases": phase_rows,
828
+ "bottlenecks": {"longest_average_phase": max(phase_rows, key=lambda item: int(item["average_ms"]), default=None),
829
+ "largest_accumulated_phase": max(phase_rows, key=lambda item: int(item["total_ms"]), default=None),
830
+ "top_time_consumers": consumers,
831
+ "shares": {
832
+ label: round(mean([int(item[key]) for item in summaries if isinstance(item.get(key), (int, float))]), 3)
833
+ if any(isinstance(item.get(key), (int, float)) for item in summaries) else None
834
+ for label, key in canonical_share_keys.items()
835
+ }},
836
+ "runs": run_rows, "phase_telemetry_available": any(bool(item.get("phase_telemetry_available")) for item in summaries),
837
+ }
838
+
839
+
840
+ def execution_timing(root: Path, run_id: str) -> dict[str, float | str]:
841
+ """Return persisted timing and terminal timestamp evidence for one run."""
842
+ connection = open_storage(root)
843
+ try:
844
+ row = connection.execute(
845
+ "SELECT execution_seconds, total_execution_seconds, execution_finished_at FROM execution_runs WHERE run_id = ?",
846
+ (run_id,),
847
+ ).fetchone()
848
+ finally:
849
+ connection.close()
850
+ if row is None:
851
+ return {}
852
+ result: dict[str, float | str] = {}
853
+ for key, value in zip(("execution_seconds", "total_execution_seconds"), row[:2], strict=True):
854
+ if isinstance(value, (int, float)) and not isinstance(value, bool) and value >= 0:
855
+ result[key] = float(value)
856
+ if isinstance(row[2], str):
857
+ result["finished_at"] = row[2]
858
+ return result
859
+
860
+
861
+ def comparable_duration_estimate(
862
+ root: Path,
863
+ *,
864
+ prompt_characters: object,
865
+ runtime_metadata: object,
866
+ run_id: object = None,
867
+ current_phase: object = None,
868
+ execution_mode: object = None,
869
+ now: datetime | None = None,
870
+ ) -> dict[str, float | int | str | bool]:
871
+ """Return a robust size-adjusted estimate from one exact runtime profile.
872
+
873
+ This is intentionally advisory: it never affects scheduling or engineering
874
+ state. Missing or unreported profile fields yield no estimate rather than
875
+ mixing incomparable providers, models or reasoning settings.
876
+ """
877
+ characters = _integer(prompt_characters)
878
+ if not characters or not isinstance(runtime_metadata, dict):
879
+ return {}
880
+ signature = tuple(
881
+ _runtime_value(runtime_metadata.get(key))
882
+ for key in ("runtime_provider", "model", "reasoning_profile", "configuration_profile")
883
+ )
884
+ if any(value is None for value in signature):
885
+ return {}
886
+ mode = _runtime_value(execution_mode)
887
+ query = """
888
+ SELECT run_id, execution_seconds, prompt_characters
889
+ FROM execution_runs
890
+ WHERE terminal_state = 'COMPLETE'
891
+ AND runtime_provider = ? AND runtime_model = ?
892
+ AND reasoning_profile = ? AND configuration_profile = ?
893
+ AND execution_seconds IS NOT NULL AND prompt_characters > 0
894
+ """
895
+ parameters: tuple[object, ...] = signature
896
+ if mode:
897
+ query += " AND execution_mode = ?"
898
+ parameters += (mode,)
899
+ query += " ORDER BY execution_finished_at DESC LIMIT 20"
900
+ connection = open_storage(root)
901
+ try:
902
+ rows = connection.execute(query, parameters).fetchall()
903
+ finally:
904
+ connection.close()
905
+ # A linear character ratio turns a modestly larger prompt into an
906
+ # implausibly long estimate even though each run has fixed startup,
907
+ # validation and reporting work. Use a bounded square-root factor instead:
908
+ # it still reflects substantial input differences without letting sparse
909
+ # historical samples dominate the operator-facing indication.
910
+ scaled = [float(seconds) * min(1.6, max(0.7, sqrt(characters / int(size)))) for _, seconds, size in rows if seconds >= 0]
911
+ if len(scaled) < 2:
912
+ return {}
913
+ ordered = sorted(scaled)
914
+ # The observed spread gives an honest range while avoiding a single old
915
+ # outlier dominating the indicator. The arithmetic mean remains available
916
+ # as transparent diagnostic evidence for a fixed input and sample set.
917
+ lower = ordered[max(0, (len(ordered) - 1) // 4)]
918
+ upper = ordered[min(len(ordered) - 1, (len(ordered) - 1) * 3 // 4)]
919
+ result: dict[str, float | int | str | bool] = {
920
+ "sample_count": len(scaled),
921
+ "average_seconds": round(mean(scaled), 3),
922
+ "lower_seconds": round(min(lower, upper), 3),
923
+ "upper_seconds": round(max(lower, upper), 3),
924
+ "runtime_provider": signature[0],
925
+ "model": signature[1],
926
+ }
927
+ remaining_steps = _remaining_steps(current_phase, execution_mode)
928
+ if not remaining_steps:
929
+ return result
930
+ phase_samples: list[float] = []
931
+ active_elapsed = _active_phase_elapsed_seconds(root, run_id, current_phase, now or datetime.now(timezone.utc))
932
+ for historical_run_id, _, size in rows:
933
+ durations = _phase_step_durations(root, str(historical_run_id))
934
+ phase_seconds = sum(durations.get(step, 0.0) for step in remaining_steps)
935
+ if phase_seconds <= 0:
936
+ continue
937
+ scale = min(1.6, max(0.7, sqrt(characters / int(size))))
938
+ phase_samples.append(max(0.0, phase_seconds * scale - active_elapsed))
939
+ if len(phase_samples) < 2:
940
+ return result
941
+ ordered_phase_samples = sorted(phase_samples)
942
+ lower_phase = ordered_phase_samples[max(0, (len(ordered_phase_samples) - 1) // 4)]
943
+ upper_phase = ordered_phase_samples[min(len(ordered_phase_samples) - 1, (len(ordered_phase_samples) - 1) * 3 // 4)]
944
+ result.update({
945
+ "phase_aware": True,
946
+ "phase_sample_count": len(phase_samples),
947
+ "remaining_lower_seconds": round(min(lower_phase, upper_phase), 3),
948
+ "remaining_upper_seconds": round(max(lower_phase, upper_phase), 3),
949
+ "active_phase_elapsed_seconds": round(active_elapsed, 3),
950
+ })
951
+ return result