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,365 @@
1
+ """Canonical active-run ownership leases backed solely by Engineering SQLite."""
2
+ from __future__ import annotations
3
+
4
+ from dataclasses import dataclass
5
+ from datetime import datetime, timedelta, timezone
6
+ import json
7
+ from pathlib import Path
8
+ import sqlite3
9
+ import socket
10
+ from threading import Event, Thread
11
+ import uuid
12
+
13
+ from .storage import EngineeringStorageError, open_storage
14
+ from .execution_timing import reconcile_interrupted_phases
15
+ from .provider_process_identity import ProcessIdentity, verify_process_identity
16
+
17
+ LEASE_VERSION = 1
18
+ HEARTBEAT_INTERVAL_SECONDS = 15
19
+ LEASE_TIMEOUT_SECONDS = 90
20
+ TERMINAL_PHASES = frozenset({"COMPLETE", "BLOCKED", "FAILED"})
21
+
22
+
23
+ def _connection(root: Path, central_database: Path | None = None) -> sqlite3.Connection:
24
+ """Open an explicitly supplied CENTRAL lease authority when available."""
25
+ if central_database is None:
26
+ return open_storage(root)
27
+ database = central_database.resolve()
28
+ if not database.is_file():
29
+ raise EngineeringStorageError("CENTRAL lease database is unavailable.")
30
+ connection = sqlite3.connect(database, isolation_level=None)
31
+ connection.execute("PRAGMA foreign_keys=ON")
32
+ connection.execute("PRAGMA busy_timeout=10000")
33
+ return connection
34
+
35
+
36
+ class LeaseConflictError(EngineeringStorageError):
37
+ """Raised when another non-expired host instance owns the run."""
38
+
39
+
40
+ @dataclass(frozen=True)
41
+ class Lease:
42
+ lease_id: str
43
+ run_id: str
44
+ host_identity: str
45
+ host_instance_id: str
46
+ acquired_at: str
47
+ last_heartbeat_at: str
48
+ expires_at: str
49
+ lease_state: str
50
+
51
+
52
+ def host_identity() -> str:
53
+ return socket.gethostname()[:120] or "execution-host"
54
+
55
+
56
+ def host_instance_id() -> str:
57
+ return f"{uuid.uuid4()}"
58
+
59
+
60
+ def _now() -> datetime:
61
+ return datetime.now(timezone.utc)
62
+
63
+
64
+ def _event(connection: object, lease_id: str, run_id: str, event: str, outcome: str | None = None) -> None:
65
+ connection.execute(
66
+ "INSERT OR IGNORE INTO execution_lease_events(lease_id,run_id,event_type,outcome,recorded_at) VALUES(?,?,?,?,?)",
67
+ (lease_id, run_id, event, outcome or "", _now().isoformat()),
68
+ )
69
+
70
+
71
+ def _recovery_provider_lease_state(connection: object, run_id: str) -> str:
72
+ """Read recovery receipts through the caller's lease transaction.
73
+
74
+ This avoids opening a second SQLite writer while stale-lease
75
+ reconciliation owns ``BEGIN IMMEDIATE``. Only a session-bound, PID reuse
76
+ protected PROCESS_STARTED receipt can retain an expired host lease.
77
+ """
78
+ try:
79
+ recovery = connection.execute(
80
+ "SELECT state,replacement_invocation_id,provider_session_id FROM provider_recovery_attempts WHERE run_id=?",
81
+ (run_id,),
82
+ ).fetchone()
83
+ if not recovery or recovery[0] != "RECOVERY_IN_PROGRESS":
84
+ return "NOT_ACTIVE"
85
+ rows = connection.execute(
86
+ "SELECT provider_session_id,process_pid,process_group,process_start_fingerprint,process_executable_identity "
87
+ "FROM provider_invocation_receipts WHERE run_id=? AND invocation_id=? AND launch_state='PROCESS_STARTED'",
88
+ (run_id, recovery[1]),
89
+ ).fetchall()
90
+ except Exception:
91
+ return "NOT_ACTIVE"
92
+ if len(rows) != 1 or not isinstance(recovery[2], str):
93
+ return "AMBIGUOUS"
94
+ session_id, pid, group, fingerprint, executable = rows[0]
95
+ if (
96
+ session_id != recovery[2] or not isinstance(pid, int) or not isinstance(group, int)
97
+ or not isinstance(fingerprint, str) or not isinstance(executable, str)
98
+ ):
99
+ return "AMBIGUOUS"
100
+ return "ACTIVE" if verify_process_identity(ProcessIdentity(pid, group, fingerprint, executable)) == "MATCH" else "AMBIGUOUS"
101
+
102
+
103
+ class LeaseHeartbeat:
104
+ """Bounded background heartbeat; lifecycle state remains runner-owned."""
105
+
106
+ def __init__(self, root: Path, lease: Lease, *, central_database: Path | None = None, interval_seconds: int = HEARTBEAT_INTERVAL_SECONDS) -> None:
107
+ self.root = root
108
+ self.lease = lease
109
+ self.central_database = central_database
110
+ self.interval_seconds = interval_seconds
111
+ self._stop = Event()
112
+ self._thread: Thread | None = None
113
+ self.error: Exception | None = None
114
+
115
+ def start(self) -> None:
116
+ if self._thread is not None:
117
+ return
118
+ self._thread = Thread(target=self._run, name=f"engineering-lease-{self.lease.run_id}", daemon=True)
119
+ self._thread.start()
120
+
121
+ def _run(self) -> None:
122
+ while not self._stop.wait(self.interval_seconds):
123
+ try:
124
+ self.lease = heartbeat(self.root, self.lease, central_database=self.central_database)
125
+ except Exception as error: # The expiry boundary remains fail-closed.
126
+ self.error = error
127
+ return
128
+
129
+ def stop(self) -> Lease:
130
+ self._stop.set()
131
+ if self._thread is not None:
132
+ self._thread.join(timeout=max(1, self.interval_seconds))
133
+ return self.lease
134
+
135
+
136
+ def acquire(root: Path, run_id: str, *, identity: str, instance_id: str, process_id: int | None = None, timeout_seconds: int = LEASE_TIMEOUT_SECONDS, central_database: Path | None = None) -> Lease:
137
+ if not 0 < HEARTBEAT_INTERVAL_SECONDS < timeout_seconds:
138
+ raise EngineeringStorageError("Active-run lease policy is invalid.")
139
+ now = _now()
140
+ expiry = now + timedelta(seconds=timeout_seconds)
141
+ connection = _connection(root, central_database)
142
+ try:
143
+ connection.execute("BEGIN IMMEDIATE")
144
+ connection.execute(
145
+ "UPDATE execution_run_leases SET lease_state='EXPIRED',updated_at=? WHERE run_id=? AND lease_state='ACTIVE' AND expires_at<?",
146
+ (now.isoformat(), run_id, now.isoformat()),
147
+ )
148
+ active = connection.execute(
149
+ "SELECT lease_id,host_instance_id FROM execution_run_leases WHERE run_id=? AND lease_state='ACTIVE'", (run_id,)
150
+ ).fetchone()
151
+ if active:
152
+ raise LeaseConflictError("A live Execution Host instance already owns this run.")
153
+ lease_id = f"lease-{uuid.uuid4()}"
154
+ values = (lease_id, run_id, identity, instance_id, process_id, now.isoformat(), now.isoformat(), expiry.isoformat(), "ACTIVE", LEASE_VERSION, now.isoformat(), now.isoformat())
155
+ connection.execute("INSERT INTO execution_run_leases(lease_id,run_id,host_identity,host_instance_id,process_id,acquired_at,last_heartbeat_at,expires_at,lease_state,lease_version,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)", values)
156
+ _event(connection, lease_id, run_id, "LEASE_ACQUIRED")
157
+ connection.execute("COMMIT")
158
+ return Lease(lease_id, run_id, identity, instance_id, now.isoformat(), now.isoformat(), expiry.isoformat(), "ACTIVE")
159
+ except Exception:
160
+ connection.execute("ROLLBACK")
161
+ raise
162
+ finally:
163
+ connection.close()
164
+
165
+
166
+ def heartbeat(root: Path, lease: Lease, *, timeout_seconds: int = LEASE_TIMEOUT_SECONDS, central_database: Path | None = None) -> Lease:
167
+ now = _now()
168
+ expiry = now + timedelta(seconds=timeout_seconds)
169
+ connection = _connection(root, central_database)
170
+ try:
171
+ updated = connection.execute("UPDATE execution_run_leases SET last_heartbeat_at=?,expires_at=?,updated_at=? WHERE lease_id=? AND run_id=? AND host_instance_id=? AND lease_state='ACTIVE'", (now.isoformat(), expiry.isoformat(), now.isoformat(), lease.lease_id, lease.run_id, lease.host_instance_id)).rowcount
172
+ if updated != 1:
173
+ raise LeaseConflictError("Execution Host no longer owns the active-run lease.")
174
+ finally:
175
+ connection.close()
176
+ return Lease(lease.lease_id, lease.run_id, lease.host_identity, lease.host_instance_id, lease.acquired_at, now.isoformat(), expiry.isoformat(), "ACTIVE")
177
+
178
+
179
+ def release(root: Path, lease: Lease, *, central_database: Path | None = None) -> None:
180
+ connection = _connection(root, central_database)
181
+ try:
182
+ connection.execute("BEGIN IMMEDIATE")
183
+ changed = connection.execute("UPDATE execution_run_leases SET lease_state='RELEASED',updated_at=? WHERE lease_id=? AND host_instance_id=? AND lease_state='ACTIVE'", (_now().isoformat(), lease.lease_id, lease.host_instance_id)).rowcount
184
+ if changed:
185
+ _event(connection, lease.lease_id, lease.run_id, "LEASE_RELEASED")
186
+ connection.execute("COMMIT")
187
+ finally:
188
+ connection.close()
189
+
190
+
191
+ def release_terminal_lease(root: Path, run_id: str, *, central_database: Path | None = None) -> bool:
192
+ """Release a departed host's lease only after its checkpoint is terminal.
193
+
194
+ The watcher calls this after its foreground child has exited and after it
195
+ has persisted the terminal transaction. It is not a stale-lease shortcut
196
+ and never decides lifecycle state itself.
197
+ """
198
+ connection = _connection(root, central_database)
199
+ try:
200
+ connection.execute("BEGIN IMMEDIATE")
201
+ row = connection.execute(
202
+ "SELECT phase,payload FROM engineering_transactions WHERE run_id=?", (run_id,)
203
+ ).fetchone()
204
+ terminal = False
205
+ if row and row[0] in TERMINAL_PHASES:
206
+ try:
207
+ payload = json.loads(row[1])
208
+ except (TypeError, json.JSONDecodeError):
209
+ payload = {}
210
+ terminal = isinstance(payload, dict) and payload.get("terminal") is True
211
+ if not terminal:
212
+ connection.execute("ROLLBACK")
213
+ return False
214
+ rows = connection.execute(
215
+ "SELECT lease_id FROM execution_run_leases WHERE run_id=? AND lease_state='ACTIVE'",
216
+ (run_id,),
217
+ ).fetchall()
218
+ now = _now().isoformat()
219
+ changed = connection.execute(
220
+ "UPDATE execution_run_leases SET lease_state='RELEASED',updated_at=? "
221
+ "WHERE run_id=? AND lease_state='ACTIVE'",
222
+ (now, run_id),
223
+ ).rowcount
224
+ for (lease_id,) in rows:
225
+ _event(connection, str(lease_id), run_id, "LEASE_RELEASED")
226
+ connection.execute("COMMIT")
227
+ return bool(changed)
228
+ except Exception:
229
+ connection.execute("ROLLBACK")
230
+ raise
231
+ finally:
232
+ connection.close()
233
+
234
+
235
+ def reconcile_stale(root: Path, *, central_database: Path | None = None) -> list[dict[str, str]]:
236
+ """Reconcile datastore ownership only; never fabricate a terminal lifecycle state."""
237
+ now = _now().isoformat()
238
+ outcomes: list[dict[str, str]] = []
239
+ interrupted_runs: list[str] = []
240
+ connection = _connection(root, central_database)
241
+ try:
242
+ connection.execute("BEGIN IMMEDIATE")
243
+ rows = connection.execute("SELECT lease_id,run_id,host_instance_id,last_heartbeat_at FROM execution_run_leases WHERE lease_state='ACTIVE' AND expires_at<?", (now,)).fetchall()
244
+ for lease_id, run_id, instance, heartbeat_at in rows:
245
+ # Provider execution is independent of the host heartbeat. A
246
+ # recovered provider turn can still be authoritatively active
247
+ # after its parent host disappeared; expiring this run lease
248
+ # would falsely make repository mutation ownership available.
249
+ recovery_lease_state = _recovery_provider_lease_state(connection, str(run_id))
250
+ if recovery_lease_state in {"ACTIVE", "AMBIGUOUS"}:
251
+ renewed = (_now() + timedelta(seconds=LEASE_TIMEOUT_SECONDS)).isoformat()
252
+ connection.execute(
253
+ "UPDATE execution_run_leases SET last_heartbeat_at=?,expires_at=?,updated_at=? "
254
+ "WHERE lease_id=? AND lease_state='ACTIVE'",
255
+ (now, renewed, now, lease_id),
256
+ )
257
+ outcome = (
258
+ "RECOVERY_PROVIDER_STILL_ACTIVE"
259
+ if recovery_lease_state == "ACTIVE" else "RECOVERY_PROVIDER_AMBIGUOUS"
260
+ )
261
+ _event(connection, str(lease_id), str(run_id), outcome)
262
+ outcomes.append({
263
+ "run_id": str(run_id), "host_instance_id": str(instance),
264
+ "last_heartbeat": str(heartbeat_at), "outcome": outcome,
265
+ })
266
+ continue
267
+ transaction = connection.execute(
268
+ "SELECT phase,payload FROM engineering_transactions WHERE run_id=?", (run_id,)
269
+ ).fetchone()
270
+ phase = transaction[0] if transaction else None
271
+ terminal_phase: str | None = phase if phase in TERMINAL_PHASES else None
272
+ if transaction and terminal_phase is None:
273
+ try:
274
+ payload = json.loads(transaction[1])
275
+ except (TypeError, json.JSONDecodeError):
276
+ payload = {}
277
+ candidate = payload.get("phase") if isinstance(payload, dict) and payload.get("terminal") is True else None
278
+ if candidate in TERMINAL_PHASES:
279
+ terminal_phase = candidate
280
+ connection.execute(
281
+ "UPDATE engineering_transactions SET phase=?,updated_at=? WHERE run_id=?",
282
+ (terminal_phase, now, run_id),
283
+ )
284
+ outcome = "TERMINAL_EVIDENCE_PRESENT" if terminal_phase else "RECOVERABLE"
285
+ connection.execute("UPDATE execution_run_leases SET lease_state='EXPIRED',updated_at=? WHERE lease_id=?", (now, lease_id))
286
+ _event(connection, lease_id, run_id, "LEASE_EXPIRED")
287
+ _event(connection, lease_id, run_id, "STALE_DETECTED", outcome)
288
+ _event(connection, lease_id, run_id, "STALE_RECONCILED", outcome)
289
+ connection.execute(
290
+ "INSERT INTO execution_run_reconciliations(run_id,outcome,reason,reconciled_at,updated_at) VALUES(?,?,?,?,?) "
291
+ "ON CONFLICT(run_id) DO UPDATE SET outcome=excluded.outcome,reason=excluded.reason,reconciled_at=excluded.reconciled_at,updated_at=excluded.updated_at",
292
+ (run_id, outcome, f"lease_expired;terminal_phase={terminal_phase or 'none'}", now, now),
293
+ )
294
+ # Close active spans after this lease transaction commits.
295
+ interrupted_runs.append(run_id)
296
+ outcomes.append({"run_id": run_id, "host_instance_id": instance, "last_heartbeat": heartbeat_at, "outcome": outcome})
297
+ active_runs = connection.execute(
298
+ "SELECT run_id,phase FROM engineering_transactions WHERE phase NOT IN ('COMPLETE','BLOCKED','FAILED')"
299
+ ).fetchall()
300
+ for run_id, phase in active_runs:
301
+ has_live_lease = connection.execute(
302
+ "SELECT 1 FROM execution_run_leases WHERE run_id=? AND lease_state='ACTIVE' AND expires_at>=?",
303
+ (run_id, now),
304
+ ).fetchone()
305
+ if has_live_lease:
306
+ continue
307
+ prior = connection.execute(
308
+ "SELECT 1 FROM execution_run_reconciliations WHERE run_id=?", (run_id,)
309
+ ).fetchone()
310
+ if prior:
311
+ continue
312
+ outcome = "INCONSISTENT" if phase in TERMINAL_PHASES else "OPERATOR_INTERVENTION_REQUIRED"
313
+ connection.execute(
314
+ "INSERT INTO execution_run_reconciliations(run_id,outcome,reason,reconciled_at,updated_at) VALUES(?,?,?,?,?)",
315
+ (run_id, outcome, "active_transaction_without_live_lease", now, now),
316
+ )
317
+ outcomes.append({"run_id": run_id, "host_instance_id": "unknown", "last_heartbeat": "unknown", "outcome": outcome})
318
+ connection.execute("COMMIT")
319
+ finally:
320
+ connection.close()
321
+ for run_id in interrupted_runs:
322
+ # Completed timing evidence remains immutable. This deliberately uses
323
+ # the actual reconciliation boundary rather than an invented expiry.
324
+ reconcile_interrupted_phases(root, run_id, central_database=central_database)
325
+ return outcomes
326
+
327
+
328
+ def liveness(root: Path, run_id: object, *, central_database: Path | None = None) -> dict[str, object]:
329
+ """Project canonical liveness without consulting status files or processes."""
330
+ if not isinstance(run_id, str):
331
+ return {"state": "UNAVAILABLE"}
332
+ now = _now().isoformat()
333
+ connection = _connection(root, central_database)
334
+ try:
335
+ row = connection.execute("SELECT host_identity,host_instance_id,last_heartbeat_at,expires_at,lease_state FROM execution_run_leases WHERE run_id=? ORDER BY created_at DESC LIMIT 1", (run_id,)).fetchone()
336
+ reconciliation = connection.execute(
337
+ "SELECT outcome,reason,reconciled_at FROM execution_run_reconciliations WHERE run_id=?", (run_id,)
338
+ ).fetchone()
339
+ finally:
340
+ connection.close()
341
+ if not row:
342
+ result: dict[str, object] = {"state": "STALE"}
343
+ if reconciliation:
344
+ result.update({"reconciliation_outcome": reconciliation[0], "reconciliation_reason": reconciliation[1], "reconciled_at": reconciliation[2]})
345
+ return result
346
+ identity, instance, heartbeat_at, expires_at, state = row
347
+ result = {"state": "LIVE" if state == "ACTIVE" and expires_at >= now else "STALE", "lease_state": state, "host_identity": identity, "host_instance_id": instance, "last_heartbeat": heartbeat_at, "lease_expiry": expires_at}
348
+ if reconciliation:
349
+ result.update({"reconciliation_outcome": reconciliation[0], "reconciliation_reason": reconciliation[1], "reconciled_at": reconciliation[2]})
350
+ return result
351
+
352
+
353
+ def history(root: Path, run_id: str, *, central_database: Path | None = None) -> dict[str, object]:
354
+ """Return bounded canonical lease evidence for reports and history views."""
355
+ connection = _connection(root, central_database)
356
+ try:
357
+ row = connection.execute(
358
+ "SELECT host_identity,host_instance_id,acquired_at,last_heartbeat_at,expires_at,lease_state "
359
+ "FROM execution_run_leases WHERE run_id=? ORDER BY created_at DESC LIMIT 1", (run_id,)
360
+ ).fetchone()
361
+ finally:
362
+ connection.close()
363
+ if not row:
364
+ return {"state": "UNAVAILABLE"}
365
+ return {"host_identity": row[0], "host_instance_id": row[1], "acquired_at": row[2], "last_heartbeat": row[3], "lease_expiry": row[4], "lease_state": row[5]}