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,591 @@
1
+ """Canonical SQLite index for completed Engineering Platform prompt runs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime, timedelta, timezone
6
+ import json
7
+ import os
8
+ import re
9
+ from pathlib import Path
10
+ import sqlite3
11
+
12
+ from .storage import open_storage
13
+
14
+
15
+ RUN_ID_PATTERN = re.compile(r"[a-z0-9][a-z0-9-]{0,63}")
16
+ REPORT_RUN_ID = re.compile(r"^- Run ID: `([a-z0-9][a-z0-9-]{0,63})`$", re.MULTILINE)
17
+ REPORT_STATE = re.compile(r"^- Terminal state: `(COMPLETE|BLOCKED|FAILED)`$", re.MULTILINE)
18
+ REPORT_TIMESTAMP = re.compile(r"^- Timestamp: ([^\n]{1,80})$", re.MULTILINE)
19
+ REPORT_OBJECTIVE = re.compile(r"^- Objective: (.+)$", re.MULTILINE)
20
+ REPORT_TITLE = re.compile(r"^# (.+)$", re.MULTILINE)
21
+ REPORT_INCREMENT_TITLE = re.compile(r"^# Engineering Platform Increment\s+—\s+(.+)$", re.MULTILINE)
22
+ REPORT_COMMIT = re.compile(
23
+ r"^- (?:Target Commit|Genesis-commit|Implementation Merge Commit|Finalization Merge Commit): `?([0-9a-f]{7,64})`?$",
24
+ re.MULTILINE | re.IGNORECASE,
25
+ )
26
+ REPORT_TARGET_BRANCH = re.compile(r"^- Target Branch: `([^`\n]{1,255})`$", re.MULTILINE)
27
+ REPORT_EXECUTION_METADATA = {
28
+ "modified": re.compile(r"^- Files Modified: `(\d{1,9})`$", re.MULTILINE),
29
+ "created": re.compile(r"^- Files Created: `(\d{1,9})`$", re.MULTILINE),
30
+ "deleted": re.compile(r"^- Files Deleted: `(\d{1,9})`$", re.MULTILINE),
31
+ "codex_commands_executed": re.compile(r"^- Codex Commands Executed: `(\d{1,9})`$", re.MULTILINE),
32
+ }
33
+ RETRY_OF = re.compile(r"^- Retry Of: `([a-z0-9][a-z0-9-]{0,63})`$", re.MULTILINE)
34
+ ORIGINAL_RUN = re.compile(r"^- Original Run: `([a-z0-9][a-z0-9-]{0,63})`$", re.MULTILINE)
35
+ RETRY_GENERATION = re.compile(r"^- Retry Generation: `(\d+)`$", re.MULTILINE)
36
+ RETRY_TIMESTAMP = re.compile(r"^- Retry Timestamp: ([^\n]{1,80})$", re.MULTILINE)
37
+ TERMINAL_STATES = frozenset({"COMPLETE", "BLOCKED", "FAILED"})
38
+ RETRY_CHILD_STATES = frozenset({"QUEUED", "ACTIVE", *TERMINAL_STATES})
39
+
40
+
41
+ def _safe_run_id(value: object) -> str | None:
42
+ return value if isinstance(value, str) and RUN_ID_PATTERN.fullmatch(value) else None
43
+
44
+
45
+ def _safe_timestamp(value: object) -> str:
46
+ if isinstance(value, datetime):
47
+ return value.astimezone(timezone.utc).isoformat()
48
+ if isinstance(value, str) and value.strip():
49
+ timestamp = value.strip()[:80]
50
+ return re.sub(r"T(\d{2})-(\d{2})-(\d{2})Z$", r"T\1:\2:\3Z", timestamp)
51
+ return datetime.now(timezone.utc).isoformat()
52
+
53
+
54
+ def _relative_report(root: Path, report: Path | None, *, central_database: Path | None = None) -> str | None:
55
+ if report is None:
56
+ return None
57
+ central = str(central_database) if central_database is not None else os.environ.get("EP_CENTRAL_OPERATIONAL_DATABASE")
58
+ if central:
59
+ try:
60
+ return "CENTRAL:" + str(report.resolve().relative_to(Path(central).resolve().parent / "artifacts"))
61
+ except (OSError, ValueError):
62
+ return None
63
+ try:
64
+ relative = report.resolve().relative_to((root / ".engineering" / "reports").resolve())
65
+ except (OSError, ValueError):
66
+ return None
67
+ return str(relative)
68
+
69
+
70
+ def _safe_checkout_path(value: object) -> str | None:
71
+ """Keep only an absolute, bounded terminal checkout snapshot."""
72
+ if not isinstance(value, (str, Path)):
73
+ return None
74
+ path = Path(value).expanduser()
75
+ if not path.is_absolute():
76
+ return None
77
+ resolved = str(path.resolve())
78
+ return resolved if len(resolved) <= 1_000 else None
79
+
80
+
81
+ def _safe_tracked_file_count(value: object) -> int | None:
82
+ return value if isinstance(value, int) and not isinstance(value, bool) and 0 <= value <= 10_000_000 else None
83
+
84
+
85
+ def _safe_target_branch(value: object) -> str | None:
86
+ """Keep a bounded, single-line branch snapshot safe for presentation."""
87
+ if not isinstance(value, str):
88
+ return None
89
+ branch = value.strip()
90
+ if not branch or len(branch) > 255 or any(character in branch for character in "\0\r\n"):
91
+ return None
92
+ return branch
93
+
94
+
95
+ def safe_execution_metadata(value: object) -> dict[str, int]:
96
+ """Keep only bounded, aggregate execution counters for presentation."""
97
+ if not isinstance(value, dict):
98
+ return {}
99
+ return {
100
+ key: item
101
+ for key in ("modified", "created", "deleted", "codex_commands_executed")
102
+ for item in (value.get(key),)
103
+ if isinstance(item, int) and not isinstance(item, bool) and 0 <= item <= 1_000_000
104
+ }
105
+
106
+
107
+ def execution_metadata_from_terminal_report(report: Path | None) -> dict[str, int]:
108
+ """Read only aggregate execution counters from an authoritative report."""
109
+ if report is None:
110
+ return {}
111
+ try:
112
+ content = report.read_text(encoding="utf-8")
113
+ except OSError:
114
+ return {}
115
+ return safe_execution_metadata({
116
+ key: int(match.group(1))
117
+ for key, pattern in REPORT_EXECUTION_METADATA.items()
118
+ if (match := pattern.search(content)) is not None
119
+ })
120
+
121
+
122
+ def record_prompt_execution(
123
+ root: Path,
124
+ *,
125
+ run_id: object,
126
+ terminal_state: object,
127
+ prompt_title: object,
128
+ executed_at: object,
129
+ report: Path | None = None,
130
+ git_commit: object = None,
131
+ retry_of: object = None,
132
+ original_run_id: object = None,
133
+ retry_generation: object = None,
134
+ retry_timestamp: object = None,
135
+ target_checkout_path: object = None,
136
+ tracked_file_count: object = None,
137
+ target_branch: object = None,
138
+ execution_metadata: object = None,
139
+ central_database: Path | None = None,
140
+ ) -> None:
141
+ """Upsert a terminal prompt projection without changing execution authority."""
142
+ safe_run_id = _safe_run_id(run_id)
143
+ if safe_run_id is None or terminal_state not in TERMINAL_STATES:
144
+ raise ValueError("prompt history requires a terminal Engineering Platform run")
145
+ title = str(prompt_title or safe_run_id).strip()[:500] or safe_run_id
146
+ commit = git_commit if isinstance(git_commit, str) and re.fullmatch(r"[0-9a-f]{7,64}", git_commit) else None
147
+ parent = _safe_run_id(retry_of)
148
+ original = _safe_run_id(original_run_id)
149
+ generation = retry_generation if isinstance(retry_generation, int) and retry_generation >= 1 else None
150
+ timestamp = _safe_timestamp(retry_timestamp) if parent and retry_timestamp else None
151
+ checkout_path = _safe_checkout_path(target_checkout_path)
152
+ tracked_files = _safe_tracked_file_count(tracked_file_count)
153
+ branch = _safe_target_branch(target_branch)
154
+ metadata = safe_execution_metadata(execution_metadata) or execution_metadata_from_terminal_report(report)
155
+ if parent is None:
156
+ original = generation = timestamp = None
157
+ elif original is None:
158
+ original = parent
159
+ elif generation is None:
160
+ generation = 1
161
+ now = datetime.now(timezone.utc).isoformat()
162
+ if central_database is None:
163
+ connection = open_storage(root)
164
+ else:
165
+ connection = sqlite3.connect(central_database.resolve(), isolation_level=None)
166
+ connection.execute("PRAGMA foreign_keys=ON")
167
+ try:
168
+ connection.execute(
169
+ """
170
+ INSERT INTO prompt_execution_history(
171
+ run_id, terminal_state, prompt_title, executed_at, git_commit, report_path, retry_of,
172
+ original_run_id, retry_generation, retry_timestamp, target_checkout_path,
173
+ tracked_file_count, target_branch, execution_metadata, updated_at
174
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
175
+ ON CONFLICT(run_id) DO UPDATE SET
176
+ terminal_state=excluded.terminal_state,
177
+ prompt_title=excluded.prompt_title,
178
+ executed_at=excluded.executed_at,
179
+ git_commit=COALESCE(excluded.git_commit, prompt_execution_history.git_commit),
180
+ report_path=COALESCE(excluded.report_path, prompt_execution_history.report_path),
181
+ retry_of=COALESCE(excluded.retry_of, prompt_execution_history.retry_of),
182
+ original_run_id=COALESCE(excluded.original_run_id, prompt_execution_history.original_run_id),
183
+ retry_generation=COALESCE(excluded.retry_generation, prompt_execution_history.retry_generation),
184
+ retry_timestamp=COALESCE(excluded.retry_timestamp, prompt_execution_history.retry_timestamp),
185
+ target_checkout_path=COALESCE(excluded.target_checkout_path, prompt_execution_history.target_checkout_path),
186
+ tracked_file_count=COALESCE(excluded.tracked_file_count, prompt_execution_history.tracked_file_count),
187
+ target_branch=COALESCE(excluded.target_branch, prompt_execution_history.target_branch),
188
+ execution_metadata=CASE WHEN excluded.execution_metadata != '{}' THEN excluded.execution_metadata ELSE prompt_execution_history.execution_metadata END,
189
+ updated_at=excluded.updated_at
190
+ """,
191
+ (
192
+ safe_run_id,
193
+ terminal_state,
194
+ title,
195
+ _safe_timestamp(executed_at),
196
+ commit,
197
+ _relative_report(root, report, central_database=central_database),
198
+ parent,
199
+ original,
200
+ generation,
201
+ timestamp,
202
+ checkout_path,
203
+ tracked_files,
204
+ branch,
205
+ json.dumps(metadata, separators=(",", ":"), sort_keys=True),
206
+ now,
207
+ ),
208
+ )
209
+ finally:
210
+ connection.close()
211
+
212
+
213
+ def _report_record(root: Path, report: Path) -> dict[str, object] | None:
214
+ try:
215
+ content = report.read_text(encoding="utf-8")
216
+ except OSError:
217
+ return None
218
+ run = REPORT_RUN_ID.search(content)
219
+ state = REPORT_STATE.search(content)
220
+ if run is None or state is None:
221
+ return None
222
+ title = REPORT_TITLE.search(content)
223
+ increment_title = REPORT_INCREMENT_TITLE.search(content)
224
+ objective = REPORT_OBJECTIVE.search(content)
225
+ timestamp = REPORT_TIMESTAMP.search(content)
226
+ commit = REPORT_COMMIT.search(content)
227
+ retry_of = RETRY_OF.search(content)
228
+ original_run = ORIGINAL_RUN.search(content)
229
+ retry_generation = RETRY_GENERATION.search(content)
230
+ retry_timestamp = RETRY_TIMESTAMP.search(content)
231
+ target_branch = REPORT_TARGET_BRANCH.search(content)
232
+ return {
233
+ "run_id": run.group(1),
234
+ "terminal_state": state.group(1),
235
+ "prompt_title": (
236
+ increment_title.group(1).strip()
237
+ if increment_title and increment_title.group(1).strip()
238
+ else objective.group(1).strip()
239
+ if objective and objective.group(1).strip()
240
+ else title.group(1).strip()
241
+ if title
242
+ else run.group(1)
243
+ ),
244
+ "executed_at": timestamp.group(1)
245
+ if timestamp
246
+ else datetime.fromtimestamp(report.stat().st_mtime, timezone.utc).isoformat(),
247
+ "report": report,
248
+ "git_commit": commit.group(1) if commit else None,
249
+ "retry_of": retry_of.group(1) if retry_of else None,
250
+ "original_run_id": original_run.group(1) if original_run else None,
251
+ "retry_generation": int(retry_generation.group(1)) if retry_generation else None,
252
+ "retry_timestamp": retry_timestamp.group(1) if retry_timestamp else None,
253
+ "target_branch": target_branch.group(1) if target_branch else None,
254
+ "execution_metadata": execution_metadata_from_terminal_report(report),
255
+ }
256
+
257
+
258
+ def submission_prompt_title(root: Path, run_id: str) -> str | None:
259
+ """Read the submitted title without deriving it from prompt content."""
260
+ connection = open_storage(root)
261
+ try:
262
+ row = connection.execute(
263
+ "SELECT submission.prompt_metadata FROM execution_submissions AS submission "
264
+ "JOIN execution_submission_links AS link ON link.submission_id=submission.submission_id "
265
+ "WHERE link.run_id=?",
266
+ (run_id,),
267
+ ).fetchone()
268
+ finally:
269
+ connection.close()
270
+ if row is None or not isinstance(row[0], str):
271
+ return None
272
+ try:
273
+ metadata = json.loads(row[0])
274
+ except json.JSONDecodeError:
275
+ return None
276
+ title = metadata.get("title") if isinstance(metadata, dict) else None
277
+ return str(title).strip()[:500] or None if isinstance(title, str) else None
278
+
279
+
280
+ def record_terminal_report(root: Path, report: Path, *, central_database: Path | None = None) -> None:
281
+ """Project one authoritative terminal report into prompt history.
282
+
283
+ Direct execution-host invocations do not pass through the inbox watcher.
284
+ Keeping this projection here makes the terminal report the shared source
285
+ for both paths and lets a later reconciled report correct an older,
286
+ incomplete history row for the same run.
287
+ """
288
+ record = _report_record(root, report)
289
+ if record is None:
290
+ raise ValueError("terminal report cannot be projected into prompt history")
291
+ submitted_title = submission_prompt_title(root, str(record["run_id"])) if central_database is None else None
292
+ # A managed Inbox submission owns its title. The terminal report's
293
+ # Objective is deliberately non-authoritative input context and must never
294
+ # replace the title already recorded at submission, including when the
295
+ # report supplies a non-generic placeholder for that Objective.
296
+ if submitted_title:
297
+ record["prompt_title"] = submitted_title
298
+ record_prompt_execution(root, central_database=central_database, **record)
299
+
300
+
301
+ def backfill_prompt_history(root: Path) -> None:
302
+ """Cache legacy reports and telemetry rows into the canonical history index."""
303
+ reports = root / ".engineering" / "reports"
304
+ connection = open_storage(root)
305
+ try:
306
+ indexed_run_ids = {
307
+ row[0]
308
+ for row in connection.execute("SELECT run_id FROM prompt_execution_history").fetchall()
309
+ if isinstance(row[0], str)
310
+ }
311
+ finally:
312
+ connection.close()
313
+ if reports.is_dir():
314
+ for report in reports.glob("*.md"):
315
+ record = _report_record(root, report)
316
+ # The stored report path is the canonical terminal projection. A
317
+ # legacy fallback can share a Run ID with a later, real report;
318
+ # never let a filesystem scan replace that explicit reference.
319
+ if record is not None and record["run_id"] not in indexed_run_ids:
320
+ record_terminal_report(root, report)
321
+ indexed_run_ids.add(record["run_id"])
322
+ connection = open_storage(root)
323
+ try:
324
+ rows = connection.execute(
325
+ """
326
+ SELECT run_id, terminal_state, execution_finished_at
327
+ FROM execution_runs
328
+ WHERE run_id NOT IN (SELECT run_id FROM prompt_execution_history)
329
+ """
330
+ ).fetchall()
331
+ finally:
332
+ connection.close()
333
+ for run_id, terminal_state, finished_at in rows:
334
+ record_prompt_execution(
335
+ root,
336
+ run_id=run_id,
337
+ terminal_state=terminal_state,
338
+ prompt_title=run_id,
339
+ executed_at=finished_at,
340
+ )
341
+
342
+
343
+ def _active_retry_children(root: Path) -> list[dict[str, object]]:
344
+ """Read active child lineage from watcher-persisted job records."""
345
+ try:
346
+ status = json.loads((root / ".engineering" / "status" / "status.json").read_text(encoding="utf-8"))
347
+ except (OSError, json.JSONDecodeError):
348
+ return []
349
+ active_run_id = _safe_run_id(status.get("run_id")) if isinstance(status, dict) else None
350
+ if active_run_id is None:
351
+ return []
352
+ children: list[dict[str, object]] = []
353
+ for job in (root / ".engineering" / "inbox-processing").glob("*/job.json"):
354
+ try:
355
+ payload = json.loads(job.read_text(encoding="utf-8"))
356
+ except (OSError, json.JSONDecodeError):
357
+ continue
358
+ if not isinstance(payload, dict) or payload.get("run_id") != active_run_id:
359
+ continue
360
+ retry = payload.get("retry")
361
+ parent = _safe_run_id(retry.get("retry_of")) if isinstance(retry, dict) else None
362
+ if parent is not None:
363
+ children.append(
364
+ {
365
+ "retry_of": parent,
366
+ "run_id": active_run_id,
367
+ "status": "ACTIVE",
368
+ "retry_timestamp": retry.get("retry_timestamp"),
369
+ }
370
+ )
371
+ return children
372
+
373
+
374
+ def _valid_retry_children(children: object) -> list[dict[str, object]]:
375
+ """Accept bounded persisted child projections without inventing run IDs."""
376
+ if not isinstance(children, list):
377
+ return []
378
+ valid: list[dict[str, object]] = []
379
+ for child in children:
380
+ if not isinstance(child, dict):
381
+ continue
382
+ parent, run_id = _safe_run_id(child.get("retry_of")), _safe_run_id(child.get("run_id"))
383
+ state = child.get("status")
384
+ if parent is None or state not in RETRY_CHILD_STATES:
385
+ continue
386
+ if state != "QUEUED" and run_id is None:
387
+ continue
388
+ projection = {
389
+ "retry_of": parent,
390
+ "status": state,
391
+ "retry_timestamp": _safe_timestamp(child["retry_timestamp"])
392
+ if child.get("retry_timestamp")
393
+ else None,
394
+ }
395
+ if run_id is not None:
396
+ projection["run_id"] = run_id
397
+ valid.append(projection)
398
+ return valid
399
+
400
+
401
+ def prompt_history(
402
+ root: Path, *, limit: int = 1_000, queued_retry_children: list[dict[str, object]] | None = None,
403
+ central_database: Path | None = None,
404
+ ) -> list[dict[str, object]]:
405
+ """Return bounded, newest-first projections safe for the private dashboard."""
406
+ bounded_limit = min(max(limit, 1), 1_000)
407
+ chat_cutoff = (datetime.now(timezone.utc) - timedelta(days=90)).isoformat()
408
+ if central_database is None:
409
+ connection = open_storage(root)
410
+ else:
411
+ connection = sqlite3.connect(central_database.resolve(), isolation_level=None)
412
+ connection.execute("PRAGMA foreign_keys=ON")
413
+ try:
414
+ rows = connection.execute(
415
+ """
416
+ SELECT history.run_id,
417
+ CASE WHEN emergency.run_id IS NOT NULL THEN 'CANCELLED' ELSE history.terminal_state END,
418
+ history.prompt_title, history.executed_at,
419
+ history.git_commit, history.report_path, history.retry_of, history.original_run_id,
420
+ history.retry_generation, history.retry_timestamp, history.target_checkout_path,
421
+ history.tracked_file_count, history.target_branch,
422
+ COALESCE(NULLIF(history.execution_metadata, '{}'), runs.execution_metadata, '{}'),
423
+ COALESCE(runs.execution_mode, admission.execution_mode), runs.repository,
424
+ COALESCE(
425
+ runs.total_execution_seconds,
426
+ ROUND((
427
+ SELECT MAX(span.duration_ms) / 1000.0
428
+ FROM execution_phase_spans AS span
429
+ WHERE span.run_id = history.run_id
430
+ AND span.phase_name = 'TOTAL_EXECUTION'
431
+ AND span.outcome IN ('COMPLETE', 'FAILED')
432
+ ), 3),
433
+ ROUND((
434
+ SELECT MAX(
435
+ 0.0,
436
+ (julianday(MAX(CASE
437
+ WHEN json_extract(log.payload, '$.event') IN ('job_completed', 'job_failed')
438
+ THEN log.created_at END)) - julianday(MIN(CASE
439
+ WHEN json_extract(log.payload, '$.event') = 'runner_started'
440
+ THEN log.created_at END))) * 86400.0
441
+ )
442
+ FROM engineering_component_logs AS log
443
+ WHERE log.component = 'inbox'
444
+ AND json_extract(log.payload, '$.run_id') = history.run_id
445
+ ), 3)
446
+ ) AS total_execution_seconds,
447
+ COALESCE(submission.producer_id, runs.producer_id),
448
+ COALESCE(submission.producer_type, runs.producer_type),
449
+ COALESCE(submission.producer_version, runs.producer_version),
450
+ COALESCE(submission.correlation_id, runs.correlation_id),
451
+ COALESCE(submission.mission_id, runs.mission_id),
452
+ COALESCE(submission.engineering_action_id, runs.engineering_action_id),
453
+ runs.execution_constraint_version, submission.submission_id,
454
+ submission.contract_version, submission.execution_context_version,
455
+ submission.execution_context_snapshot, submission.forge_governance_handoff_version,
456
+ submission.forge_governance_handoff_snapshot, dismissal.terminal_state,
457
+ dismissal.handling_state, dismissal.dismissed_at, dismissal.dismissed_by,
458
+ emergency.cancelled_at,
459
+ (SELECT COUNT(*) FROM execution_chat_messages AS chat
460
+ WHERE chat.run_id = history.run_id AND chat.created_at >= ?)
461
+ FROM prompt_execution_history AS history
462
+ LEFT JOIN execution_runs AS runs ON runs.run_id = history.run_id
463
+ LEFT JOIN execution_submission_links AS submission_link ON submission_link.run_id = history.run_id
464
+ LEFT JOIN execution_submissions AS submission ON submission.submission_id = submission_link.submission_id
465
+ LEFT JOIN execution_admission_decisions AS admission ON admission.run_id = history.run_id
466
+ LEFT JOIN execution_dismissals AS dismissal ON dismissal.run_id = history.run_id
467
+ LEFT JOIN execution_emergency_recoveries AS emergency ON emergency.run_id = history.run_id
468
+ ORDER BY history.executed_at DESC, history.run_id DESC
469
+ LIMIT ?
470
+ """,
471
+ (chat_cutoff, bounded_limit),
472
+ ).fetchall()
473
+ finally:
474
+ connection.close()
475
+ records = [
476
+ {
477
+ "run_id": row[0],
478
+ "status": row[1],
479
+ "title": row[2],
480
+ "executed_at": row[3],
481
+ "git_commit": row[4],
482
+ "report_available": bool(row[5]),
483
+ "retry_of": row[6],
484
+ "original_run_id": row[7],
485
+ "retry_generation": row[8],
486
+ "retry_timestamp": row[9],
487
+ "target_checkout_path": row[10],
488
+ "tracked_file_count": row[11],
489
+ "target_branch": row[12],
490
+ "execution_metadata": safe_execution_metadata(json.loads(row[13])) if isinstance(row[13], str) else {},
491
+ "execution_mode": row[14],
492
+ "repository": row[15],
493
+ "total_execution_seconds": row[16],
494
+ "producer_id": row[17] or "legacy",
495
+ "producer_type": row[18] or "HUMAN",
496
+ "producer_version": row[19],
497
+ "correlation_id": row[20],
498
+ "mission_id": row[21],
499
+ "engineering_action_id": row[22],
500
+ "execution_constraint_version": row[23],
501
+ "submission_id": row[24],
502
+ "producer_submission_contract_version": row[25],
503
+ "execution_context_version": row[26],
504
+ "execution_context": json.loads(row[27]) if isinstance(row[27], str) else None,
505
+ "forge_governance_handoff_version": row[28],
506
+ "forge_governance_handoff": json.loads(row[29]) if isinstance(row[29], str) else None,
507
+ "dismissed": row[30] is not None,
508
+ "handling_state": row[31] or "OPEN",
509
+ "dismissed_at": row[32],
510
+ "dismissed_by": row[33],
511
+ "emergency_cancelled_at": row[34],
512
+ "chat_message_count": row[35],
513
+ }
514
+ for row in rows
515
+ ]
516
+ for record in records:
517
+ if record["forge_governance_handoff"] is None:
518
+ record.pop("forge_governance_handoff_version")
519
+ record.pop("forge_governance_handoff")
520
+ if record["emergency_cancelled_at"] is None:
521
+ record.pop("emergency_cancelled_at")
522
+ # Lineage is a read-only projection of persisted child evidence. It never
523
+ # changes a terminal parent or treats retry as resume. A terminal child
524
+ # wins over active, which wins over an unclaimed Inbox child.
525
+ children: dict[str, dict[str, object]] = {}
526
+ for child in [
527
+ *_valid_retry_children(queued_retry_children),
528
+ # CENTRAL history may not inspect a checkout-local watcher projection.
529
+ *(_active_retry_children(root) if central_database is None else ()),
530
+ *[record for record in records if record.get("retry_of")],
531
+ ]:
532
+ parent = child.get("retry_of")
533
+ if isinstance(parent, str):
534
+ children[parent] = child
535
+ for record in records:
536
+ child = children.get(record["run_id"])
537
+ record["retry_child_run_id"] = child.get("run_id") if child else None
538
+ record["retry_status"] = child.get("status") if child else None
539
+ record["retry_timestamp"] = child.get("retry_timestamp") if child else record["retry_timestamp"]
540
+ record["queued_retry_child"] = bool(child and child.get("status") == "QUEUED")
541
+ record["active_retry_child"] = bool(child and child.get("status") == "ACTIVE")
542
+ record["can_retry"] = record.get("status") in {"BLOCKED", "FAILED"} and child is None and not record["dismissed"]
543
+ chain = [record["run_id"]]
544
+ cursor = record
545
+ while cursor.get("retry_of") and cursor["retry_of"] not in chain:
546
+ chain.insert(0, cursor["retry_of"])
547
+ cursor = next((item for item in records if item["run_id"] == cursor["retry_of"]), {})
548
+ record["retry_chain"] = chain
549
+ record["current_active_run"] = (
550
+ child.get("run_id") if child and child.get("status") != "QUEUED" else record["run_id"]
551
+ )
552
+ return records
553
+
554
+
555
+ def report_path_for_prompt_history(root: Path, run_id: object) -> Path | None:
556
+ """Resolve only the report explicitly indexed for the requested terminal run."""
557
+ safe_run_id = _safe_run_id(run_id)
558
+ if safe_run_id is None:
559
+ return None
560
+ connection = open_storage(root)
561
+ try:
562
+ row = connection.execute(
563
+ "SELECT report_path FROM prompt_execution_history WHERE run_id=?", (safe_run_id,)
564
+ ).fetchone()
565
+ finally:
566
+ connection.close()
567
+ if not row or not isinstance(row[0], str):
568
+ return None
569
+ central = os.environ.get("EP_CENTRAL_OPERATIONAL_DATABASE")
570
+ if central and row[0].startswith("CENTRAL:"):
571
+ path = (Path(central).resolve().parent / "artifacts" / row[0].removeprefix("CENTRAL:")).resolve()
572
+ try:
573
+ path.relative_to(Path(central).resolve().parent / "artifacts")
574
+ return path if path.is_file() else None
575
+ except (OSError, ValueError):
576
+ return None
577
+ path = (root / ".engineering" / "reports" / row[0]).resolve()
578
+ try:
579
+ path.relative_to((root / ".engineering" / "reports").resolve())
580
+ return path if path.is_file() else None
581
+ except (OSError, ValueError):
582
+ return None
583
+
584
+
585
+ def report_for_prompt_history(root: Path, run_id: object) -> bytes | None:
586
+ """Return only a report explicitly indexed for the requested terminal run."""
587
+ path = report_path_for_prompt_history(root, run_id)
588
+ try:
589
+ return path.read_bytes() if path is not None else None
590
+ except OSError:
591
+ return None