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.
- engineering_platform/ENGINEERING_PLATFORM_CONFIG.json +32 -0
- engineering_platform/ENGINEERING_PLATFORM_VERSION.json +15 -0
- engineering_platform/__init__.py +1 -0
- engineering_platform/__main__.py +7 -0
- engineering_platform/agent_state.py +530 -0
- engineering_platform/agent_trust.py +174 -0
- engineering_platform/assets/dashboard.css +1317 -0
- engineering_platform/assets/dashboard.js +8534 -0
- engineering_platform/assets/dashboard_locales.mjs +4049 -0
- engineering_platform/assets/dashboard_status_store.mjs +41 -0
- engineering_platform/assets/operations-console/apple-touch-icon-dark.png +0 -0
- engineering_platform/assets/operations-console/apple-touch-icon-light.png +0 -0
- engineering_platform/assets/operations-console/icon-dark.png +0 -0
- engineering_platform/assets/operations-console/icon-light.png +0 -0
- engineering_platform/assets/operations-console/icon-transparent.png +0 -0
- engineering_platform/assets/operations-console/manifest.webmanifest +11 -0
- engineering_platform/capability_preflight.py +285 -0
- engineering_platform/capability_review.py +261 -0
- engineering_platform/central_data_transfer.py +195 -0
- engineering_platform/central_database.py +245 -0
- engineering_platform/central_store_migration.py +1672 -0
- engineering_platform/codex_capacity.py +81 -0
- engineering_platform/codex_chat.py +226 -0
- engineering_platform/codex_observability.py +153 -0
- engineering_platform/component_lock.py +40 -0
- engineering_platform/component_logging.py +420 -0
- engineering_platform/console_presentation.py +14 -0
- engineering_platform/console_route_ownership.py +83 -0
- engineering_platform/contracts/__init__.py +38 -0
- engineering_platform/contracts/ep_consumer.py +391 -0
- engineering_platform/contracts/models.py +105 -0
- engineering_platform/contracts/projection.py +401 -0
- engineering_platform/dashboard_browser_validation.py +206 -0
- engineering_platform/dashboard_state.py +630 -0
- engineering_platform/dashboard_supervisor.swift +105 -0
- engineering_platform/dashboard_translation.py +129 -0
- engineering_platform/dependabot_producer.py +349 -0
- engineering_platform/drift_diagnostics.py +144 -0
- engineering_platform/emergency_recovery.py +268 -0
- engineering_platform/engineering_memory.py +139 -0
- engineering_platform/ep_consumer_credentials.py +473 -0
- engineering_platform/evidence_projection.py +213 -0
- engineering_platform/execution_activity.py +218 -0
- engineering_platform/execution_context.py +132 -0
- engineering_platform/execution_errors.py +42 -0
- engineering_platform/execution_evidence.py +24 -0
- engineering_platform/execution_executor.py +730 -0
- engineering_platform/execution_finalization.py +44 -0
- engineering_platform/execution_host.py +3306 -0
- engineering_platform/execution_lease.py +365 -0
- engineering_platform/execution_lifecycle.py +447 -0
- engineering_platform/execution_models.py +43 -0
- engineering_platform/execution_readiness.py +166 -0
- engineering_platform/execution_reporting.py +1607 -0
- engineering_platform/execution_repository.py +253 -0
- engineering_platform/execution_timeout_policy.py +56 -0
- engineering_platform/execution_timing.py +440 -0
- engineering_platform/execution_transaction.py +28 -0
- engineering_platform/external_producer_binding.py +235 -0
- engineering_platform/file_inbox.py +249 -0
- engineering_platform/forensic_attribution.py +338 -0
- engineering_platform/forensic_attribution_v2.py +134 -0
- engineering_platform/forensic_delta.py +299 -0
- engineering_platform/golden_scenario.py +63 -0
- engineering_platform/historical_dashboard_configuration.py +171 -0
- engineering_platform/host_admin.py +199 -0
- engineering_platform/host_preflight.py +231 -0
- engineering_platform/installation_relocation.py +122 -0
- engineering_platform/investigation_ledger.py +89 -0
- engineering_platform/legacy_inbox_migration.py +79 -0
- engineering_platform/lifecycle_worker.py +223 -0
- engineering_platform/live_status.py +267 -0
- engineering_platform/local_api.py +209 -0
- engineering_platform/local_api_keychain.py +51 -0
- engineering_platform/local_repository_binding.py +138 -0
- engineering_platform/managed_autonomy.py +509 -0
- engineering_platform/managed_codex_runtime.py +105 -0
- engineering_platform/parity_context.py +203 -0
- engineering_platform/parity_lifecycle_dispatcher.py +488 -0
- engineering_platform/platform_admin.py +13 -0
- engineering_platform/platform_api.py +428 -0
- engineering_platform/platform_bootstrap.py +385 -0
- engineering_platform/platform_components.py +65 -0
- engineering_platform/platform_version.py +171 -0
- engineering_platform/pr_check_repair.py +276 -0
- engineering_platform/pr_evidence_backfill.py +278 -0
- engineering_platform/producer.py +209 -0
- engineering_platform/project_agent.py +366 -0
- engineering_platform/project_agent_service.py +244 -0
- engineering_platform/project_topology.py +126 -0
- engineering_platform/prompt_history.py +591 -0
- engineering_platform/provider_context.py +136 -0
- engineering_platform/provider_context_benchmark.py +41 -0
- engineering_platform/provider_context_scope.py +90 -0
- engineering_platform/provider_interruption.py +168 -0
- engineering_platform/provider_process_identity.py +80 -0
- engineering_platform/provider_readiness.py +138 -0
- engineering_platform/provider_recovery.py +647 -0
- engineering_platform/provider_usage.py +497 -0
- engineering_platform/providers.py +471 -0
- engineering_platform/qualification.py +220 -0
- engineering_platform/recommendation_handoff.py +238 -0
- engineering_platform/report_analysis.py +193 -0
- engineering_platform/repository_attachment.py +171 -0
- engineering_platform/repository_handoff.py +95 -0
- engineering_platform/resources.py +38 -0
- engineering_platform/reviewer_evidence.py +70 -0
- engineering_platform/schemas/repository-attachment.schema.json +61 -0
- engineering_platform/server.py +3679 -0
- engineering_platform/server_console_services.py +2024 -0
- engineering_platform/server_relay.py +172 -0
- engineering_platform/server_service.py +122 -0
- engineering_platform/status_model.py +135 -0
- engineering_platform/status_reconciliation.py +34 -0
- engineering_platform/storage.py +2440 -0
- engineering_platform/submission_cli.py +77 -0
- engineering_platform/submission_intake.py +45 -0
- engineering_platform/submission_service.py +317 -0
- engineering_platform/telemetry.py +951 -0
- engineering_platform/templates/workspace-config.json +25 -0
- engineering_platform/validation_identity.py +50 -0
- engineering_platform/validation_profile.py +211 -0
- engineering_platform/workspace_preflight.py +263 -0
- engineering_platform/worktree_provenance.py +147 -0
- engineering_platform/worktree_tooling.py +18 -0
- engineering_platform-2.2.0.dist-info/METADATA +18 -0
- engineering_platform-2.2.0.dist-info/RECORD +130 -0
- engineering_platform-2.2.0.dist-info/WHEEL +5 -0
- engineering_platform-2.2.0.dist-info/entry_points.txt +6 -0
- 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)
|