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,488 @@
1
+ """Phase-P composition of canonical submissions with the preserved local host.
2
+
3
+ This module intentionally owns only the CENTRAL claim and the translation of a
4
+ canonical submission into the existing Execution Host input. Queue ordering,
5
+ predecessors, leases, provider invocation, recovery, finalization, reporting,
6
+ and telemetry remain owned by their historical implementations.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from contextlib import contextmanager
11
+ from dataclasses import dataclass
12
+ from datetime import datetime, timezone
13
+ import json
14
+ import os
15
+ from pathlib import Path
16
+ import re
17
+ import sqlite3
18
+ import uuid
19
+ from typing import Callable, Protocol
20
+
21
+ from . import submission_service
22
+ from .agent_state import StateError, StateStore, TransactionState, redact_diagnostic
23
+ from .execution_errors import RunnerError
24
+ from .execution_host import EngineeringRunner
25
+ from .execution_repository import GhCliClient, SubprocessRepositoryClient
26
+ from .parity_context import HistoricalCandidate, ParityProjectContext, historical_candidate, project_context
27
+ from .platform_bootstrap import provision_runtime_workspace
28
+ from .providers import CodexCliProvider, GitProvider
29
+ from .execution_executor import CodexCliClient
30
+ from .execution_reporting import generate_terminal_report
31
+ from .host_preflight import execute as execute_host_preflight
32
+ from .workspace_preflight import execute as execute_workspace_preflight
33
+ from .capability_preflight import execute as execute_capability_preflight
34
+ from .storage import (
35
+ CENTRAL_OPERATIONAL_DATABASE_ENVIRONMENT,
36
+ ENGINEERING_STORAGE_SCHEMA_VERSION,
37
+ record_admission_decision,
38
+ record_artifact,
39
+ record_run_qualification_context,
40
+ record_submission,
41
+ )
42
+ from .prompt_history import prompt_history, record_terminal_report
43
+ from .report_analysis import analyze as analyze_terminal_report
44
+
45
+
46
+ TERMINAL_STATES = frozenset({"COMPLETE", "BLOCKED", "FAILED"})
47
+ OPERATOR_RESOLUTION_OPEN = "OPEN"
48
+ OPERATOR_RESOLUTION_DISMISSED = "DISMISSED"
49
+ OPERATOR_RESOLUTION_RETRIED = "RETRIED"
50
+ _LOCAL_PATH = re.compile(r"(?:/[^\s:]+)+")
51
+
52
+
53
+ class ParityLifecycleDispatchError(RuntimeError):
54
+ """A stable failure at the Phase-P composition boundary."""
55
+
56
+
57
+ def dismiss_operator_gate(data_root: Path, *, project_id: str, run_id: str) -> dict[str, str]:
58
+ """Explicitly release a failed CENTRAL run's project FIFO gate."""
59
+ with sqlite3.connect(data_root / "engineering.db") as connection:
60
+ connection.execute("BEGIN IMMEDIATE")
61
+ cursor = connection.execute(
62
+ """UPDATE ep_parity_lifecycle_dispatches
63
+ SET operator_resolution=?,updated_at=?
64
+ WHERE project_id=? AND run_id=? AND state IN ('BLOCKED','FAILED')
65
+ AND operator_resolution=?""",
66
+ (OPERATOR_RESOLUTION_DISMISSED, _utcnow(), project_id, run_id, OPERATOR_RESOLUTION_OPEN),
67
+ )
68
+ if cursor.rowcount != 1:
69
+ connection.execute("ROLLBACK")
70
+ raise ParityLifecycleDispatchError("PROJECT_RUN_NOT_AWAITING_OPERATOR")
71
+ connection.execute("COMMIT")
72
+ return {"run_id": run_id, "handling_state": OPERATOR_RESOLUTION_DISMISSED}
73
+
74
+
75
+ def retry_operator_gate(data_root: Path, *, project_id: str, run_id: str) -> submission_service.SubmissionResult:
76
+ """Create the only FIFO-successor allowed to resolve a failed run."""
77
+ with sqlite3.connect(data_root / "engineering.db") as connection:
78
+ connection.execute("BEGIN IMMEDIATE")
79
+ row = connection.execute(
80
+ """SELECT d.repository_id,s.producer_id,s.producer_type,s.producer_version,
81
+ s.prompt,s.transport,s.correlation_id,s.mission_id,s.engineering_action_id,s.constraints
82
+ FROM ep_parity_lifecycle_dispatches AS d
83
+ JOIN ep_submissions AS s ON s.submission_id=d.submission_id
84
+ WHERE d.project_id=? AND d.run_id=? AND d.state IN ('BLOCKED','FAILED')
85
+ AND d.operator_resolution=?""",
86
+ (project_id, run_id, OPERATOR_RESOLUTION_OPEN),
87
+ ).fetchone()
88
+ if row is None:
89
+ connection.execute("ROLLBACK")
90
+ raise ParityLifecycleDispatchError("PROJECT_RUN_NOT_AWAITING_OPERATOR")
91
+ try:
92
+ constraints = json.loads(str(row[9]))
93
+ except (TypeError, ValueError, json.JSONDecodeError):
94
+ connection.execute("ROLLBACK")
95
+ raise ParityLifecycleDispatchError("RETRY_CONSTRAINTS_INVALID") from None
96
+ if not isinstance(constraints, dict):
97
+ connection.execute("ROLLBACK")
98
+ raise ParityLifecycleDispatchError("RETRY_CONSTRAINTS_INVALID")
99
+ request = submission_service.SubmissionRequest(
100
+ project_id=project_id, repository_id=str(row[0]), producer_id=str(row[1]),
101
+ producer_type=str(row[2]), producer_version=str(row[3]) if row[3] is not None else None,
102
+ prompt=f"Retry-Of: {run_id}\n\n{str(row[4])}", transport=str(row[5]),
103
+ correlation_id=str(row[6]) if row[6] is not None else None,
104
+ mission_id=str(row[7]) if row[7] is not None else None,
105
+ engineering_action_id=str(row[8]) if row[8] is not None else None, constraints=constraints,
106
+ )
107
+ result = submission_service.submit(connection, request)
108
+ cursor = connection.execute(
109
+ """UPDATE ep_parity_lifecycle_dispatches
110
+ SET operator_resolution=?,resolution_submission_id=?,updated_at=?
111
+ WHERE project_id=? AND run_id=? AND operator_resolution=?""",
112
+ (OPERATOR_RESOLUTION_RETRIED, result.submission_id, _utcnow(), project_id, run_id, OPERATOR_RESOLUTION_OPEN),
113
+ )
114
+ if cursor.rowcount != 1:
115
+ connection.execute("ROLLBACK")
116
+ raise ParityLifecycleDispatchError("PROJECT_RETRY_RESOLUTION_CONFLICT")
117
+ connection.execute("COMMIT")
118
+ return result
119
+
120
+
121
+ class Runner(Protocol):
122
+ def run(self, prompt_path: Path, run_id: str | None = None, resume: bool = False,
123
+ owner_authorized: bool = False, transaction_kind: str = "IMPLEMENTATION") -> TransactionState: ...
124
+
125
+
126
+ RunnerFactory = Callable[[Path], Runner]
127
+
128
+
129
+ @dataclass(frozen=True)
130
+ class DispatchReceipt:
131
+ submission_id: str
132
+ project_id: str
133
+ repository_id: str
134
+ run_id: str
135
+ state: str
136
+ duplicate_claim: bool
137
+
138
+
139
+ def _utcnow() -> str:
140
+ return datetime.now(timezone.utc).isoformat()
141
+
142
+
143
+ def _allocate_run_id() -> str:
144
+ """Allocate a canonical CENTRAL lifecycle run identity."""
145
+ return f"inbox-{uuid.uuid4().hex}"
146
+
147
+
148
+ def _record_provider_free_admission(
149
+ repository_root: Path,
150
+ *,
151
+ run_id: str,
152
+ submission_id: str,
153
+ execution_mode: str,
154
+ results: tuple[object, ...],
155
+ ) -> tuple[str, tuple[str, ...]]:
156
+ """Persist the deterministic pre-provider admission decision centrally.
157
+
158
+ This belongs to the lifecycle composition boundary. It deliberately does
159
+ not claim a File Inbox item or depend on the retired watcher runtime.
160
+ """
161
+ gates: list[dict[str, object]] = []
162
+ failures: list[str] = []
163
+ for result in results:
164
+ timestamp = getattr(result, "timestamp", "unavailable")
165
+ stage = type(result).__name__.removesuffix("Result")
166
+ for check in getattr(result, "checks", ()):
167
+ identifier = str(getattr(check, "identifier", "unavailable"))
168
+ observed = str(getattr(check, "outcome", "UNAVAILABLE"))
169
+ gates.append({
170
+ "gate_id": identifier,
171
+ "stage": stage,
172
+ "expected": "PASS",
173
+ "observed": observed,
174
+ "verified_at": timestamp,
175
+ })
176
+ if observed == "FAIL":
177
+ failures.append(identifier)
178
+ decision = "FAIL" if failures else "PASS"
179
+ record_admission_decision(
180
+ repository_root,
181
+ run_id=run_id,
182
+ submission_id=submission_id,
183
+ execution_mode=execution_mode,
184
+ decision=decision,
185
+ failed_gate_ids=tuple(failures),
186
+ evidence=tuple(gates),
187
+ observed_at=_utcnow(),
188
+ )
189
+ return decision, tuple(failures)
190
+
191
+
192
+ def _default_runner(repository_root: Path, *, central_database: Path | None = None) -> EngineeringRunner:
193
+ """Construct the installed historical runner without a watcher or Agent."""
194
+ remote = GitProvider().execute(repository_root, "git", "remote", "get-url", "origin")
195
+ match = re.search(r"github\.com[/:]([^/]+/[^/]+?)(?:\.git)?$", remote.stdout.strip())
196
+ repository = match.group(1) if remote.returncode == 0 and match else None
197
+ return EngineeringRunner(
198
+ repository_root,
199
+ StateStore(
200
+ repository_root / ".engineering" / "engineering-runs",
201
+ central_database=central_database,
202
+ emit_local_projection=False,
203
+ ),
204
+ SubprocessRepositoryClient(),
205
+ GhCliClient(repository=repository),
206
+ CodexCliClient(CodexCliProvider()),
207
+ )
208
+
209
+
210
+ @contextmanager
211
+ def _historical_admission_environment(repository_root: Path, data_root: Path):
212
+ """Keep the runner's existing persisted-admission guard in force."""
213
+ keys = (
214
+ "ENGINEERING_PLATFORM_ADMITTED_STORAGE_SCHEMA",
215
+ "ENGINEERING_PLATFORM_ADMITTED_STORAGE_ROOT",
216
+ CENTRAL_OPERATIONAL_DATABASE_ENVIRONMENT,
217
+ )
218
+ previous = {key: os.environ.get(key) for key in keys}
219
+ os.environ[keys[0]] = str(ENGINEERING_STORAGE_SCHEMA_VERSION)
220
+ os.environ[keys[1]] = str(repository_root)
221
+ os.environ[CENTRAL_OPERATIONAL_DATABASE_ENVIRONMENT] = str(data_root / "engineering.db")
222
+ try:
223
+ yield
224
+ finally:
225
+ for key, value in previous.items():
226
+ if value is None:
227
+ os.environ.pop(key, None)
228
+ else:
229
+ os.environ[key] = value
230
+
231
+
232
+ class ParityLifecycleDispatcher:
233
+ """The one installed local writer allowed to claim a parity submission."""
234
+
235
+ def __init__(self, data_root: Path, *, runner_factory: RunnerFactory | None = None) -> None:
236
+ self.data_root = data_root.resolve()
237
+ self.runner_factory = runner_factory or (
238
+ lambda root: _default_runner(root, central_database=self.data_root / "engineering.db")
239
+ )
240
+
241
+ def _prompt_path(self, context: ParityProjectContext, run_id: str) -> Path:
242
+ # Prompts and mutable lifecycle evidence are installation-owned. The
243
+ # checked-out repository is an execution target, never a queue store.
244
+ path = self.data_root / "artifacts" / "projects" / context.project_id / "runs" / run_id / "submission.md"
245
+ path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
246
+ return path
247
+
248
+ def _claim(self, submission_id: str) -> tuple[ParityProjectContext, HistoricalCandidate, str, Path, bool]:
249
+ with sqlite3.connect(self.data_root / "engineering.db") as connection:
250
+ connection.execute("PRAGMA foreign_keys=ON")
251
+ connection.execute("BEGIN IMMEDIATE")
252
+ existing = connection.execute(
253
+ "SELECT project_id,repository_id,run_id,prompt_path FROM ep_parity_lifecycle_dispatches WHERE submission_id=?",
254
+ (submission_id,),
255
+ ).fetchone()
256
+ if existing is not None:
257
+ provenance = connection.execute(
258
+ """SELECT 1 FROM ep_receipt_run_provenance p
259
+ JOIN engineering_metadata m ON m.key='installation.instance_id'
260
+ WHERE p.submission_id=? AND p.run_id=?
261
+ AND p.project_id=? AND p.repository_id=?
262
+ AND p.installation_id=m.value""",
263
+ (submission_id, str(existing[2]), str(existing[0]), str(existing[1])),
264
+ ).fetchone()
265
+ if provenance is None:
266
+ connection.execute("ROLLBACK")
267
+ raise ParityLifecycleDispatchError("PROVENANCE_REPLAY_INVALID")
268
+ context = project_context(connection, data_root=self.data_root, project_id=str(existing[0]), repository_id=str(existing[1]))
269
+ candidate = historical_candidate(connection, context=context, submission_id=submission_id)
270
+ connection.execute("COMMIT")
271
+ return context, candidate, str(existing[2]), Path(str(existing[3])), True
272
+ row = connection.execute("SELECT project_id,repository_id FROM ep_submissions WHERE submission_id=?", (submission_id,)).fetchone()
273
+ if row is None:
274
+ connection.execute("ROLLBACK")
275
+ raise ParityLifecycleDispatchError("UNKNOWN_SUBMISSION")
276
+ context = project_context(connection, data_root=self.data_root, project_id=str(row[0]), repository_id=str(row[1]))
277
+ active = connection.execute(
278
+ """SELECT run_id FROM ep_parity_lifecycle_dispatches
279
+ WHERE project_id=? AND (
280
+ state IN ('CLAIMED','RUNNING')
281
+ OR (state IN ('BLOCKED','FAILED') AND operator_resolution='OPEN')
282
+ OR (operator_resolution='RETRIED' AND resolution_submission_id!=?)
283
+ ) LIMIT 1""",
284
+ (context.project_id, submission_id),
285
+ ).fetchone()
286
+ if active is not None:
287
+ connection.execute("ROLLBACK")
288
+ raise ParityLifecycleDispatchError("PROJECT_RUN_ALREADY_ACTIVE")
289
+ candidate = historical_candidate(connection, context=context, submission_id=submission_id)
290
+ run_id = _allocate_run_id()
291
+ prompt = self._prompt_path(context, run_id)
292
+ now = _utcnow()
293
+ connection.execute(
294
+ "INSERT INTO ep_execution_runs(run_id,project_id,state,created_at,updated_at,execution_mode) VALUES(?,?, 'CLAIMED', ?, ?, ?)",
295
+ (run_id, context.project_id, now, now, candidate.execution_mode),
296
+ )
297
+ connection.execute(
298
+ "INSERT INTO ep_parity_lifecycle_dispatches(submission_id,project_id,repository_id,run_id,state,prompt_path,claimed_at,updated_at) VALUES(?,?,?,?, 'CLAIMED', ?,?,?)",
299
+ (submission_id, context.project_id, context.repository_id, run_id, str(prompt), now, now),
300
+ )
301
+ installation = connection.execute("SELECT value FROM engineering_metadata WHERE key='installation.instance_id'").fetchone()
302
+ if installation is None:
303
+ connection.execute("ROLLBACK")
304
+ raise ParityLifecycleDispatchError("PROVENANCE_INSTALLATION_UNAVAILABLE")
305
+ connection.execute(
306
+ "INSERT INTO ep_receipt_run_provenance(submission_id,run_id,project_id,repository_id,installation_id,created_at) VALUES(?,?,?,?,?,?)",
307
+ (submission_id, run_id, context.project_id, context.repository_id, str(installation[0]), now),
308
+ )
309
+ connection.execute("COMMIT")
310
+ return context, candidate, run_id, prompt, False
311
+
312
+ @staticmethod
313
+ def _persist_historical_input(repository_root: Path, candidate: HistoricalCandidate, run_id: str, prompt: Path) -> None:
314
+ """Persist canonical input and run the standalone admission checks."""
315
+ # The preserved host preflight requires the standard runtime layout.
316
+ # Reuse its product bootstrap rather than creating a P-A-specific one.
317
+ provision_runtime_workspace(repository_root)
318
+ prompt.write_text(candidate.prompt, encoding="utf-8")
319
+ now = _utcnow()
320
+ record_submission(
321
+ repository_root, submission_id=candidate.submission_id,
322
+ producer_id=candidate.producer_id, producer_type=candidate.producer_type,
323
+ producer_version=candidate.producer_version, contract_version="1.0",
324
+ prompt_content=candidate.prompt,
325
+ prompt_metadata={"filename": prompt.name, "digest": candidate.prompt_digest, "title": "CENTRAL parity submission"},
326
+ target_identity={"project_id": candidate.context.project_id, "repository_id": candidate.context.repository_id, "path": str(repository_root)},
327
+ original_envelope=candidate.producer_envelope(), correlation_id=candidate.correlation_id,
328
+ mission_id=candidate.mission_id, engineering_action_id=candidate.engineering_action_id,
329
+ link_run_id=run_id, received_at=now,
330
+ )
331
+ record_run_qualification_context(
332
+ repository_root, run_id=run_id, submission_id=candidate.submission_id,
333
+ fresh_submission=True, retry_parent_run_id=None, resume_parent_run_id=None, recorded_at=now,
334
+ )
335
+ host = execute_host_preflight(repository_root, run_id=run_id)
336
+ workspace = execute_workspace_preflight(repository_root, candidate.prompt, run_id=run_id)
337
+ capability = execute_capability_preflight(repository_root, candidate.prompt, run_id=run_id)
338
+ decision, _ = _record_provider_free_admission(
339
+ repository_root, run_id=run_id, submission_id=candidate.submission_id,
340
+ execution_mode=candidate.execution_mode, results=(host, workspace, capability),
341
+ )
342
+ if decision != "PASS":
343
+ for result in (host, workspace, capability):
344
+ for check in getattr(result, "checks", ()):
345
+ if getattr(check, "outcome", None) == "FAIL":
346
+ raise ParityLifecycleDispatchError(
347
+ "HISTORICAL_ADMISSION_BLOCKED|%s|%s|%s"
348
+ % (type(result).__name__.removesuffix("Result"),
349
+ getattr(check, "identifier", "unavailable"),
350
+ redact_diagnostic(str(getattr(check, "reason", "Preflight check failed.")), limit=500))
351
+ )
352
+ raise ParityLifecycleDispatchError("HISTORICAL_ADMISSION_BLOCKED")
353
+
354
+ def _set_state(self, submission_id: str, run_id: str, state: str) -> None:
355
+ if state not in {"CLAIMED", "RUNNING", *TERMINAL_STATES}:
356
+ raise ParityLifecycleDispatchError("INVALID_DISPATCH_STATE")
357
+ with sqlite3.connect(self.data_root / "engineering.db") as connection:
358
+ now = _utcnow()
359
+ connection.execute("UPDATE ep_execution_runs SET state=?,updated_at=? WHERE run_id=?", (state, now, run_id))
360
+ resolution = OPERATOR_RESOLUTION_OPEN if state in {"BLOCKED", "FAILED"} else "NONE"
361
+ connection.execute(
362
+ """UPDATE ep_parity_lifecycle_dispatches
363
+ SET state=?,operator_resolution=?,updated_at=?
364
+ WHERE submission_id=? AND run_id=?""",
365
+ (state, resolution, now, submission_id, run_id),
366
+ )
367
+
368
+ @staticmethod
369
+ def _terminal_history_exists(repository_root: Path, run_id: str, data_root: Path) -> bool:
370
+ return any(item.get("run_id") == run_id for item in prompt_history(
371
+ repository_root, central_database=data_root / "engineering.db"
372
+ ))
373
+
374
+ @classmethod
375
+ def _project_terminal_history(cls, repository_root: Path, state: TransactionState,
376
+ runner: object | None = None, data_root: Path | None = None) -> None:
377
+ """Preserve the terminal report and full historical Console projection.
378
+
379
+ CENTRAL composes the historical runner directly, bypassing its CLI
380
+ entrypoint. The entrypoint normally writes this evidence; retaining
381
+ that post-run boundary here keeps a completed CENTRAL run visible in
382
+ its project-scoped Operations Console.
383
+ """
384
+ if data_root is None:
385
+ raise ParityLifecycleDispatchError("CENTRAL_HISTORY_DATABASE_REQUIRED")
386
+ if not state.terminal or cls._terminal_history_exists(repository_root, state.run_id, data_root):
387
+ return
388
+ report = generate_terminal_report(
389
+ repository_root, state,
390
+ getattr(runner, "platform_manifest", None),
391
+ getattr(runner, "detected_codex_cli", None),
392
+ getattr(runner, "reviewer_records", ()),
393
+ getattr(getattr(runner, "agent", None), "last_runtime_metadata", None),
394
+ getattr(getattr(runner, "agent", None), "last_execution_metadata", None),
395
+ central_database=data_root / "engineering.db",
396
+ )
397
+ record_terminal_report(repository_root, report, central_database=data_root / "engineering.db")
398
+ record_artifact(
399
+ repository_root, report, artifact_id=f"report:{state.run_id}",
400
+ artifact_type="TERMINAL_REPORT", content_type="text/markdown",
401
+ created_at=_utcnow(), run_id=state.run_id,
402
+ central_database=data_root / "engineering.db", artifact_root=data_root / "artifacts",
403
+ )
404
+ analyze_terminal_report(repository_root, state.run_id, report)
405
+
406
+ def reconcile_terminal_history(self) -> None:
407
+ """Backfill only missing Console rows for terminal CENTRAL runs."""
408
+ with sqlite3.connect(self.data_root / "engineering.db") as connection:
409
+ rows = connection.execute(
410
+ "SELECT project_id,repository_id,run_id FROM ep_parity_lifecycle_dispatches "
411
+ "WHERE state IN ('COMPLETE','BLOCKED','FAILED') ORDER BY claimed_at"
412
+ ).fetchall()
413
+ contexts = [
414
+ (project_context(connection, data_root=self.data_root, project_id=str(project_id), repository_id=str(repository_id)), str(run_id))
415
+ for project_id, repository_id, run_id in rows
416
+ ]
417
+ for context, run_id in contexts:
418
+ if context.local_repository_root is None:
419
+ continue
420
+ try:
421
+ state = StateStore(
422
+ context.local_repository_root / ".engineering" / "engineering-runs",
423
+ central_database=self.data_root / "engineering.db",
424
+ emit_local_projection=False,
425
+ ).load(run_id)
426
+ except StateError:
427
+ # CENTRAL terminal history can outlive the local retained
428
+ # checkpoint. It is already durable history, not an active
429
+ # recovery candidate; never let such a row prevent Server
430
+ # startup or fresh project-scoped queue processing.
431
+ continue
432
+ self._project_terminal_history(context.local_repository_root, state, data_root=self.data_root)
433
+
434
+ def _record_early_runner_failure(self, *, submission_id: str, context: ParityProjectContext,
435
+ run_id: str, error: Exception, stage: str = "RUNNER_INITIALIZATION") -> None:
436
+ """Persist only the missing pre-checkpoint explanation, never a run state."""
437
+ message = _LOCAL_PATH.sub("[LOCAL_PATH]", redact_diagnostic(str(error), limit=500))
438
+ component = code = None
439
+ if message.startswith("HISTORICAL_ADMISSION_BLOCKED|"):
440
+ _, component, code, message = message.split("|", 3)
441
+ path = self.data_root / "artifacts" / "projects" / context.project_id / "runs" / run_id / "early-runner-failure.json"
442
+ payload = {"submission_id": submission_id, "project_id": context.project_id,
443
+ "repository_id": context.repository_id, "run_id": run_id,
444
+ "failure_stage": stage, "error_type": type(error).__name__,
445
+ "diagnostic_code": "RUNNER_EARLY_FAILURE" if stage == "RUNNER_INITIALIZATION" else "PRECHECKPOINT_FAILURE", "message": message,
446
+ "admission_component": component, "component_code": code,
447
+ "recorded_at": _utcnow()}
448
+ path.write_text(json.dumps(payload, sort_keys=True) + "\n", encoding="utf-8")
449
+ path.chmod(0o600)
450
+
451
+ def dispatch(self, submission_id: str) -> DispatchReceipt:
452
+ context, candidate, run_id, prompt, duplicate = self._claim(submission_id)
453
+ if context.local_repository_root is None:
454
+ raise ParityLifecycleDispatchError("LOCAL_BINDING_UNBOUND")
455
+ repository_root = context.local_repository_root
456
+ self._set_state(submission_id, run_id, "RUNNING")
457
+ # The installed ingress qualification deliberately stops only after
458
+ # CENTRAL has allocated and persisted the canonical dispatch/run. It
459
+ # exercises the real worker and dispatcher, while avoiding provider,
460
+ # checkout and PR side effects for six transport canaries.
461
+ if os.environ.get("EP_QUALIFICATION_INITIALIZE_ONLY") == "1":
462
+ return DispatchReceipt(submission_id, context.project_id, context.repository_id, run_id, "RUNNING", duplicate)
463
+ try:
464
+ with _historical_admission_environment(repository_root, self.data_root):
465
+ if not duplicate:
466
+ self._persist_historical_input(repository_root, candidate, run_id, prompt)
467
+ runner = self.runner_factory(repository_root)
468
+ state = runner.run(
469
+ prompt, run_id=run_id, resume=duplicate,
470
+ owner_authorized=candidate.execution_mode == "MANAGED",
471
+ )
472
+ # Report/history indexing is execution evidence too. Keep it
473
+ # inside the explicit CENTRAL context; a terminal projection
474
+ # must never reopen the repository-local database.
475
+ self._project_terminal_history(repository_root, state, runner, self.data_root)
476
+ terminal = state.phase if state.phase in TERMINAL_STATES else "RUNNING"
477
+ self._set_state(submission_id, run_id, terminal)
478
+ return DispatchReceipt(submission_id, context.project_id, context.repository_id, run_id, terminal, duplicate)
479
+ except RunnerError as error:
480
+ self._record_early_runner_failure(submission_id=submission_id, context=context, run_id=run_id, error=error)
481
+ self._set_state(submission_id, run_id, "BLOCKED")
482
+ raise
483
+ except Exception as error:
484
+ # A nonterminal checkpoint is deliberately resumable with the same
485
+ # run ID; an uncheckpointed admission failure is visible as BLOCKED.
486
+ self._record_early_runner_failure(submission_id=submission_id, context=context, run_id=run_id, error=error, stage="PRECHECKPOINT")
487
+ self._set_state(submission_id, run_id, "BLOCKED")
488
+ raise
@@ -0,0 +1,13 @@
1
+ """Installation-owner authorization for local Server administration."""
2
+ from __future__ import annotations
3
+
4
+ from pathlib import Path
5
+ import os
6
+
7
+
8
+ def require_installation_owner(data_root: Path) -> str:
9
+ """Authorize only the owner of the private Server data root."""
10
+ owner = data_root.resolve().stat().st_uid
11
+ if os.geteuid() != owner:
12
+ raise PermissionError("PLATFORM_ADMIN_FORBIDDEN")
13
+ return str(owner)