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,2440 @@
|
|
|
1
|
+
"""Versioned SQLite schema contract for private Engineering Platform evidence.
|
|
2
|
+
|
|
3
|
+
This module owns only the database lifecycle. Consumers are migrated to it in a
|
|
4
|
+
separate, compatibility-tested change; an unrecognized database is never
|
|
5
|
+
silently replaced or downgraded.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
from datetime import datetime, timezone
|
|
12
|
+
import argparse
|
|
13
|
+
import json
|
|
14
|
+
import hashlib
|
|
15
|
+
import os
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
import re
|
|
18
|
+
import sqlite3
|
|
19
|
+
import sys
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
WORKSPACE_DIRECTORY = ".engineering"
|
|
23
|
+
DATABASE_FILENAME = "engineering.db"
|
|
24
|
+
ENGINEERING_STORAGE_SCHEMA_VERSION = 41
|
|
25
|
+
STORE_AUTHORITY_POINTER = "store-authority.json"
|
|
26
|
+
JOURNAL_MODES = frozenset({"DELETE", "MEMORY"})
|
|
27
|
+
LEGACY_DISMISSALS_PATH = Path(".engineering/status/execution_dismissals.json")
|
|
28
|
+
ADMITTED_STORAGE_SCHEMA_ENVIRONMENT = "ENGINEERING_PLATFORM_ADMITTED_STORAGE_SCHEMA"
|
|
29
|
+
ADMITTED_STORAGE_ROOT_ENVIRONMENT = "ENGINEERING_PLATFORM_ADMITTED_STORAGE_ROOT"
|
|
30
|
+
# This is set only by the installed CENTRAL lifecycle composition boundary.
|
|
31
|
+
# It is deliberately not inferred from a checkout: an explicit installation
|
|
32
|
+
# database is the only supported operational authority for a standalone run.
|
|
33
|
+
CENTRAL_OPERATIONAL_DATABASE_ENVIRONMENT = "EP_CENTRAL_OPERATIONAL_DATABASE"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class EngineeringStorageError(RuntimeError):
|
|
37
|
+
"""Raised when the local Engineering evidence database is unsafe to use."""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _admitted_migration_ceiling(root: Path) -> int | None:
|
|
41
|
+
"""Return the watcher-admitted schema only for its canonical workspace."""
|
|
42
|
+
admitted_root = os.environ.get(ADMITTED_STORAGE_ROOT_ENVIRONMENT)
|
|
43
|
+
admitted_schema = os.environ.get(ADMITTED_STORAGE_SCHEMA_ENVIRONMENT)
|
|
44
|
+
if not admitted_root and not admitted_schema:
|
|
45
|
+
return None
|
|
46
|
+
if not admitted_root or not admitted_schema:
|
|
47
|
+
raise EngineeringStorageError("Engineering storage admission context is incomplete.")
|
|
48
|
+
try:
|
|
49
|
+
ceiling = int(admitted_schema)
|
|
50
|
+
except ValueError as error:
|
|
51
|
+
raise EngineeringStorageError("Engineering storage admission schema is invalid.") from error
|
|
52
|
+
if ceiling < 1:
|
|
53
|
+
raise EngineeringStorageError("Engineering storage admission schema is invalid.")
|
|
54
|
+
try:
|
|
55
|
+
applies = root.resolve() == Path(admitted_root).resolve()
|
|
56
|
+
except OSError as error:
|
|
57
|
+
raise EngineeringStorageError("Engineering storage admission root is invalid.") from error
|
|
58
|
+
return ceiling if applies else None
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
Migration = Callable[[sqlite3.Connection], None]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _schema_v1(connection: sqlite3.Connection) -> None:
|
|
65
|
+
"""Create the initial normalized local-evidence schema."""
|
|
66
|
+
for statement in """
|
|
67
|
+
CREATE TABLE IF NOT EXISTS engineering_schema_migrations (
|
|
68
|
+
version INTEGER PRIMARY KEY,
|
|
69
|
+
applied_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
70
|
+
);
|
|
71
|
+
CREATE TABLE IF NOT EXISTS engineering_metadata (
|
|
72
|
+
key TEXT PRIMARY KEY,
|
|
73
|
+
value TEXT NOT NULL
|
|
74
|
+
);
|
|
75
|
+
CREATE TABLE IF NOT EXISTS engineering_status (
|
|
76
|
+
name TEXT PRIMARY KEY,
|
|
77
|
+
payload TEXT NOT NULL,
|
|
78
|
+
updated_at TEXT NOT NULL
|
|
79
|
+
);
|
|
80
|
+
CREATE TABLE IF NOT EXISTS engineering_transactions (
|
|
81
|
+
run_id TEXT PRIMARY KEY,
|
|
82
|
+
payload TEXT NOT NULL,
|
|
83
|
+
phase TEXT NOT NULL,
|
|
84
|
+
updated_at TEXT NOT NULL
|
|
85
|
+
);
|
|
86
|
+
CREATE TABLE IF NOT EXISTS engineering_artifacts (
|
|
87
|
+
id INTEGER PRIMARY KEY,
|
|
88
|
+
category TEXT NOT NULL,
|
|
89
|
+
run_id TEXT,
|
|
90
|
+
name TEXT NOT NULL,
|
|
91
|
+
content BLOB NOT NULL,
|
|
92
|
+
created_at TEXT NOT NULL,
|
|
93
|
+
UNIQUE(category, run_id, name)
|
|
94
|
+
);
|
|
95
|
+
CREATE INDEX IF NOT EXISTS engineering_artifacts_lookup
|
|
96
|
+
ON engineering_artifacts(category, run_id, created_at DESC);
|
|
97
|
+
CREATE TABLE IF NOT EXISTS engineering_component_logs (
|
|
98
|
+
id INTEGER PRIMARY KEY,
|
|
99
|
+
component TEXT NOT NULL,
|
|
100
|
+
payload TEXT NOT NULL,
|
|
101
|
+
created_at TEXT NOT NULL
|
|
102
|
+
);
|
|
103
|
+
CREATE INDEX IF NOT EXISTS engineering_component_logs_lookup
|
|
104
|
+
ON engineering_component_logs(component, id DESC);
|
|
105
|
+
""".split(";"):
|
|
106
|
+
if statement.strip():
|
|
107
|
+
connection.execute(statement)
|
|
108
|
+
legacy_tables = {
|
|
109
|
+
str(row[0])
|
|
110
|
+
for row in connection.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
|
111
|
+
}
|
|
112
|
+
copies = (
|
|
113
|
+
("ep_status", "engineering_status", "name,payload,updated_at"),
|
|
114
|
+
("ep_transactions", "engineering_transactions", "run_id,payload,phase,updated_at"),
|
|
115
|
+
("ep_artifacts", "engineering_artifacts", "id,category,run_id,name,content,created_at"),
|
|
116
|
+
("ep_component_logs", "engineering_component_logs", "id,component,payload,created_at"),
|
|
117
|
+
)
|
|
118
|
+
for source, destination, columns in copies:
|
|
119
|
+
if source in legacy_tables:
|
|
120
|
+
connection.execute(f"INSERT OR IGNORE INTO {destination}({columns}) SELECT {columns} FROM {source}")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _schema_v2(connection: sqlite3.Connection) -> None:
|
|
124
|
+
"""Create generic, local-only Execution Host telemetry evidence."""
|
|
125
|
+
for statement in """
|
|
126
|
+
CREATE TABLE IF NOT EXISTS execution_runs (
|
|
127
|
+
run_id TEXT PRIMARY KEY,
|
|
128
|
+
execution_date TEXT NOT NULL,
|
|
129
|
+
arrived_at TEXT NOT NULL,
|
|
130
|
+
execution_started_at TEXT NOT NULL,
|
|
131
|
+
execution_finished_at TEXT NOT NULL,
|
|
132
|
+
queue_wait_seconds REAL NOT NULL,
|
|
133
|
+
execution_seconds REAL,
|
|
134
|
+
terminal_state TEXT NOT NULL,
|
|
135
|
+
input_tokens INTEGER,
|
|
136
|
+
output_tokens INTEGER,
|
|
137
|
+
total_tokens INTEGER,
|
|
138
|
+
execution_mode TEXT NOT NULL,
|
|
139
|
+
workspace TEXT NOT NULL,
|
|
140
|
+
repository TEXT NOT NULL,
|
|
141
|
+
execution_host_version TEXT NOT NULL
|
|
142
|
+
);
|
|
143
|
+
CREATE INDEX IF NOT EXISTS execution_runs_daily_lookup
|
|
144
|
+
ON execution_runs(execution_date, terminal_state);
|
|
145
|
+
CREATE TABLE IF NOT EXISTS daily_execution_statistics (
|
|
146
|
+
execution_date TEXT NOT NULL,
|
|
147
|
+
workspace TEXT NOT NULL,
|
|
148
|
+
repository TEXT NOT NULL,
|
|
149
|
+
execution_mode TEXT NOT NULL,
|
|
150
|
+
prompt_count INTEGER NOT NULL,
|
|
151
|
+
complete_count INTEGER NOT NULL,
|
|
152
|
+
blocked_count INTEGER NOT NULL,
|
|
153
|
+
failed_count INTEGER NOT NULL,
|
|
154
|
+
average_execution_seconds REAL,
|
|
155
|
+
average_queue_wait_seconds REAL,
|
|
156
|
+
input_tokens INTEGER,
|
|
157
|
+
output_tokens INTEGER,
|
|
158
|
+
total_tokens INTEGER,
|
|
159
|
+
PRIMARY KEY(execution_date, workspace, repository, execution_mode)
|
|
160
|
+
);
|
|
161
|
+
CREATE INDEX IF NOT EXISTS daily_execution_statistics_date_lookup
|
|
162
|
+
ON daily_execution_statistics(execution_date);
|
|
163
|
+
""".split(";"):
|
|
164
|
+
if statement.strip():
|
|
165
|
+
connection.execute(statement)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _schema_v3(connection: sqlite3.Connection) -> None:
|
|
169
|
+
"""Add total Execution Host elapsed time without changing run authority."""
|
|
170
|
+
connection.execute("ALTER TABLE execution_runs ADD COLUMN total_execution_seconds REAL")
|
|
171
|
+
connection.execute(
|
|
172
|
+
"ALTER TABLE daily_execution_statistics ADD COLUMN average_total_execution_seconds REAL"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _schema_v4(connection: sqlite3.Connection) -> None:
|
|
177
|
+
"""Retired migration marker for legacy local component logs.
|
|
178
|
+
|
|
179
|
+
The old importer made ``dashboard`` and ``inbox`` writable component
|
|
180
|
+
identities in the CENTRAL store. Those aliases have no supported
|
|
181
|
+
consumer, so historical files are now forensic input only and are never
|
|
182
|
+
imported into operational component logging.
|
|
183
|
+
"""
|
|
184
|
+
del connection
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _schema_v5(connection: sqlite3.Connection) -> None:
|
|
188
|
+
"""Create the canonical, private index of terminal prompt executions."""
|
|
189
|
+
connection.execute(
|
|
190
|
+
"""
|
|
191
|
+
CREATE TABLE IF NOT EXISTS prompt_execution_history (
|
|
192
|
+
run_id TEXT PRIMARY KEY,
|
|
193
|
+
terminal_state TEXT NOT NULL,
|
|
194
|
+
prompt_title TEXT NOT NULL,
|
|
195
|
+
executed_at TEXT NOT NULL,
|
|
196
|
+
git_commit TEXT,
|
|
197
|
+
report_path TEXT,
|
|
198
|
+
updated_at TEXT NOT NULL
|
|
199
|
+
)
|
|
200
|
+
"""
|
|
201
|
+
)
|
|
202
|
+
connection.execute(
|
|
203
|
+
"CREATE INDEX IF NOT EXISTS prompt_execution_history_executed_lookup "
|
|
204
|
+
"ON prompt_execution_history(executed_at DESC, run_id DESC)"
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _schema_v6(connection: sqlite3.Connection) -> None:
|
|
209
|
+
"""Persist immutable retry lineage separately from the original run."""
|
|
210
|
+
for statement in (
|
|
211
|
+
"ALTER TABLE prompt_execution_history ADD COLUMN retry_of TEXT",
|
|
212
|
+
"ALTER TABLE prompt_execution_history ADD COLUMN original_run_id TEXT",
|
|
213
|
+
"ALTER TABLE prompt_execution_history ADD COLUMN retry_generation INTEGER",
|
|
214
|
+
"ALTER TABLE prompt_execution_history ADD COLUMN retry_timestamp TEXT",
|
|
215
|
+
"ALTER TABLE execution_runs ADD COLUMN retry_of TEXT",
|
|
216
|
+
"ALTER TABLE execution_runs ADD COLUMN original_run_id TEXT",
|
|
217
|
+
"ALTER TABLE execution_runs ADD COLUMN retry_generation INTEGER",
|
|
218
|
+
"ALTER TABLE execution_runs ADD COLUMN retry_timestamp TEXT",
|
|
219
|
+
):
|
|
220
|
+
connection.execute(statement)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _schema_v7(connection: sqlite3.Connection) -> None:
|
|
224
|
+
"""Record the safe runtime signature needed for comparable duration estimates."""
|
|
225
|
+
for statement in (
|
|
226
|
+
"ALTER TABLE execution_runs ADD COLUMN prompt_characters INTEGER",
|
|
227
|
+
"ALTER TABLE execution_runs ADD COLUMN runtime_provider TEXT",
|
|
228
|
+
"ALTER TABLE execution_runs ADD COLUMN runtime_model TEXT",
|
|
229
|
+
"ALTER TABLE execution_runs ADD COLUMN reasoning_profile TEXT",
|
|
230
|
+
"ALTER TABLE execution_runs ADD COLUMN configuration_profile TEXT",
|
|
231
|
+
):
|
|
232
|
+
connection.execute(statement)
|
|
233
|
+
connection.execute(
|
|
234
|
+
"CREATE INDEX IF NOT EXISTS execution_runs_duration_profile_lookup "
|
|
235
|
+
"ON execution_runs(terminal_state, runtime_provider, runtime_model, "
|
|
236
|
+
"reasoning_profile, configuration_profile)"
|
|
237
|
+
)
|
|
238
|
+
# Existing immutable reports already contain safe runtime provenance and
|
|
239
|
+
# the submitted objective. Extract only the profile and its character
|
|
240
|
+
# count so the new estimate can learn from prior local evidence without
|
|
241
|
+
# copying prompt content into telemetry.
|
|
242
|
+
database = Path(connection.execute("PRAGMA database_list").fetchone()[2])
|
|
243
|
+
reports_root = database.parent / "reports"
|
|
244
|
+
rows = connection.execute(
|
|
245
|
+
"""
|
|
246
|
+
SELECT runs.run_id, history.report_path
|
|
247
|
+
FROM execution_runs AS runs
|
|
248
|
+
JOIN prompt_execution_history AS history ON history.run_id = runs.run_id
|
|
249
|
+
WHERE history.report_path IS NOT NULL
|
|
250
|
+
"""
|
|
251
|
+
).fetchall()
|
|
252
|
+
labels = {
|
|
253
|
+
"runtime_provider": "Runtime Provider",
|
|
254
|
+
"runtime_model": "AI Model",
|
|
255
|
+
"reasoning_profile": "Reasoning Profile",
|
|
256
|
+
"configuration_profile": "Configuration Profile",
|
|
257
|
+
}
|
|
258
|
+
for run_id, relative_path in rows:
|
|
259
|
+
if not isinstance(run_id, str) or not isinstance(relative_path, str):
|
|
260
|
+
continue
|
|
261
|
+
try:
|
|
262
|
+
report = (reports_root / relative_path).resolve()
|
|
263
|
+
report.relative_to(reports_root.resolve())
|
|
264
|
+
text = report.read_text(encoding="utf-8")
|
|
265
|
+
except (OSError, ValueError):
|
|
266
|
+
continue
|
|
267
|
+
values: dict[str, str | None] = {}
|
|
268
|
+
for key, label in labels.items():
|
|
269
|
+
match = re.search(rf"^- {re.escape(label)}: `([^`\n]{{1,120}})`$", text, re.MULTILINE)
|
|
270
|
+
value = match.group(1).strip() if match else ""
|
|
271
|
+
values[key] = value if value and value.casefold() not in {"not reported", "unavailable"} else None
|
|
272
|
+
prompt_size = re.search(r"^- Submitted Prompt Characters: `(\d+)`$", text, re.MULTILINE)
|
|
273
|
+
if prompt_size is not None:
|
|
274
|
+
prompt_characters = int(prompt_size.group(1))
|
|
275
|
+
else:
|
|
276
|
+
# Compatibility for immutable reports written before submitted
|
|
277
|
+
# prompt text was removed from the report header.
|
|
278
|
+
objective = re.search(
|
|
279
|
+
r"^- Objective: (.*?)(?=\n\n## )", text, re.MULTILINE | re.DOTALL
|
|
280
|
+
)
|
|
281
|
+
prompt_characters = len(objective.group(1)) if objective else None
|
|
282
|
+
connection.execute(
|
|
283
|
+
"""
|
|
284
|
+
UPDATE execution_runs SET prompt_characters=?, runtime_provider=?, runtime_model=?,
|
|
285
|
+
reasoning_profile=?, configuration_profile=? WHERE run_id=?
|
|
286
|
+
""",
|
|
287
|
+
(
|
|
288
|
+
prompt_characters,
|
|
289
|
+
values["runtime_provider"],
|
|
290
|
+
values["runtime_model"],
|
|
291
|
+
values["reasoning_profile"],
|
|
292
|
+
values["configuration_profile"],
|
|
293
|
+
run_id,
|
|
294
|
+
),
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def _schema_v8(connection: sqlite3.Connection) -> None:
|
|
299
|
+
"""Preserve target-workspace facts with each terminal execution."""
|
|
300
|
+
for statement in (
|
|
301
|
+
"ALTER TABLE prompt_execution_history ADD COLUMN target_checkout_path TEXT",
|
|
302
|
+
"ALTER TABLE prompt_execution_history ADD COLUMN tracked_file_count INTEGER",
|
|
303
|
+
):
|
|
304
|
+
connection.execute(statement)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def _schema_v9(connection: sqlite3.Connection) -> None:
|
|
308
|
+
"""Persist producer-neutral provenance and immutable execution receipts."""
|
|
309
|
+
for statement in (
|
|
310
|
+
"ALTER TABLE execution_runs ADD COLUMN producer_id TEXT NOT NULL DEFAULT 'legacy'",
|
|
311
|
+
"ALTER TABLE execution_runs ADD COLUMN producer_type TEXT NOT NULL DEFAULT 'HUMAN'",
|
|
312
|
+
"ALTER TABLE execution_runs ADD COLUMN producer_version TEXT",
|
|
313
|
+
"ALTER TABLE execution_runs ADD COLUMN correlation_id TEXT",
|
|
314
|
+
"ALTER TABLE execution_runs ADD COLUMN mission_id TEXT",
|
|
315
|
+
"ALTER TABLE execution_runs ADD COLUMN engineering_action_id TEXT",
|
|
316
|
+
"ALTER TABLE execution_runs ADD COLUMN execution_constraint_version TEXT",
|
|
317
|
+
"CREATE INDEX IF NOT EXISTS execution_runs_producer_lookup ON execution_runs(producer_type, producer_id)",
|
|
318
|
+
"""CREATE TABLE IF NOT EXISTS execution_receipts (
|
|
319
|
+
run_id TEXT PRIMARY KEY,
|
|
320
|
+
producer_id TEXT NOT NULL,
|
|
321
|
+
producer_type TEXT NOT NULL,
|
|
322
|
+
producer_version TEXT,
|
|
323
|
+
mission_id TEXT,
|
|
324
|
+
engineering_action_id TEXT,
|
|
325
|
+
correlation_id TEXT,
|
|
326
|
+
execution_constraint_version TEXT,
|
|
327
|
+
execution_host TEXT NOT NULL,
|
|
328
|
+
execution_host_version TEXT NOT NULL,
|
|
329
|
+
receipt_timestamp TEXT NOT NULL,
|
|
330
|
+
execution_outcome TEXT NOT NULL
|
|
331
|
+
)""",
|
|
332
|
+
):
|
|
333
|
+
connection.execute(statement)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def _schema_v10(connection: sqlite3.Connection) -> None:
|
|
337
|
+
"""Preserve the target branch observed when terminal evidence is written."""
|
|
338
|
+
connection.execute("ALTER TABLE prompt_execution_history ADD COLUMN target_branch TEXT")
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def _schema_v11(connection: sqlite3.Connection) -> None:
|
|
342
|
+
"""Add the canonical host records; files retain payloads and projections only."""
|
|
343
|
+
for statement in """
|
|
344
|
+
CREATE TABLE execution_submissions (
|
|
345
|
+
submission_id TEXT PRIMARY KEY,
|
|
346
|
+
producer_id TEXT NOT NULL,
|
|
347
|
+
producer_type TEXT NOT NULL,
|
|
348
|
+
producer_version TEXT,
|
|
349
|
+
contract_version TEXT,
|
|
350
|
+
prompt_content TEXT NOT NULL,
|
|
351
|
+
prompt_metadata TEXT NOT NULL,
|
|
352
|
+
target_identity TEXT NOT NULL,
|
|
353
|
+
original_envelope TEXT NOT NULL,
|
|
354
|
+
correlation_id TEXT,
|
|
355
|
+
mission_id TEXT,
|
|
356
|
+
execution_run_id TEXT REFERENCES execution_runs(run_id),
|
|
357
|
+
received_at TEXT NOT NULL,
|
|
358
|
+
UNIQUE(producer_id, correlation_id)
|
|
359
|
+
);
|
|
360
|
+
CREATE TABLE execution_lifecycle_events (
|
|
361
|
+
id INTEGER PRIMARY KEY,
|
|
362
|
+
run_id TEXT NOT NULL REFERENCES engineering_transactions(run_id) ON DELETE CASCADE,
|
|
363
|
+
phase TEXT NOT NULL,
|
|
364
|
+
checkpoint TEXT NOT NULL,
|
|
365
|
+
recorded_at TEXT NOT NULL,
|
|
366
|
+
UNIQUE(run_id, phase, checkpoint)
|
|
367
|
+
);
|
|
368
|
+
CREATE INDEX execution_lifecycle_events_run_lookup
|
|
369
|
+
ON execution_lifecycle_events(run_id, id DESC);
|
|
370
|
+
CREATE TABLE execution_artifact_records (
|
|
371
|
+
artifact_id TEXT PRIMARY KEY,
|
|
372
|
+
artifact_type TEXT NOT NULL,
|
|
373
|
+
digest_algorithm TEXT NOT NULL,
|
|
374
|
+
digest TEXT NOT NULL,
|
|
375
|
+
content_type TEXT NOT NULL,
|
|
376
|
+
run_id TEXT REFERENCES execution_runs(run_id),
|
|
377
|
+
submission_id TEXT REFERENCES execution_submissions(submission_id),
|
|
378
|
+
mission_id TEXT,
|
|
379
|
+
execution_id TEXT,
|
|
380
|
+
producer_id TEXT,
|
|
381
|
+
created_at TEXT NOT NULL,
|
|
382
|
+
integrity_status TEXT NOT NULL,
|
|
383
|
+
storage_location TEXT NOT NULL,
|
|
384
|
+
projection_status TEXT NOT NULL,
|
|
385
|
+
UNIQUE(storage_location),
|
|
386
|
+
UNIQUE(digest_algorithm, digest, storage_location)
|
|
387
|
+
);
|
|
388
|
+
CREATE TABLE execution_projections (
|
|
389
|
+
projection_name TEXT PRIMARY KEY,
|
|
390
|
+
classification TEXT NOT NULL CHECK(classification IN ('PROJECTION','ARTIFACT_PAYLOAD','CONFIGURATION','OBSERVABILITY','RECOVERY_EXPORT')),
|
|
391
|
+
payload TEXT NOT NULL,
|
|
392
|
+
source_digest TEXT NOT NULL,
|
|
393
|
+
updated_at TEXT NOT NULL
|
|
394
|
+
);
|
|
395
|
+
CREATE TABLE execution_migration_provenance (
|
|
396
|
+
source_location TEXT PRIMARY KEY,
|
|
397
|
+
source_digest TEXT NOT NULL,
|
|
398
|
+
imported_at TEXT NOT NULL,
|
|
399
|
+
record_kind TEXT NOT NULL
|
|
400
|
+
);
|
|
401
|
+
""".split(";"):
|
|
402
|
+
if statement.strip():
|
|
403
|
+
connection.execute(statement)
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def _legacy_payload(path: Path) -> tuple[dict[str, object], str] | None:
|
|
407
|
+
try:
|
|
408
|
+
raw = path.read_bytes()
|
|
409
|
+
payload = json.loads(raw)
|
|
410
|
+
except (OSError, json.JSONDecodeError):
|
|
411
|
+
return None
|
|
412
|
+
return (payload, hashlib.sha256(raw).hexdigest()) if isinstance(payload, dict) else None
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def _schema_v12(connection: sqlite3.Connection) -> None:
|
|
416
|
+
"""Import legacy state once, preserving it as verified migration provenance.
|
|
417
|
+
|
|
418
|
+
The migration is deliberately one-way. Once a record is present, normal
|
|
419
|
+
runtime reads never rebuild authority from filesystem projections.
|
|
420
|
+
"""
|
|
421
|
+
database = Path(connection.execute("PRAGMA database_list").fetchone()[2])
|
|
422
|
+
workspace = database.parent
|
|
423
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
424
|
+
candidates: list[tuple[Path, str, str]] = [
|
|
425
|
+
(workspace / "status" / "status.json", "watcher_status", "PROJECTION"),
|
|
426
|
+
(workspace / "status" / "current.json", "live_status", "PROJECTION"),
|
|
427
|
+
]
|
|
428
|
+
candidates.extend((path, f"transaction:{path.stem}", "CHECKPOINT") for path in (workspace / "engineering-runs").glob("*.json"))
|
|
429
|
+
for path, name, kind in candidates:
|
|
430
|
+
legacy = _legacy_payload(path)
|
|
431
|
+
if legacy is None:
|
|
432
|
+
continue
|
|
433
|
+
payload, digest = legacy
|
|
434
|
+
prior = connection.execute(
|
|
435
|
+
"SELECT source_digest FROM execution_migration_provenance WHERE source_location=?", (str(path),)
|
|
436
|
+
).fetchone()
|
|
437
|
+
if prior and prior[0] != digest:
|
|
438
|
+
raise EngineeringStorageError("Legacy projection changed during canonical datastore migration.")
|
|
439
|
+
if prior:
|
|
440
|
+
continue
|
|
441
|
+
if kind == "CHECKPOINT":
|
|
442
|
+
run_id = payload.get("run_id")
|
|
443
|
+
phase = payload.get("phase")
|
|
444
|
+
if not isinstance(run_id, str) or not isinstance(phase, str):
|
|
445
|
+
# Keep malformed compatibility files for operator diagnosis;
|
|
446
|
+
# they are never promoted into canonical authority.
|
|
447
|
+
continue
|
|
448
|
+
encoded = json.dumps(payload, separators=(",", ":"), sort_keys=True)
|
|
449
|
+
existing = connection.execute("SELECT payload FROM engineering_transactions WHERE run_id=?", (run_id,)).fetchone()
|
|
450
|
+
if existing and existing[0] != encoded:
|
|
451
|
+
raise EngineeringStorageError("Legacy checkpoint conflicts with canonical transaction state.")
|
|
452
|
+
connection.execute("INSERT OR IGNORE INTO engineering_transactions(run_id,payload,phase,updated_at) VALUES(?,?,?,?)", (run_id, encoded, phase, now))
|
|
453
|
+
connection.execute("INSERT OR IGNORE INTO execution_lifecycle_events(run_id,phase,checkpoint,recorded_at) VALUES(?,?,?,?)", (run_id, phase, encoded, now))
|
|
454
|
+
else:
|
|
455
|
+
store_projection(connection, name, payload, classification="PROJECTION", updated_at=now)
|
|
456
|
+
connection.execute("INSERT INTO execution_migration_provenance(source_location,source_digest,imported_at,record_kind) VALUES(?,?,?,?)", (str(path), digest, now, kind))
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def _schema_v13(connection: sqlite3.Connection) -> None:
|
|
460
|
+
"""Import immutable terminal-report metadata once, never during dashboard reads."""
|
|
461
|
+
from .prompt_history import _report_record, _safe_timestamp
|
|
462
|
+
|
|
463
|
+
database = Path(connection.execute("PRAGMA database_list").fetchone()[2])
|
|
464
|
+
workspace = database.parent
|
|
465
|
+
reports = workspace / "reports"
|
|
466
|
+
if not reports.is_dir():
|
|
467
|
+
return
|
|
468
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
469
|
+
for report in reports.glob("*.md"):
|
|
470
|
+
try:
|
|
471
|
+
digest = hashlib.sha256(report.read_bytes()).hexdigest()
|
|
472
|
+
except OSError:
|
|
473
|
+
continue
|
|
474
|
+
record = _report_record(workspace.parent, report)
|
|
475
|
+
if record is None:
|
|
476
|
+
continue
|
|
477
|
+
source_location = str(report)
|
|
478
|
+
prior = connection.execute(
|
|
479
|
+
"SELECT source_digest FROM execution_migration_provenance WHERE source_location=?", (source_location,)
|
|
480
|
+
).fetchone()
|
|
481
|
+
if prior and prior[0] != digest:
|
|
482
|
+
raise EngineeringStorageError("Legacy report changed during canonical datastore migration.")
|
|
483
|
+
if prior:
|
|
484
|
+
continue
|
|
485
|
+
run_id = record["run_id"]
|
|
486
|
+
connection.execute(
|
|
487
|
+
"INSERT OR IGNORE INTO prompt_execution_history(run_id,terminal_state,prompt_title,executed_at,git_commit,report_path,retry_of,original_run_id,retry_generation,retry_timestamp,target_branch,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)",
|
|
488
|
+
(run_id, record["terminal_state"], record["prompt_title"], _safe_timestamp(record["executed_at"]), record["git_commit"],
|
|
489
|
+
str(report.resolve().relative_to(reports.resolve())), record["retry_of"], record["original_run_id"],
|
|
490
|
+
record["retry_generation"], record["retry_timestamp"], record["target_branch"], now),
|
|
491
|
+
)
|
|
492
|
+
run_exists = connection.execute("SELECT 1 FROM execution_runs WHERE run_id=?", (run_id,)).fetchone()
|
|
493
|
+
connection.execute(
|
|
494
|
+
"INSERT OR IGNORE INTO execution_artifact_records(artifact_id,artifact_type,digest_algorithm,digest,content_type,run_id,created_at,integrity_status,storage_location,projection_status) VALUES(?,?,?,?,?,?,?,?,?,?)",
|
|
495
|
+
(f"legacy-report:{run_id}", "TERMINAL_REPORT", "sha256", digest, "text/markdown", run_id if run_exists else None,
|
|
496
|
+
now, "VERIFIED", str(report.resolve().relative_to(reports.resolve())), "AVAILABLE"),
|
|
497
|
+
)
|
|
498
|
+
connection.execute(
|
|
499
|
+
"INSERT INTO execution_migration_provenance(source_location,source_digest,imported_at,record_kind) VALUES(?,?,?,?)",
|
|
500
|
+
(source_location, digest, now, "ARTIFACT_PAYLOAD"),
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def _schema_v14(connection: sqlite3.Connection) -> None:
|
|
505
|
+
"""Add canonical, bounded ownership leases without assuming old ACTIVE runs live."""
|
|
506
|
+
for statement in """
|
|
507
|
+
CREATE TABLE execution_run_leases (
|
|
508
|
+
lease_id TEXT PRIMARY KEY,
|
|
509
|
+
run_id TEXT NOT NULL REFERENCES engineering_transactions(run_id) ON DELETE CASCADE,
|
|
510
|
+
host_identity TEXT NOT NULL,
|
|
511
|
+
host_instance_id TEXT NOT NULL,
|
|
512
|
+
process_id INTEGER,
|
|
513
|
+
acquired_at TEXT NOT NULL,
|
|
514
|
+
last_heartbeat_at TEXT NOT NULL,
|
|
515
|
+
expires_at TEXT NOT NULL,
|
|
516
|
+
lease_state TEXT NOT NULL CHECK(lease_state IN ('ACTIVE','EXPIRED','RELEASED')),
|
|
517
|
+
lease_version INTEGER NOT NULL,
|
|
518
|
+
created_at TEXT NOT NULL,
|
|
519
|
+
updated_at TEXT NOT NULL
|
|
520
|
+
);
|
|
521
|
+
CREATE UNIQUE INDEX execution_run_one_active_lease
|
|
522
|
+
ON execution_run_leases(run_id) WHERE lease_state='ACTIVE';
|
|
523
|
+
CREATE INDEX execution_run_leases_expiry_lookup
|
|
524
|
+
ON execution_run_leases(lease_state, expires_at);
|
|
525
|
+
CREATE TABLE execution_lease_events (
|
|
526
|
+
id INTEGER PRIMARY KEY,
|
|
527
|
+
lease_id TEXT NOT NULL REFERENCES execution_run_leases(lease_id) ON DELETE CASCADE,
|
|
528
|
+
run_id TEXT NOT NULL,
|
|
529
|
+
event_type TEXT NOT NULL CHECK(event_type IN ('LEASE_ACQUIRED','LEASE_EXPIRED','STALE_DETECTED','STALE_RECONCILED','LEASE_RELEASED')),
|
|
530
|
+
outcome TEXT NOT NULL DEFAULT '',
|
|
531
|
+
recorded_at TEXT NOT NULL,
|
|
532
|
+
UNIQUE(lease_id, event_type, outcome)
|
|
533
|
+
);
|
|
534
|
+
CREATE INDEX execution_lease_events_run_lookup
|
|
535
|
+
ON execution_lease_events(run_id, id DESC);
|
|
536
|
+
CREATE TABLE execution_run_reconciliations (
|
|
537
|
+
run_id TEXT PRIMARY KEY REFERENCES engineering_transactions(run_id) ON DELETE CASCADE,
|
|
538
|
+
outcome TEXT NOT NULL CHECK(outcome IN ('RECOVERABLE','OPERATOR_INTERVENTION_REQUIRED','TERMINAL_EVIDENCE_PRESENT','INCONSISTENT')),
|
|
539
|
+
reason TEXT NOT NULL,
|
|
540
|
+
reconciled_at TEXT NOT NULL,
|
|
541
|
+
updated_at TEXT NOT NULL
|
|
542
|
+
);
|
|
543
|
+
""".split(";"):
|
|
544
|
+
if statement.strip():
|
|
545
|
+
connection.execute(statement)
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
def _schema_v15(connection: sqlite3.Connection) -> None:
|
|
549
|
+
"""Persist typed readiness decisions as canonical run-correlated evidence."""
|
|
550
|
+
connection.execute(
|
|
551
|
+
"CREATE TABLE execution_readiness_evaluations ("
|
|
552
|
+
"run_id TEXT PRIMARY KEY REFERENCES engineering_transactions(run_id) ON DELETE CASCADE,"
|
|
553
|
+
"profile_id TEXT NOT NULL,profile_version INTEGER NOT NULL,execution_mode TEXT NOT NULL,"
|
|
554
|
+
"passed INTEGER NOT NULL CHECK(passed IN (0,1)),failed_requirements TEXT NOT NULL,"
|
|
555
|
+
"facts TEXT NOT NULL,evaluated_at TEXT NOT NULL,diagnostic TEXT)"
|
|
556
|
+
)
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
def _schema_v16(connection: sqlite3.Connection) -> None:
|
|
560
|
+
"""Persist immutable Producer Envelope context snapshots and run linkage."""
|
|
561
|
+
for statement in (
|
|
562
|
+
"ALTER TABLE execution_submissions ADD COLUMN execution_context_snapshot TEXT",
|
|
563
|
+
"ALTER TABLE execution_submissions ADD COLUMN execution_context_version TEXT",
|
|
564
|
+
"CREATE TABLE execution_submission_links (submission_id TEXT PRIMARY KEY REFERENCES execution_submissions(submission_id),run_id TEXT NOT NULL,linked_at TEXT NOT NULL)",
|
|
565
|
+
"CREATE UNIQUE INDEX execution_submission_links_run_lookup ON execution_submission_links(run_id)",
|
|
566
|
+
):
|
|
567
|
+
connection.execute(statement)
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def _schema_v17(connection: sqlite3.Connection) -> None:
|
|
571
|
+
"""Retain the declared Engineering Action provenance with the submission."""
|
|
572
|
+
connection.execute("ALTER TABLE execution_submissions ADD COLUMN engineering_action_id TEXT")
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
def _schema_v18(connection: sqlite3.Connection) -> None:
|
|
576
|
+
"""Persist immutable operator handling separately from terminal outcome."""
|
|
577
|
+
connection.execute(
|
|
578
|
+
"CREATE TABLE execution_dismissals ("
|
|
579
|
+
"run_id TEXT PRIMARY KEY REFERENCES prompt_execution_history(run_id) ON DELETE RESTRICT,"
|
|
580
|
+
"terminal_state TEXT NOT NULL CHECK(terminal_state IN ('COMPLETE','BLOCKED','FAILED')) ,"
|
|
581
|
+
"handling_state TEXT NOT NULL CHECK(handling_state='DISMISSED'),"
|
|
582
|
+
"dismissed_at TEXT NOT NULL,dismissed_by TEXT NOT NULL)"
|
|
583
|
+
)
|
|
584
|
+
connection.execute(
|
|
585
|
+
"CREATE TRIGGER execution_dismissals_immutable_update "
|
|
586
|
+
"BEFORE UPDATE ON execution_dismissals BEGIN "
|
|
587
|
+
"SELECT RAISE(ABORT, 'Execution dismissal evidence is immutable.'); END"
|
|
588
|
+
)
|
|
589
|
+
connection.execute(
|
|
590
|
+
"CREATE TRIGGER execution_dismissals_immutable_delete "
|
|
591
|
+
"BEFORE DELETE ON execution_dismissals BEGIN "
|
|
592
|
+
"SELECT RAISE(ABORT, 'Execution dismissal evidence is immutable.'); END"
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
def _schema_v19(connection: sqlite3.Connection) -> None:
|
|
597
|
+
"""Persist each supplied Forge governance snapshot with its submission."""
|
|
598
|
+
connection.execute("ALTER TABLE execution_submissions ADD COLUMN forge_governance_handoff_snapshot TEXT")
|
|
599
|
+
connection.execute("ALTER TABLE execution_submissions ADD COLUMN forge_governance_handoff_version TEXT")
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def _schema_v20(connection: sqlite3.Connection) -> None:
|
|
603
|
+
"""Persist immutable, run-scoped execution phase spans.
|
|
604
|
+
|
|
605
|
+
Existing executions intentionally receive no synthetic phase records. A
|
|
606
|
+
span is started and completed at an observed runtime boundary; durations
|
|
607
|
+
are measured by the caller's monotonic clock and timestamps remain UTC.
|
|
608
|
+
"""
|
|
609
|
+
for statement in """
|
|
610
|
+
CREATE TABLE execution_phase_spans (
|
|
611
|
+
phase_id TEXT PRIMARY KEY,
|
|
612
|
+
run_id TEXT NOT NULL,
|
|
613
|
+
phase_name TEXT NOT NULL,
|
|
614
|
+
phase_category TEXT NOT NULL,
|
|
615
|
+
parent_phase_id TEXT,
|
|
616
|
+
attempt INTEGER NOT NULL DEFAULT 1 CHECK(attempt >= 1),
|
|
617
|
+
ordinal INTEGER NOT NULL DEFAULT 1 CHECK(ordinal >= 1),
|
|
618
|
+
started_at TEXT NOT NULL,
|
|
619
|
+
completed_at TEXT,
|
|
620
|
+
duration_ms INTEGER,
|
|
621
|
+
outcome TEXT NOT NULL CHECK(outcome IN ('ACTIVE','COMPLETE','FAILED','INTERRUPTED','STALE')),
|
|
622
|
+
metadata TEXT NOT NULL DEFAULT '{}',
|
|
623
|
+
CHECK((completed_at IS NULL AND duration_ms IS NULL AND outcome='ACTIVE')
|
|
624
|
+
OR (completed_at IS NOT NULL AND duration_ms IS NOT NULL AND outcome != 'ACTIVE')),
|
|
625
|
+
UNIQUE(run_id, ordinal)
|
|
626
|
+
);
|
|
627
|
+
CREATE INDEX execution_phase_spans_run_lookup
|
|
628
|
+
ON execution_phase_spans(run_id, ordinal);
|
|
629
|
+
CREATE INDEX execution_phase_spans_parent_lookup
|
|
630
|
+
ON execution_phase_spans(run_id, parent_phase_id, ordinal);
|
|
631
|
+
""".split(";"):
|
|
632
|
+
if statement.strip():
|
|
633
|
+
connection.execute(statement)
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
def _schema_v21(connection: sqlite3.Connection) -> None:
|
|
637
|
+
"""Persist safe aggregate execution counters with terminal evidence."""
|
|
638
|
+
for statement in (
|
|
639
|
+
"ALTER TABLE prompt_execution_history ADD COLUMN execution_metadata TEXT NOT NULL DEFAULT '{}'",
|
|
640
|
+
"ALTER TABLE execution_runs ADD COLUMN execution_metadata TEXT NOT NULL DEFAULT '{}'",
|
|
641
|
+
):
|
|
642
|
+
connection.execute(statement)
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
def _schema_v22(connection: sqlite3.Connection) -> None:
|
|
646
|
+
"""Append immutable provider-invocation evidence and bounded churn counters."""
|
|
647
|
+
connection.execute(
|
|
648
|
+
"""CREATE TABLE provider_invocations (
|
|
649
|
+
invocation_id TEXT PRIMARY KEY, run_id TEXT NOT NULL, ordinal INTEGER NOT NULL,
|
|
650
|
+
provider TEXT NOT NULL, model TEXT, phase TEXT NOT NULL, role TEXT NOT NULL,
|
|
651
|
+
started_at TEXT NOT NULL, completed_at TEXT, duration_ms INTEGER,
|
|
652
|
+
input_tokens INTEGER, cached_input_tokens INTEGER, uncached_input_tokens INTEGER,
|
|
653
|
+
output_tokens INTEGER, reasoning_tokens INTEGER, total_tokens INTEGER,
|
|
654
|
+
usage_authority TEXT NOT NULL CHECK(usage_authority IN ('AUTHORITATIVE','DERIVED','UNAVAILABLE')),
|
|
655
|
+
speed_state TEXT NOT NULL CHECK(speed_state IN ('FAST','NORMAL_DEFAULT','OTHER','UNKNOWN')),
|
|
656
|
+
retry_ordinal INTEGER NOT NULL DEFAULT 0, estimated_credits REAL, estimated_eur REAL,
|
|
657
|
+
rate_table_version TEXT NOT NULL, churn TEXT NOT NULL DEFAULT '{}', UNIQUE(run_id, ordinal)
|
|
658
|
+
)"""
|
|
659
|
+
)
|
|
660
|
+
connection.execute("CREATE INDEX provider_invocations_run_lookup ON provider_invocations(run_id, ordinal)")
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
def _schema_v23(connection: sqlite3.Connection) -> None:
|
|
664
|
+
"""Add explicit model provenance without modifying historical evidence."""
|
|
665
|
+
connection.execute(
|
|
666
|
+
"ALTER TABLE provider_invocations ADD COLUMN model_authority TEXT NOT NULL DEFAULT 'UNAVAILABLE' "
|
|
667
|
+
"CHECK(model_authority IN ('AUTHORITATIVE','DERIVED','UNAVAILABLE'))"
|
|
668
|
+
)
|
|
669
|
+
connection.execute("ALTER TABLE provider_invocations ADD COLUMN raw_provider_model TEXT")
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
def _schema_v24(connection: sqlite3.Connection) -> None:
|
|
673
|
+
"""Keep bounded, counter-only Codex usage snapshots per invocation."""
|
|
674
|
+
connection.execute(
|
|
675
|
+
"""CREATE TABLE provider_usage_snapshots (
|
|
676
|
+
invocation_id TEXT NOT NULL, ordinal INTEGER NOT NULL,
|
|
677
|
+
input_tokens INTEGER, cached_input_tokens INTEGER, uncached_input_tokens INTEGER,
|
|
678
|
+
output_tokens INTEGER,
|
|
679
|
+
reasoning_tokens INTEGER, total_tokens INTEGER,
|
|
680
|
+
input_delta INTEGER, cached_input_delta INTEGER, uncached_input_delta INTEGER,
|
|
681
|
+
output_delta INTEGER,
|
|
682
|
+
PRIMARY KEY(invocation_id, ordinal),
|
|
683
|
+
FOREIGN KEY(invocation_id) REFERENCES provider_invocations(invocation_id)
|
|
684
|
+
)"""
|
|
685
|
+
)
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
def _schema_v25(connection: sqlite3.Connection) -> None:
|
|
689
|
+
"""Repair early v24 snapshot tables missing uncached-token counters.
|
|
690
|
+
|
|
691
|
+
Some live databases recorded migration 24 while carrying the initial
|
|
692
|
+
provider-usage table shape. Keep this migration additive and inspect the
|
|
693
|
+
physical table so both those databases and clean v24 installations migrate
|
|
694
|
+
safely.
|
|
695
|
+
"""
|
|
696
|
+
columns = {
|
|
697
|
+
str(row[1])
|
|
698
|
+
for row in connection.execute("PRAGMA table_info(provider_usage_snapshots)")
|
|
699
|
+
}
|
|
700
|
+
for name in ("uncached_input_tokens", "uncached_input_delta"):
|
|
701
|
+
if name not in columns:
|
|
702
|
+
connection.execute(
|
|
703
|
+
f"ALTER TABLE provider_usage_snapshots ADD COLUMN {name} INTEGER"
|
|
704
|
+
)
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
def _schema_v26(connection: sqlite3.Connection) -> None:
|
|
708
|
+
"""Append bounded Managed-autonomy evidence without lifecycle authority."""
|
|
709
|
+
for statement in """
|
|
710
|
+
CREATE TABLE IF NOT EXISTS managed_autonomy_actions (
|
|
711
|
+
id INTEGER PRIMARY KEY, run_id TEXT NOT NULL, action TEXT NOT NULL,
|
|
712
|
+
authority TEXT NOT NULL CHECK(authority IN ('AUTONOMOUS_EP_ACTION','EXPECTED_OPERATOR_GATE','EXTERNAL_PLATFORM_EVENT','UNPLANNED_MANUAL_INTERVENTION','UNKNOWN_AUTHORITY')),
|
|
713
|
+
actor TEXT NOT NULL, evidence_ref TEXT NOT NULL, observed_at TEXT NOT NULL
|
|
714
|
+
);
|
|
715
|
+
CREATE INDEX IF NOT EXISTS managed_autonomy_actions_run_lookup ON managed_autonomy_actions(run_id,id);
|
|
716
|
+
CREATE TABLE IF NOT EXISTS managed_governance_gates (
|
|
717
|
+
run_id TEXT NOT NULL, gate_type TEXT NOT NULL CHECK(gate_type IN ('IMPLEMENTATION_MERGE_APPROVAL','FINALIZATION_MERGE_APPROVAL')),
|
|
718
|
+
gate_authority TEXT NOT NULL, status TEXT NOT NULL CHECK(status IN ('NOT_REQUIRED','WAITING','SATISFIED','UNAVAILABLE')),
|
|
719
|
+
requested_at TEXT NOT NULL, resolved_at TEXT, resolution_actor TEXT, related_pr INTEGER, phase TEXT NOT NULL,
|
|
720
|
+
PRIMARY KEY(run_id,gate_type)
|
|
721
|
+
);
|
|
722
|
+
CREATE TABLE IF NOT EXISTS managed_validation_observations (
|
|
723
|
+
id INTEGER PRIMARY KEY, run_id TEXT NOT NULL, control TEXT NOT NULL,
|
|
724
|
+
state TEXT NOT NULL CHECK(state IN ('PASS','FAIL','NOT_EXECUTED','NOT_APPLICABLE','UNAVAILABLE','WAITING')),
|
|
725
|
+
required INTEGER NOT NULL CHECK(required IN (0,1)), currentness INTEGER NOT NULL, observed_at TEXT NOT NULL
|
|
726
|
+
);
|
|
727
|
+
CREATE INDEX IF NOT EXISTS managed_validation_observations_run_lookup ON managed_validation_observations(run_id,control,id);
|
|
728
|
+
""".split(";"):
|
|
729
|
+
if statement.strip():
|
|
730
|
+
connection.execute(statement)
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
def _schema_v27(connection: sqlite3.Connection) -> None:
|
|
734
|
+
"""Append terminal PR-check observations used by Managed report projections."""
|
|
735
|
+
for statement in """
|
|
736
|
+
CREATE TABLE IF NOT EXISTS managed_pr_check_observations (
|
|
737
|
+
id INTEGER PRIMARY KEY, run_id TEXT NOT NULL, pr_number INTEGER NOT NULL,
|
|
738
|
+
pr_role TEXT NOT NULL CHECK(pr_role IN ('IMPLEMENTATION','FINALIZATION')),
|
|
739
|
+
pr_state TEXT NOT NULL, merge_state TEXT NOT NULL,
|
|
740
|
+
merge_commit TEXT, required_checks_state TEXT NOT NULL
|
|
741
|
+
CHECK(required_checks_state IN ('PASS','FAIL','WAITING','UNAVAILABLE')),
|
|
742
|
+
evidence_ref TEXT NOT NULL, observed_at TEXT NOT NULL, currentness INTEGER NOT NULL
|
|
743
|
+
);
|
|
744
|
+
CREATE INDEX IF NOT EXISTS managed_pr_check_observations_run_lookup
|
|
745
|
+
ON managed_pr_check_observations(run_id,pr_role,currentness,id);
|
|
746
|
+
""".split(";"):
|
|
747
|
+
if statement.strip():
|
|
748
|
+
connection.execute(statement)
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
def _schema_v28(connection: sqlite3.Connection) -> None:
|
|
752
|
+
"""Keep immutable evidence for an operator-approved emergency rollback."""
|
|
753
|
+
connection.execute(
|
|
754
|
+
"CREATE TABLE IF NOT EXISTS execution_emergency_recoveries ("
|
|
755
|
+
"run_id TEXT PRIMARY KEY REFERENCES prompt_execution_history(run_id) ON DELETE RESTRICT,"
|
|
756
|
+
"cancelled_at TEXT NOT NULL,rolled_back INTEGER NOT NULL CHECK(rolled_back IN (0,1)),"
|
|
757
|
+
"removed_branch TEXT,recorded_by TEXT NOT NULL)"
|
|
758
|
+
)
|
|
759
|
+
connection.execute(
|
|
760
|
+
"CREATE TRIGGER IF NOT EXISTS execution_emergency_recoveries_immutable_update "
|
|
761
|
+
"BEFORE UPDATE ON execution_emergency_recoveries BEGIN "
|
|
762
|
+
"SELECT RAISE(ABORT, 'Emergency recovery evidence is immutable.'); END"
|
|
763
|
+
)
|
|
764
|
+
connection.execute(
|
|
765
|
+
"CREATE TRIGGER IF NOT EXISTS execution_emergency_recoveries_immutable_delete "
|
|
766
|
+
"BEFORE DELETE ON execution_emergency_recoveries BEGIN "
|
|
767
|
+
"SELECT RAISE(ABORT, 'Emergency recovery evidence is immutable.'); END"
|
|
768
|
+
)
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
def _schema_v29(connection: sqlite3.Connection) -> None:
|
|
772
|
+
"""Retain append-only Dependabot Inbox-admission evidence before execution."""
|
|
773
|
+
connection.execute(
|
|
774
|
+
"CREATE TABLE IF NOT EXISTS dependabot_admission_events ("
|
|
775
|
+
"id INTEGER PRIMARY KEY,repository TEXT NOT NULL,pull_request INTEGER NOT NULL,"
|
|
776
|
+
"head_sha TEXT NOT NULL,head_branch TEXT NOT NULL,submission_id TEXT NOT NULL,"
|
|
777
|
+
"event_type TEXT NOT NULL CHECK(event_type IN ('ENQUEUED')),observed_at TEXT NOT NULL,"
|
|
778
|
+
"UNIQUE(repository,pull_request,event_type))"
|
|
779
|
+
)
|
|
780
|
+
connection.execute(
|
|
781
|
+
"CREATE TRIGGER IF NOT EXISTS dependabot_admission_events_immutable_update "
|
|
782
|
+
"BEFORE UPDATE ON dependabot_admission_events BEGIN "
|
|
783
|
+
"SELECT RAISE(ABORT, 'Dependabot admission evidence is immutable.'); END"
|
|
784
|
+
)
|
|
785
|
+
connection.execute(
|
|
786
|
+
"CREATE TRIGGER IF NOT EXISTS dependabot_admission_events_immutable_delete "
|
|
787
|
+
"BEFORE DELETE ON dependabot_admission_events BEGIN "
|
|
788
|
+
"SELECT RAISE(ABORT, 'Dependabot admission evidence is immutable.'); END"
|
|
789
|
+
)
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
def _schema_v30(connection: sqlite3.Connection) -> None:
|
|
793
|
+
"""Durably stage terminal telemetry before its rebuildable projection.
|
|
794
|
+
|
|
795
|
+
The outbox is deliberately independent of lifecycle authority. A terminal
|
|
796
|
+
checkpoint remains authoritative; this table only makes its operational
|
|
797
|
+
telemetry projection recoverable after a watcher or process loss.
|
|
798
|
+
"""
|
|
799
|
+
connection.execute(
|
|
800
|
+
"CREATE TABLE IF NOT EXISTS terminal_telemetry_outbox ("
|
|
801
|
+
"run_id TEXT PRIMARY KEY, payload TEXT NOT NULL, source TEXT NOT NULL "
|
|
802
|
+
"CHECK(source IN ('LIVE_TERMINAL','RECOVERY','BACKFILL')),"
|
|
803
|
+
"state TEXT NOT NULL CHECK(state IN ('PENDING','PROCESSED','FAILED_RETRYABLE')) "
|
|
804
|
+
"DEFAULT 'PENDING', attempt_count INTEGER NOT NULL DEFAULT 0, last_error TEXT,"
|
|
805
|
+
"created_at TEXT NOT NULL, processed_at TEXT)"
|
|
806
|
+
)
|
|
807
|
+
connection.execute(
|
|
808
|
+
"CREATE INDEX IF NOT EXISTS terminal_telemetry_outbox_pending_lookup "
|
|
809
|
+
"ON terminal_telemetry_outbox(state,created_at,run_id)"
|
|
810
|
+
)
|
|
811
|
+
connection.execute(
|
|
812
|
+
"CREATE TRIGGER IF NOT EXISTS terminal_telemetry_outbox_payload_immutable "
|
|
813
|
+
"BEFORE UPDATE OF run_id,payload,source,created_at ON terminal_telemetry_outbox BEGIN "
|
|
814
|
+
"SELECT RAISE(ABORT, 'Terminal telemetry intent is immutable.'); END"
|
|
815
|
+
)
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
def _schema_v31(connection: sqlite3.Connection) -> None:
|
|
819
|
+
"""Keep immutable, operator-invoked PR-evidence backfill decisions.
|
|
820
|
+
|
|
821
|
+
A backfill can amend a legacy checkpoint only after live GitHub evidence
|
|
822
|
+
has been verified. This table retains both applied and deliberately
|
|
823
|
+
skipped decisions, so historical state never gains an unexplained PR.
|
|
824
|
+
"""
|
|
825
|
+
connection.execute(
|
|
826
|
+
"CREATE TABLE IF NOT EXISTS execution_pr_evidence_backfills ("
|
|
827
|
+
"id INTEGER PRIMARY KEY,run_id TEXT NOT NULL REFERENCES engineering_transactions(run_id) ON DELETE RESTRICT,"
|
|
828
|
+
"pr_role TEXT NOT NULL CHECK(pr_role IN ('IMPLEMENTATION','FINALIZATION')),"
|
|
829
|
+
"outcome TEXT NOT NULL CHECK(outcome IN ('APPLIED','SKIPPED')),"
|
|
830
|
+
"reason TEXT NOT NULL,pr_number INTEGER,expected_branch TEXT,expected_merge_commit TEXT,"
|
|
831
|
+
"observed_at TEXT NOT NULL,actor TEXT NOT NULL,"
|
|
832
|
+
"CHECK((outcome='APPLIED' AND pr_number IS NOT NULL) OR outcome='SKIPPED')"
|
|
833
|
+
")"
|
|
834
|
+
)
|
|
835
|
+
connection.execute(
|
|
836
|
+
"CREATE INDEX IF NOT EXISTS execution_pr_evidence_backfills_run_lookup "
|
|
837
|
+
"ON execution_pr_evidence_backfills(run_id,id)"
|
|
838
|
+
)
|
|
839
|
+
connection.execute(
|
|
840
|
+
"CREATE TRIGGER IF NOT EXISTS execution_pr_evidence_backfills_immutable_update "
|
|
841
|
+
"BEFORE UPDATE ON execution_pr_evidence_backfills BEGIN "
|
|
842
|
+
"SELECT RAISE(ABORT, 'Pull-request evidence backfill audit is immutable.'); END"
|
|
843
|
+
)
|
|
844
|
+
connection.execute(
|
|
845
|
+
"CREATE TRIGGER IF NOT EXISTS execution_pr_evidence_backfills_immutable_delete "
|
|
846
|
+
"BEFORE DELETE ON execution_pr_evidence_backfills BEGIN "
|
|
847
|
+
"SELECT RAISE(ABORT, 'Pull-request evidence backfill audit is immutable.'); END"
|
|
848
|
+
)
|
|
849
|
+
|
|
850
|
+
|
|
851
|
+
def _schema_v32(connection: sqlite3.Connection) -> None:
|
|
852
|
+
"""Persist immutable, provider-free admission decisions for every candidate run."""
|
|
853
|
+
connection.execute(
|
|
854
|
+
"CREATE TABLE IF NOT EXISTS execution_admission_decisions ("
|
|
855
|
+
"run_id TEXT PRIMARY KEY,submission_id TEXT NOT NULL REFERENCES execution_submissions(submission_id),"
|
|
856
|
+
"execution_mode TEXT NOT NULL,decision TEXT NOT NULL CHECK(decision IN ('PASS','FAIL')),"
|
|
857
|
+
"failed_gate_ids TEXT NOT NULL,evidence TEXT NOT NULL,observed_at TEXT NOT NULL)"
|
|
858
|
+
)
|
|
859
|
+
for operation in ("UPDATE", "DELETE"):
|
|
860
|
+
connection.execute(
|
|
861
|
+
f"CREATE TRIGGER IF NOT EXISTS execution_admission_decisions_immutable_{operation.casefold()} "
|
|
862
|
+
f"BEFORE {operation} ON execution_admission_decisions BEGIN "
|
|
863
|
+
"SELECT RAISE(ABORT, 'Admission decision evidence is immutable.'); END"
|
|
864
|
+
)
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
def _schema_v33(connection: sqlite3.Connection) -> None:
|
|
868
|
+
"""Persist immutable qualification lineage and resolved validation policy.
|
|
869
|
+
|
|
870
|
+
This intentionally adds no historical rows. A legacy run therefore stays
|
|
871
|
+
unavailable instead of receiving invented fresh-submission or validation
|
|
872
|
+
evidence during schema activation.
|
|
873
|
+
"""
|
|
874
|
+
connection.execute(
|
|
875
|
+
"CREATE TABLE IF NOT EXISTS execution_run_qualification_context ("
|
|
876
|
+
"run_id TEXT PRIMARY KEY,submission_id TEXT NOT NULL,"
|
|
877
|
+
"fresh_submission INTEGER NOT NULL CHECK(fresh_submission IN (0,1)),"
|
|
878
|
+
"retry_parent_run_id TEXT,resume_parent_run_id TEXT,recorded_at TEXT NOT NULL,"
|
|
879
|
+
"CHECK(NOT (retry_parent_run_id IS NOT NULL AND resume_parent_run_id IS NOT NULL)),"
|
|
880
|
+
"CHECK((fresh_submission=1 AND retry_parent_run_id IS NULL AND resume_parent_run_id IS NULL) "
|
|
881
|
+
"OR (fresh_submission=0 AND (retry_parent_run_id IS NOT NULL OR resume_parent_run_id IS NOT NULL)))"
|
|
882
|
+
")"
|
|
883
|
+
)
|
|
884
|
+
connection.execute(
|
|
885
|
+
"CREATE TABLE IF NOT EXISTS execution_validation_profiles ("
|
|
886
|
+
"run_id TEXT PRIMARY KEY,selected_validation_tier TEXT NOT NULL,"
|
|
887
|
+
"validation_profile_version TEXT NOT NULL,required_validation_controls TEXT NOT NULL,"
|
|
888
|
+
"recorded_at TEXT NOT NULL)"
|
|
889
|
+
)
|
|
890
|
+
connection.execute(
|
|
891
|
+
"CREATE TABLE IF NOT EXISTS execution_validation_control_results ("
|
|
892
|
+
"id INTEGER PRIMARY KEY,run_id TEXT NOT NULL,validation_id TEXT NOT NULL,"
|
|
893
|
+
"category TEXT NOT NULL,control_identity TEXT NOT NULL,required_for_profile INTEGER NOT NULL "
|
|
894
|
+
"CHECK(required_for_profile IN (0,1)),execution_status TEXT NOT NULL,"
|
|
895
|
+
"result TEXT NOT NULL,evidence_ref TEXT NOT NULL,observed_at TEXT NOT NULL,currentness INTEGER NOT NULL,"
|
|
896
|
+
"UNIQUE(run_id,validation_id,currentness))"
|
|
897
|
+
)
|
|
898
|
+
for table in (
|
|
899
|
+
"execution_run_qualification_context", "execution_validation_profiles",
|
|
900
|
+
"execution_validation_control_results",
|
|
901
|
+
):
|
|
902
|
+
for operation in ("UPDATE", "DELETE"):
|
|
903
|
+
connection.execute(
|
|
904
|
+
f"CREATE TRIGGER IF NOT EXISTS {table}_immutable_{operation.casefold()} "
|
|
905
|
+
f"BEFORE {operation} ON {table} BEGIN "
|
|
906
|
+
f"SELECT RAISE(ABORT, '{table} evidence is immutable.'); END"
|
|
907
|
+
)
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
def _schema_v34(connection: sqlite3.Connection) -> None:
|
|
911
|
+
"""Store bounded, redacted, run-scoped advisory chat transcripts."""
|
|
912
|
+
connection.execute(
|
|
913
|
+
"CREATE TABLE IF NOT EXISTS execution_chat_messages ("
|
|
914
|
+
"id INTEGER PRIMARY KEY,run_id TEXT NOT NULL REFERENCES prompt_execution_history(run_id),"
|
|
915
|
+
"role TEXT NOT NULL CHECK(role IN ('user','assistant')),content TEXT NOT NULL,"
|
|
916
|
+
"model TEXT,created_at TEXT NOT NULL)"
|
|
917
|
+
)
|
|
918
|
+
connection.execute(
|
|
919
|
+
"CREATE INDEX IF NOT EXISTS execution_chat_messages_run_created "
|
|
920
|
+
"ON execution_chat_messages(run_id,id)"
|
|
921
|
+
)
|
|
922
|
+
connection.execute(
|
|
923
|
+
"CREATE TRIGGER IF NOT EXISTS execution_chat_messages_immutable_update "
|
|
924
|
+
"BEFORE UPDATE ON execution_chat_messages BEGIN "
|
|
925
|
+
"SELECT RAISE(ABORT, 'Chat transcript messages are immutable.'); END"
|
|
926
|
+
)
|
|
927
|
+
|
|
928
|
+
|
|
929
|
+
def _schema_v35(connection: sqlite3.Connection) -> None:
|
|
930
|
+
"""Persist immutable command start and terminal evidence as one lineage."""
|
|
931
|
+
connection.execute(
|
|
932
|
+
"CREATE TABLE IF NOT EXISTS execution_validation_command_invocations ("
|
|
933
|
+
"run_id TEXT NOT NULL,validation_id TEXT NOT NULL,command_id TEXT NOT NULL,"
|
|
934
|
+
"category TEXT NOT NULL,control_identity TEXT NOT NULL,required_for_profile INTEGER NOT NULL,"
|
|
935
|
+
"started_at TEXT NOT NULL,currentness INTEGER NOT NULL,evidence_ref TEXT NOT NULL,"
|
|
936
|
+
"PRIMARY KEY(run_id,command_id))"
|
|
937
|
+
)
|
|
938
|
+
connection.execute(
|
|
939
|
+
"CREATE TABLE IF NOT EXISTS execution_validation_command_terminals ("
|
|
940
|
+
"run_id TEXT NOT NULL,command_id TEXT NOT NULL,completed_at TEXT NOT NULL,"
|
|
941
|
+
"duration_ms INTEGER,exit_code INTEGER,result TEXT NOT NULL,evidence_ref TEXT NOT NULL,"
|
|
942
|
+
"PRIMARY KEY(run_id,command_id),"
|
|
943
|
+
"FOREIGN KEY(run_id,command_id) REFERENCES execution_validation_command_invocations(run_id,command_id))"
|
|
944
|
+
)
|
|
945
|
+
for table in ("execution_validation_command_invocations", "execution_validation_command_terminals"):
|
|
946
|
+
for operation in ("UPDATE", "DELETE"):
|
|
947
|
+
connection.execute(
|
|
948
|
+
f"CREATE TRIGGER IF NOT EXISTS {table}_immutable_{operation.casefold()} "
|
|
949
|
+
f"BEFORE {operation} ON {table} BEGIN "
|
|
950
|
+
f"SELECT RAISE(ABORT, '{table} evidence is immutable.'); END"
|
|
951
|
+
)
|
|
952
|
+
|
|
953
|
+
|
|
954
|
+
def _schema_v36(connection: sqlite3.Connection) -> None:
|
|
955
|
+
"""Persist one immutable, run-bound qualification snapshot prospectively.
|
|
956
|
+
|
|
957
|
+
The snapshot is deliberately a bounded JSON document because its fields
|
|
958
|
+
are a versioned projection of already-authoritative run evidence, not a
|
|
959
|
+
second relational evidence engine. No legacy row is created here.
|
|
960
|
+
"""
|
|
961
|
+
connection.execute(
|
|
962
|
+
"CREATE TABLE IF NOT EXISTS execution_run_qualification_snapshots ("
|
|
963
|
+
"run_id TEXT PRIMARY KEY,snapshot_version INTEGER NOT NULL,"
|
|
964
|
+
"snapshot_id TEXT NOT NULL UNIQUE,required_control_snapshot_ref TEXT NOT NULL,"
|
|
965
|
+
"terminal_checkpoint_ref TEXT NOT NULL,payload TEXT NOT NULL,persisted_at TEXT NOT NULL)"
|
|
966
|
+
)
|
|
967
|
+
for operation in ("UPDATE", "DELETE"):
|
|
968
|
+
connection.execute(
|
|
969
|
+
f"CREATE TRIGGER IF NOT EXISTS execution_run_qualification_snapshots_immutable_{operation.casefold()} "
|
|
970
|
+
f"BEFORE {operation} ON execution_run_qualification_snapshots BEGIN "
|
|
971
|
+
"SELECT RAISE(ABORT, 'Run qualification snapshot is immutable.'); END"
|
|
972
|
+
)
|
|
973
|
+
|
|
974
|
+
|
|
975
|
+
def _schema_v37(connection: sqlite3.Connection) -> None:
|
|
976
|
+
"""Persist the forward-only canonical execution activity summary."""
|
|
977
|
+
connection.execute(
|
|
978
|
+
"CREATE TABLE IF NOT EXISTS execution_activity_summaries ("
|
|
979
|
+
"run_id TEXT PRIMARY KEY,"
|
|
980
|
+
"summary_version INTEGER NOT NULL,payload TEXT NOT NULL,persisted_at TEXT NOT NULL)"
|
|
981
|
+
)
|
|
982
|
+
for operation in ("UPDATE", "DELETE"):
|
|
983
|
+
connection.execute(
|
|
984
|
+
f"CREATE TRIGGER IF NOT EXISTS execution_activity_summaries_immutable_{operation.casefold()} "
|
|
985
|
+
f"BEFORE {operation} ON execution_activity_summaries BEGIN "
|
|
986
|
+
"SELECT RAISE(ABORT, 'Execution activity summary is immutable.'); END"
|
|
987
|
+
)
|
|
988
|
+
# A historical test/store can retain migration 37 while replaying its
|
|
989
|
+
# early v24 shape; preserve v25's additive repair without rewriting rows.
|
|
990
|
+
columns = {str(row[1]) for row in connection.execute("PRAGMA table_info(provider_usage_snapshots)")}
|
|
991
|
+
for name in ("uncached_input_tokens", "uncached_input_delta"):
|
|
992
|
+
if name not in columns:
|
|
993
|
+
connection.execute(f"ALTER TABLE provider_usage_snapshots ADD COLUMN {name} INTEGER")
|
|
994
|
+
|
|
995
|
+
|
|
996
|
+
def _schema_v38(connection: sqlite3.Connection) -> None:
|
|
997
|
+
"""Persist one bounded provider-interruption recovery and its process receipt.
|
|
998
|
+
|
|
999
|
+
Provider invocation telemetry remains immutable and is appended only when
|
|
1000
|
+
an invocation reaches its terminal evidence boundary. These two tables
|
|
1001
|
+
record the preceding launch lifecycle needed to recover safely after the
|
|
1002
|
+
Execution Host itself disappears.
|
|
1003
|
+
"""
|
|
1004
|
+
connection.execute(
|
|
1005
|
+
"CREATE TABLE IF NOT EXISTS provider_recovery_attempts ("
|
|
1006
|
+
"run_id TEXT PRIMARY KEY REFERENCES engineering_transactions(run_id) ON DELETE CASCADE,"
|
|
1007
|
+
"recovery_ordinal INTEGER NOT NULL CHECK(recovery_ordinal=1),"
|
|
1008
|
+
"maximum_attempts INTEGER NOT NULL CHECK(maximum_attempts=1),"
|
|
1009
|
+
"triggering_invocation_id TEXT NOT NULL, replacement_invocation_id TEXT NOT NULL UNIQUE,"
|
|
1010
|
+
"lifecycle_phase TEXT NOT NULL, state TEXT NOT NULL CHECK(state IN ("
|
|
1011
|
+
"'RECOVERY_AVAILABLE','RECOVERY_STARTING','RECOVERY_IN_PROGRESS','RECOVERED',"
|
|
1012
|
+
"'EXHAUSTED','PRECHECK_FAILED','AMBIGUOUS')) ,"
|
|
1013
|
+
"requested_at TEXT NOT NULL, provider_session_id TEXT UNIQUE, launch_claimed_at TEXT, process_receipt_id TEXT,"
|
|
1014
|
+
"process_pid INTEGER, process_group INTEGER, provider_confirmed_active_at TEXT,"
|
|
1015
|
+
"completed_at TEXT, result TEXT, result_evidence_ref TEXT, branch TEXT,"
|
|
1016
|
+
"worktree_identity TEXT, lease_id TEXT, fault_injection_consumed_at TEXT, diagnostic_code TEXT)"
|
|
1017
|
+
)
|
|
1018
|
+
connection.execute(
|
|
1019
|
+
"CREATE TABLE IF NOT EXISTS provider_invocation_receipts ("
|
|
1020
|
+
"receipt_id TEXT PRIMARY KEY, run_id TEXT NOT NULL REFERENCES provider_recovery_attempts(run_id) ON DELETE CASCADE,"
|
|
1021
|
+
"invocation_id TEXT NOT NULL, launch_state TEXT NOT NULL CHECK(launch_state IN ('CLAIMED','PROCESS_STARTED','TERMINAL')),"
|
|
1022
|
+
"provider_session_id TEXT, process_pid INTEGER, process_group INTEGER, process_start_fingerprint TEXT,"
|
|
1023
|
+
"process_executable_identity TEXT, started_at TEXT NOT NULL, completed_at TEXT,"
|
|
1024
|
+
"outcome TEXT, result_evidence_ref TEXT, UNIQUE(run_id, invocation_id, launch_state))"
|
|
1025
|
+
)
|
|
1026
|
+
connection.execute("CREATE INDEX IF NOT EXISTS provider_recovery_state_lookup ON provider_recovery_attempts(state,requested_at)")
|
|
1027
|
+
for table in ("provider_invocation_receipts",):
|
|
1028
|
+
for operation in ("UPDATE", "DELETE"):
|
|
1029
|
+
connection.execute(
|
|
1030
|
+
f"CREATE TRIGGER IF NOT EXISTS {table}_immutable_{operation.casefold()} BEFORE {operation} ON {table} BEGIN "
|
|
1031
|
+
f"SELECT RAISE(ABORT, '{table} evidence is immutable.'); END"
|
|
1032
|
+
)
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
def _schema_v39(connection: sqlite3.Connection) -> None:
|
|
1036
|
+
"""Add verifier-only EP consumer credential authority metadata."""
|
|
1037
|
+
connection.execute(
|
|
1038
|
+
"CREATE TABLE IF NOT EXISTS ep_consumer_credentials ("
|
|
1039
|
+
"credential_id TEXT PRIMARY KEY CHECK(length(credential_id) BETWEEN 1 AND 128),"
|
|
1040
|
+
"consumer_id TEXT NOT NULL CHECK(length(consumer_id) BETWEEN 1 AND 128),"
|
|
1041
|
+
"project_id TEXT NOT NULL CHECK(length(project_id) BETWEEN 1 AND 128),"
|
|
1042
|
+
"verifier BLOB NOT NULL UNIQUE CHECK(length(verifier)=32),"
|
|
1043
|
+
"fingerprint BLOB NOT NULL UNIQUE CHECK(length(fingerprint)=32),"
|
|
1044
|
+
"issued_at TEXT NOT NULL,expires_at TEXT,revoked_at TEXT,"
|
|
1045
|
+
"replaced_by_credential_id TEXT REFERENCES ep_consumer_credentials(credential_id))"
|
|
1046
|
+
)
|
|
1047
|
+
connection.execute(
|
|
1048
|
+
"CREATE INDEX IF NOT EXISTS ep_consumer_credentials_scope_lookup "
|
|
1049
|
+
"ON ep_consumer_credentials(consumer_id,project_id,revoked_at)"
|
|
1050
|
+
)
|
|
1051
|
+
|
|
1052
|
+
|
|
1053
|
+
def _schema_v40(connection: sqlite3.Connection) -> None:
|
|
1054
|
+
"""Add the non-secret EP consumer registration authority."""
|
|
1055
|
+
connection.execute(
|
|
1056
|
+
"CREATE TABLE IF NOT EXISTS ep_consumer_registrations ("
|
|
1057
|
+
"consumer_id TEXT NOT NULL CHECK(length(consumer_id) BETWEEN 1 AND 128),"
|
|
1058
|
+
"project_id TEXT NOT NULL CHECK(length(project_id) BETWEEN 1 AND 128),"
|
|
1059
|
+
"status TEXT NOT NULL CHECK(status IN ('ACTIVE','DISABLED','REVOKED')),"
|
|
1060
|
+
"created_at TEXT NOT NULL,updated_at TEXT NOT NULL,disabled_at TEXT,revoked_at TEXT,"
|
|
1061
|
+
"audit_metadata TEXT NOT NULL DEFAULT '{}',"
|
|
1062
|
+
"PRIMARY KEY(consumer_id,project_id))"
|
|
1063
|
+
)
|
|
1064
|
+
connection.execute(
|
|
1065
|
+
"CREATE INDEX IF NOT EXISTS ep_consumer_registrations_status_lookup "
|
|
1066
|
+
"ON ep_consumer_registrations(consumer_id,project_id,status)"
|
|
1067
|
+
)
|
|
1068
|
+
|
|
1069
|
+
|
|
1070
|
+
_CONSUMER_CREDENTIAL_COLUMNS = (
|
|
1071
|
+
"credential_id", "consumer_id", "project_id", "verifier", "fingerprint",
|
|
1072
|
+
"issued_at", "expires_at", "revoked_at", "replaced_by_credential_id",
|
|
1073
|
+
)
|
|
1074
|
+
_CONSUMER_REGISTRATION_COLUMNS = (
|
|
1075
|
+
"consumer_id", "project_id", "status", "created_at", "updated_at",
|
|
1076
|
+
"disabled_at", "revoked_at", "audit_metadata",
|
|
1077
|
+
)
|
|
1078
|
+
|
|
1079
|
+
|
|
1080
|
+
def _consumer_columns(connection: sqlite3.Connection, table: str) -> tuple[str, ...]:
|
|
1081
|
+
return tuple(str(row[1]) for row in connection.execute(f"PRAGMA table_info({table})"))
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
def _require_consumer_table_shape(
|
|
1085
|
+
connection: sqlite3.Connection, *, credentials: str, registrations: str,
|
|
1086
|
+
) -> None:
|
|
1087
|
+
if _consumer_columns(connection, credentials) != _CONSUMER_CREDENTIAL_COLUMNS:
|
|
1088
|
+
raise EngineeringStorageError("EP consumer credential table shape is invalid.")
|
|
1089
|
+
if _consumer_columns(connection, registrations) != _CONSUMER_REGISTRATION_COLUMNS:
|
|
1090
|
+
raise EngineeringStorageError("EP consumer registration table shape is invalid.")
|
|
1091
|
+
credential_pk = tuple(
|
|
1092
|
+
str(row[1]) for row in connection.execute(f"PRAGMA table_info({credentials})") if int(row[5]) > 0
|
|
1093
|
+
)
|
|
1094
|
+
registration_pk = tuple(
|
|
1095
|
+
str(row[1]) for row in connection.execute(f"PRAGMA table_info({registrations})") if int(row[5]) > 0
|
|
1096
|
+
)
|
|
1097
|
+
if credential_pk != ("credential_id",) or registration_pk != ("consumer_id", "project_id"):
|
|
1098
|
+
raise EngineeringStorageError("EP consumer credential table identity is invalid.")
|
|
1099
|
+
|
|
1100
|
+
|
|
1101
|
+
def _assert_exact_consumer_transfer(
|
|
1102
|
+
connection: sqlite3.Connection, *, source: str, destination: str, columns: tuple[str, ...],
|
|
1103
|
+
) -> None:
|
|
1104
|
+
column_list = ",".join(columns)
|
|
1105
|
+
source_count = int(connection.execute(f"SELECT COUNT(*) FROM {source}").fetchone()[0])
|
|
1106
|
+
destination_count = int(connection.execute(f"SELECT COUNT(*) FROM {destination}").fetchone()[0])
|
|
1107
|
+
if source_count != destination_count:
|
|
1108
|
+
raise EngineeringStorageError("EP consumer credential transfer cardinality is invalid.")
|
|
1109
|
+
missing = connection.execute(
|
|
1110
|
+
f"SELECT {column_list} FROM {source} EXCEPT SELECT {column_list} FROM {destination} LIMIT 1"
|
|
1111
|
+
).fetchone()
|
|
1112
|
+
extra = connection.execute(
|
|
1113
|
+
f"SELECT {column_list} FROM {destination} EXCEPT SELECT {column_list} FROM {source} LIMIT 1"
|
|
1114
|
+
).fetchone()
|
|
1115
|
+
if missing is not None or extra is not None:
|
|
1116
|
+
raise EngineeringStorageError("EP consumer credential transfer identity is invalid.")
|
|
1117
|
+
|
|
1118
|
+
|
|
1119
|
+
def _schema_v41(connection: sqlite3.Connection) -> None:
|
|
1120
|
+
"""Transfer historical credential tables to the neutral authority namespace.
|
|
1121
|
+
|
|
1122
|
+
The storage migration framework encloses this function and metadata version
|
|
1123
|
+
advance in one immediate transaction. Legacy tables are retained untouched
|
|
1124
|
+
only as migration/rollback evidence; no runtime reader falls back to them.
|
|
1125
|
+
"""
|
|
1126
|
+
|
|
1127
|
+
legacy_credentials, legacy_registrations = (
|
|
1128
|
+
"local_api_credentials", "local_api_consumer_registrations",
|
|
1129
|
+
)
|
|
1130
|
+
current_credentials, current_registrations = (
|
|
1131
|
+
"ep_consumer_credentials", "ep_consumer_registrations",
|
|
1132
|
+
)
|
|
1133
|
+
tables = {
|
|
1134
|
+
str(row[0]) for row in connection.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
|
1135
|
+
}
|
|
1136
|
+
legacy = {legacy_credentials, legacy_registrations} & tables
|
|
1137
|
+
current = {current_credentials, current_registrations} & tables
|
|
1138
|
+
if legacy and legacy != {legacy_credentials, legacy_registrations}:
|
|
1139
|
+
raise EngineeringStorageError("EP consumer credential migration source is incomplete.")
|
|
1140
|
+
if current and current != {current_credentials, current_registrations}:
|
|
1141
|
+
raise EngineeringStorageError("EP consumer credential migration destination is incomplete.")
|
|
1142
|
+
if legacy and current:
|
|
1143
|
+
raise EngineeringStorageError("EP consumer credential migration has ambiguous parallel authority.")
|
|
1144
|
+
if legacy:
|
|
1145
|
+
_require_consumer_table_shape(
|
|
1146
|
+
connection, credentials=legacy_credentials, registrations=legacy_registrations,
|
|
1147
|
+
)
|
|
1148
|
+
_schema_v39(connection)
|
|
1149
|
+
_schema_v40(connection)
|
|
1150
|
+
connection.execute(
|
|
1151
|
+
"INSERT INTO ep_consumer_registrations(consumer_id,project_id,status,created_at,updated_at,disabled_at,revoked_at,audit_metadata) "
|
|
1152
|
+
"SELECT consumer_id,project_id,status,created_at,updated_at,disabled_at,revoked_at,audit_metadata "
|
|
1153
|
+
"FROM local_api_consumer_registrations"
|
|
1154
|
+
)
|
|
1155
|
+
connection.execute(
|
|
1156
|
+
"INSERT INTO ep_consumer_credentials(credential_id,consumer_id,project_id,verifier,fingerprint,issued_at,expires_at,revoked_at,replaced_by_credential_id) "
|
|
1157
|
+
"SELECT credential_id,consumer_id,project_id,verifier,fingerprint,issued_at,expires_at,revoked_at,replaced_by_credential_id "
|
|
1158
|
+
"FROM local_api_credentials"
|
|
1159
|
+
)
|
|
1160
|
+
_assert_exact_consumer_transfer(
|
|
1161
|
+
connection, source=legacy_registrations, destination=current_registrations,
|
|
1162
|
+
columns=_CONSUMER_REGISTRATION_COLUMNS,
|
|
1163
|
+
)
|
|
1164
|
+
_assert_exact_consumer_transfer(
|
|
1165
|
+
connection, source=legacy_credentials, destination=current_credentials,
|
|
1166
|
+
columns=_CONSUMER_CREDENTIAL_COLUMNS,
|
|
1167
|
+
)
|
|
1168
|
+
elif current:
|
|
1169
|
+
_require_consumer_table_shape(
|
|
1170
|
+
connection, credentials=current_credentials, registrations=current_registrations,
|
|
1171
|
+
)
|
|
1172
|
+
else:
|
|
1173
|
+
raise EngineeringStorageError("EP consumer credential migration source is absent.")
|
|
1174
|
+
|
|
1175
|
+
|
|
1176
|
+
def _import_legacy_execution_dismissals(root: Path, connection: sqlite3.Connection) -> None:
|
|
1177
|
+
"""Copy valid legacy dismissal evidence into the canonical datastore.
|
|
1178
|
+
|
|
1179
|
+
The former JSON audit is retained as source evidence, but never consulted
|
|
1180
|
+
by projections after its records have been copied to SQLite. Repeating the
|
|
1181
|
+
import is safe: the canonical run key makes it idempotent and permits a
|
|
1182
|
+
record whose history was backfilled later to be imported on a later open.
|
|
1183
|
+
"""
|
|
1184
|
+
path = root / LEGACY_DISMISSALS_PATH
|
|
1185
|
+
try:
|
|
1186
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
1187
|
+
except FileNotFoundError:
|
|
1188
|
+
return
|
|
1189
|
+
except (OSError, json.JSONDecodeError):
|
|
1190
|
+
return
|
|
1191
|
+
if not isinstance(payload, list):
|
|
1192
|
+
return
|
|
1193
|
+
for record in payload:
|
|
1194
|
+
if not isinstance(record, dict) or record.get("dismissed") is not True:
|
|
1195
|
+
continue
|
|
1196
|
+
run_id = record.get("run_id")
|
|
1197
|
+
terminal_state = record.get("terminal_state")
|
|
1198
|
+
dismissed_at = record.get("dismissed_at")
|
|
1199
|
+
dismissed_by = record.get("dismissed_by")
|
|
1200
|
+
if (
|
|
1201
|
+
not isinstance(run_id, str)
|
|
1202
|
+
or not re.fullmatch(r"[a-z0-9][a-z0-9-]{0,63}", run_id)
|
|
1203
|
+
or terminal_state not in {"COMPLETE", "BLOCKED", "FAILED"}
|
|
1204
|
+
or not isinstance(dismissed_at, str)
|
|
1205
|
+
or not dismissed_at.strip()
|
|
1206
|
+
or not isinstance(dismissed_by, str)
|
|
1207
|
+
or not dismissed_by.strip()
|
|
1208
|
+
):
|
|
1209
|
+
continue
|
|
1210
|
+
history_exists = connection.execute(
|
|
1211
|
+
"SELECT 1 FROM prompt_execution_history WHERE run_id=?", (run_id,)
|
|
1212
|
+
).fetchone()
|
|
1213
|
+
if history_exists is None:
|
|
1214
|
+
continue
|
|
1215
|
+
connection.execute(
|
|
1216
|
+
"INSERT OR IGNORE INTO execution_dismissals(run_id,terminal_state,handling_state,dismissed_at,dismissed_by) "
|
|
1217
|
+
"VALUES(?,?,?,?,?)",
|
|
1218
|
+
(run_id, terminal_state, "DISMISSED", dismissed_at.strip(), dismissed_by.strip()),
|
|
1219
|
+
)
|
|
1220
|
+
|
|
1221
|
+
|
|
1222
|
+
MIGRATIONS: dict[int, Migration] = {
|
|
1223
|
+
1: _schema_v1,
|
|
1224
|
+
2: _schema_v2,
|
|
1225
|
+
3: _schema_v3,
|
|
1226
|
+
4: _schema_v4,
|
|
1227
|
+
5: _schema_v5,
|
|
1228
|
+
6: _schema_v6,
|
|
1229
|
+
7: _schema_v7,
|
|
1230
|
+
8: _schema_v8,
|
|
1231
|
+
9: _schema_v9,
|
|
1232
|
+
10: _schema_v10,
|
|
1233
|
+
11: _schema_v11,
|
|
1234
|
+
12: _schema_v12,
|
|
1235
|
+
13: _schema_v13,
|
|
1236
|
+
14: _schema_v14,
|
|
1237
|
+
15: _schema_v15,
|
|
1238
|
+
16: _schema_v16,
|
|
1239
|
+
17: _schema_v17,
|
|
1240
|
+
18: _schema_v18,
|
|
1241
|
+
19: _schema_v19,
|
|
1242
|
+
20: _schema_v20,
|
|
1243
|
+
21: _schema_v21,
|
|
1244
|
+
22: _schema_v22,
|
|
1245
|
+
23: _schema_v23,
|
|
1246
|
+
24: _schema_v24,
|
|
1247
|
+
25: _schema_v25,
|
|
1248
|
+
26: _schema_v26,
|
|
1249
|
+
27: _schema_v27,
|
|
1250
|
+
28: _schema_v28,
|
|
1251
|
+
29: _schema_v29,
|
|
1252
|
+
30: _schema_v30,
|
|
1253
|
+
31: _schema_v31,
|
|
1254
|
+
32: _schema_v32,
|
|
1255
|
+
33: _schema_v33,
|
|
1256
|
+
34: _schema_v34,
|
|
1257
|
+
35: _schema_v35,
|
|
1258
|
+
36: _schema_v36,
|
|
1259
|
+
37: _schema_v37,
|
|
1260
|
+
38: _schema_v38,
|
|
1261
|
+
39: _schema_v39,
|
|
1262
|
+
40: _schema_v40,
|
|
1263
|
+
41: _schema_v41,
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
|
|
1267
|
+
AI_CAPACITY_HISTORY_BUCKET_HOURS = 2
|
|
1268
|
+
|
|
1269
|
+
|
|
1270
|
+
def record_ai_capacity_bi_hourly(
|
|
1271
|
+
root: Path, *, provider: str, remaining_percent: float, observed_at: datetime | None = None
|
|
1272
|
+
) -> None:
|
|
1273
|
+
"""Keep the lowest remaining capacity in each two-hour UTC bucket.
|
|
1274
|
+
|
|
1275
|
+
This intentionally stores only a percentage and a provider label: no
|
|
1276
|
+
account, credit, or request detail is retained. The bounded series uses
|
|
1277
|
+
existing observability projection storage, so it does not alter the
|
|
1278
|
+
Execution Host's active schema contract.
|
|
1279
|
+
"""
|
|
1280
|
+
normalized_provider = provider.strip()[:120]
|
|
1281
|
+
if not normalized_provider:
|
|
1282
|
+
return
|
|
1283
|
+
try:
|
|
1284
|
+
remaining = float(remaining_percent)
|
|
1285
|
+
except (TypeError, ValueError):
|
|
1286
|
+
return
|
|
1287
|
+
if not 0 <= remaining <= 100:
|
|
1288
|
+
return
|
|
1289
|
+
timestamp = (observed_at or datetime.now(timezone.utc)).astimezone(timezone.utc)
|
|
1290
|
+
bucket = timestamp.replace(
|
|
1291
|
+
hour=timestamp.hour - (timestamp.hour % AI_CAPACITY_HISTORY_BUCKET_HOURS),
|
|
1292
|
+
minute=0,
|
|
1293
|
+
second=0,
|
|
1294
|
+
microsecond=0,
|
|
1295
|
+
)
|
|
1296
|
+
connection = open_storage(root, journal_mode="MEMORY")
|
|
1297
|
+
try:
|
|
1298
|
+
row = connection.execute(
|
|
1299
|
+
"SELECT payload FROM execution_projections WHERE projection_name='ai_capacity_hourly'"
|
|
1300
|
+
).fetchone()
|
|
1301
|
+
try:
|
|
1302
|
+
payload = json.loads(row[0]) if row else {}
|
|
1303
|
+
except (TypeError, json.JSONDecodeError):
|
|
1304
|
+
payload = {}
|
|
1305
|
+
providers = payload.get("providers") if isinstance(payload, dict) else None
|
|
1306
|
+
providers = providers if isinstance(providers, dict) else {}
|
|
1307
|
+
samples = providers.get(normalized_provider)
|
|
1308
|
+
samples = samples if isinstance(samples, dict) else {}
|
|
1309
|
+
bucket_key = bucket.isoformat()
|
|
1310
|
+
existing = samples.get(bucket_key)
|
|
1311
|
+
if isinstance(existing, (int, float)) and not isinstance(existing, bool):
|
|
1312
|
+
remaining = min(remaining, float(existing))
|
|
1313
|
+
if remaining == float(existing):
|
|
1314
|
+
return
|
|
1315
|
+
samples[bucket_key] = remaining
|
|
1316
|
+
cutoff = bucket.timestamp() - 7 * 24 * 3600
|
|
1317
|
+
providers[normalized_provider] = {
|
|
1318
|
+
key: value
|
|
1319
|
+
for key, value in samples.items()
|
|
1320
|
+
if isinstance(value, (int, float))
|
|
1321
|
+
and not isinstance(value, bool)
|
|
1322
|
+
and (_capacity_timestamp(key) or 0) >= cutoff
|
|
1323
|
+
}
|
|
1324
|
+
store_projection(
|
|
1325
|
+
connection,
|
|
1326
|
+
"ai_capacity_hourly",
|
|
1327
|
+
{"providers": providers, "updated_at": timestamp.isoformat()},
|
|
1328
|
+
classification="OBSERVABILITY",
|
|
1329
|
+
)
|
|
1330
|
+
finally:
|
|
1331
|
+
connection.close()
|
|
1332
|
+
|
|
1333
|
+
|
|
1334
|
+
def ai_capacity_history(root: Path, *, provider: str, hours: int = 168) -> list[dict[str, object]]:
|
|
1335
|
+
"""Return the rolling two-hour local capacity series for a provider."""
|
|
1336
|
+
normalized_provider = provider.strip()[:120]
|
|
1337
|
+
if not normalized_provider or hours < 1:
|
|
1338
|
+
return []
|
|
1339
|
+
cutoff = datetime.now(timezone.utc).replace(minute=0, second=0, microsecond=0)
|
|
1340
|
+
earliest = cutoff.timestamp() - (hours - 1) * 3600
|
|
1341
|
+
connection = open_storage(root, journal_mode="MEMORY")
|
|
1342
|
+
try:
|
|
1343
|
+
row = connection.execute(
|
|
1344
|
+
"SELECT payload FROM execution_projections WHERE projection_name='ai_capacity_hourly'"
|
|
1345
|
+
).fetchone()
|
|
1346
|
+
finally:
|
|
1347
|
+
connection.close()
|
|
1348
|
+
try:
|
|
1349
|
+
payload = json.loads(row[0]) if row else {}
|
|
1350
|
+
except (TypeError, json.JSONDecodeError):
|
|
1351
|
+
return []
|
|
1352
|
+
providers = payload.get("providers") if isinstance(payload, dict) else None
|
|
1353
|
+
samples = providers.get(normalized_provider) if isinstance(providers, dict) else None
|
|
1354
|
+
if not isinstance(samples, dict):
|
|
1355
|
+
return []
|
|
1356
|
+
return [
|
|
1357
|
+
{"at": key, "remaining_percent": float(value)}
|
|
1358
|
+
for key, value in sorted(samples.items())
|
|
1359
|
+
if isinstance(value, (int, float))
|
|
1360
|
+
and not isinstance(value, bool)
|
|
1361
|
+
and 0 <= float(value) <= 100
|
|
1362
|
+
and (_capacity_timestamp(key) or 0) >= earliest
|
|
1363
|
+
and int((_capacity_timestamp(key) or 0) // 3600) % AI_CAPACITY_HISTORY_BUCKET_HOURS == 0
|
|
1364
|
+
]
|
|
1365
|
+
|
|
1366
|
+
|
|
1367
|
+
def _capacity_timestamp(value: object) -> float | None:
|
|
1368
|
+
"""Parse a bounded UTC capacity bucket without accepting malformed data."""
|
|
1369
|
+
if not isinstance(value, str):
|
|
1370
|
+
return None
|
|
1371
|
+
try:
|
|
1372
|
+
timestamp = datetime.fromisoformat(value)
|
|
1373
|
+
except ValueError:
|
|
1374
|
+
return None
|
|
1375
|
+
return timestamp.astimezone(timezone.utc).timestamp() if timestamp.tzinfo else None
|
|
1376
|
+
|
|
1377
|
+
|
|
1378
|
+
def dismissal_for_run(root: Path, run_id: object) -> dict[str, object] | None:
|
|
1379
|
+
"""Return immutable operator-handling evidence from canonical SQLite."""
|
|
1380
|
+
if not isinstance(run_id, str) or not re.fullmatch(r"[a-z0-9][a-z0-9-]{0,63}", run_id):
|
|
1381
|
+
return None
|
|
1382
|
+
connection = open_storage(root)
|
|
1383
|
+
try:
|
|
1384
|
+
row = connection.execute(
|
|
1385
|
+
"SELECT terminal_state,handling_state,dismissed_at,dismissed_by "
|
|
1386
|
+
"FROM execution_dismissals WHERE run_id=?", (run_id,)
|
|
1387
|
+
).fetchone()
|
|
1388
|
+
finally:
|
|
1389
|
+
connection.close()
|
|
1390
|
+
if row is None:
|
|
1391
|
+
return None
|
|
1392
|
+
return {
|
|
1393
|
+
"run_id": run_id,
|
|
1394
|
+
"terminal_state": row[0],
|
|
1395
|
+
"dismissed": True,
|
|
1396
|
+
"handling_state": row[1],
|
|
1397
|
+
"dismissed_at": row[2],
|
|
1398
|
+
"dismissed_by": row[3],
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
|
|
1402
|
+
def is_active_blocking_predecessor(root: Path, run_id: object, terminal_state: object) -> bool:
|
|
1403
|
+
"""Return whether canonical evidence still requires predecessor resolution.
|
|
1404
|
+
|
|
1405
|
+
A terminal ``BLOCKED`` or ``FAILED`` run fails closed unless its immutable
|
|
1406
|
+
dismissal record agrees with that terminal evidence. Dismissal is
|
|
1407
|
+
operator handling, not a lifecycle rewrite: callers must retain the
|
|
1408
|
+
original terminal state in history while removing only its active queue
|
|
1409
|
+
gate.
|
|
1410
|
+
"""
|
|
1411
|
+
if terminal_state not in {"BLOCKED", "FAILED"}:
|
|
1412
|
+
return False
|
|
1413
|
+
if not isinstance(run_id, str) or not re.fullmatch(r"[a-z0-9][a-z0-9-]{0,63}", run_id):
|
|
1414
|
+
return False
|
|
1415
|
+
dismissal = dismissal_for_run(root, run_id)
|
|
1416
|
+
if dismissal is None:
|
|
1417
|
+
# Older records without canonical operator evidence deliberately stay
|
|
1418
|
+
# blocking; absence must never be interpreted as a dismissal.
|
|
1419
|
+
return True
|
|
1420
|
+
return not (
|
|
1421
|
+
dismissal.get("dismissed") is True
|
|
1422
|
+
and dismissal.get("handling_state") == "DISMISSED"
|
|
1423
|
+
and dismissal.get("terminal_state") == terminal_state
|
|
1424
|
+
)
|
|
1425
|
+
|
|
1426
|
+
|
|
1427
|
+
def record_execution_dismissal(root: Path, *, run_id: str, terminal_state: str,
|
|
1428
|
+
dismissed_at: str, dismissed_by: str,
|
|
1429
|
+
central_database: Path | None = None) -> dict[str, object]:
|
|
1430
|
+
"""Record one immutable dismissal after its terminal history row exists."""
|
|
1431
|
+
if terminal_state not in {"COMPLETE", "BLOCKED", "FAILED"}:
|
|
1432
|
+
raise EngineeringStorageError("Dismissal requires a terminal execution outcome.")
|
|
1433
|
+
connection = open_storage(root) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None)
|
|
1434
|
+
try:
|
|
1435
|
+
connection.execute(
|
|
1436
|
+
"INSERT INTO execution_dismissals(run_id,terminal_state,handling_state,dismissed_at,dismissed_by) "
|
|
1437
|
+
"VALUES(?,?,?,?,?)",
|
|
1438
|
+
(run_id, terminal_state, "DISMISSED", dismissed_at, dismissed_by),
|
|
1439
|
+
)
|
|
1440
|
+
except sqlite3.IntegrityError as error:
|
|
1441
|
+
raise EngineeringStorageError("Execution dismissal is already recorded or has no terminal history.") from error
|
|
1442
|
+
finally:
|
|
1443
|
+
connection.close()
|
|
1444
|
+
return {
|
|
1445
|
+
"run_id": run_id, "terminal_state": terminal_state, "dismissed": True,
|
|
1446
|
+
"handling_state": "DISMISSED", "dismissed_at": dismissed_at, "dismissed_by": dismissed_by,
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
|
|
1450
|
+
def record_emergency_recovery(root: Path, *, run_id: str, cancelled_at: str,
|
|
1451
|
+
rolled_back: bool, removed_branch: str | None,
|
|
1452
|
+
central_database: Path | None = None) -> None:
|
|
1453
|
+
"""Append one immutable operator-authorized emergency recovery record."""
|
|
1454
|
+
if not re.fullmatch(r"inbox-[a-z0-9-]{6,64}", run_id):
|
|
1455
|
+
raise EngineeringStorageError("Emergency recovery requires a valid Inbox run.")
|
|
1456
|
+
if not isinstance(cancelled_at, str) or not cancelled_at.strip():
|
|
1457
|
+
raise EngineeringStorageError("Emergency recovery requires a timestamp.")
|
|
1458
|
+
if removed_branch is not None and (not isinstance(removed_branch, str) or not removed_branch.startswith("codex/")):
|
|
1459
|
+
raise EngineeringStorageError("Emergency recovery branch evidence is invalid.")
|
|
1460
|
+
connection = open_storage(root) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None)
|
|
1461
|
+
try:
|
|
1462
|
+
connection.execute(
|
|
1463
|
+
"INSERT INTO execution_emergency_recoveries(run_id,cancelled_at,rolled_back,removed_branch,recorded_by) VALUES(?,?,?,?,?)",
|
|
1464
|
+
(run_id, cancelled_at.strip(), int(rolled_back), removed_branch, "dashboard_emergency_recovery"),
|
|
1465
|
+
)
|
|
1466
|
+
except sqlite3.IntegrityError as error:
|
|
1467
|
+
raise EngineeringStorageError("Emergency recovery is already recorded or has no terminal history.") from error
|
|
1468
|
+
finally:
|
|
1469
|
+
connection.close()
|
|
1470
|
+
|
|
1471
|
+
|
|
1472
|
+
def _encoded_payload(payload: dict[str, object]) -> str:
|
|
1473
|
+
return json.dumps(payload, separators=(",", ":"), sort_keys=True)
|
|
1474
|
+
|
|
1475
|
+
|
|
1476
|
+
def store_projection(connection: sqlite3.Connection, name: str, payload: dict[str, object], *, classification: str = "PROJECTION", updated_at: str | None = None) -> None:
|
|
1477
|
+
"""Persist a bounded projection record before its optional filesystem copy."""
|
|
1478
|
+
if classification not in {"PROJECTION", "ARTIFACT_PAYLOAD", "CONFIGURATION", "OBSERVABILITY", "RECOVERY_EXPORT"}:
|
|
1479
|
+
raise EngineeringStorageError("Unknown execution projection classification.")
|
|
1480
|
+
encoded = _encoded_payload(payload)
|
|
1481
|
+
connection.execute(
|
|
1482
|
+
"INSERT INTO execution_projections(projection_name,classification,payload,source_digest,updated_at) VALUES(?,?,?,?,?) "
|
|
1483
|
+
"ON CONFLICT(projection_name) DO UPDATE SET classification=excluded.classification,payload=excluded.payload,source_digest=excluded.source_digest,updated_at=excluded.updated_at",
|
|
1484
|
+
(name, classification, encoded, hashlib.sha256(encoded.encode()).hexdigest(), updated_at or datetime.now(timezone.utc).isoformat()),
|
|
1485
|
+
)
|
|
1486
|
+
|
|
1487
|
+
|
|
1488
|
+
def load_projection(
|
|
1489
|
+
root: Path, name: str, *, central_database: Path | None = None,
|
|
1490
|
+
) -> dict[str, object] | None:
|
|
1491
|
+
"""Load a canonical projection without consulting its compatibility file."""
|
|
1492
|
+
if central_database is None:
|
|
1493
|
+
connection = open_storage(root)
|
|
1494
|
+
else:
|
|
1495
|
+
database = central_database.resolve()
|
|
1496
|
+
if not database.is_file():
|
|
1497
|
+
raise EngineeringStorageError("CENTRAL projection database is unavailable.")
|
|
1498
|
+
connection = sqlite3.connect(database, isolation_level=None)
|
|
1499
|
+
try:
|
|
1500
|
+
row = connection.execute("SELECT payload FROM execution_projections WHERE projection_name=?", (name,)).fetchone()
|
|
1501
|
+
finally:
|
|
1502
|
+
connection.close()
|
|
1503
|
+
if not row:
|
|
1504
|
+
return None
|
|
1505
|
+
try:
|
|
1506
|
+
payload = json.loads(row[0])
|
|
1507
|
+
except (TypeError, json.JSONDecodeError) as error:
|
|
1508
|
+
raise EngineeringStorageError("Canonical execution projection is corrupt.") from error
|
|
1509
|
+
if not isinstance(payload, dict):
|
|
1510
|
+
raise EngineeringStorageError("Canonical execution projection is invalid.")
|
|
1511
|
+
return payload
|
|
1512
|
+
|
|
1513
|
+
|
|
1514
|
+
def record_submission(
|
|
1515
|
+
root: Path,
|
|
1516
|
+
*,
|
|
1517
|
+
submission_id: str,
|
|
1518
|
+
producer_id: str,
|
|
1519
|
+
producer_type: str,
|
|
1520
|
+
prompt_content: str,
|
|
1521
|
+
prompt_metadata: dict[str, object],
|
|
1522
|
+
target_identity: dict[str, object],
|
|
1523
|
+
original_envelope: dict[str, object] | str,
|
|
1524
|
+
received_at: str,
|
|
1525
|
+
producer_version: str | None = None,
|
|
1526
|
+
contract_version: str | None = None,
|
|
1527
|
+
correlation_id: str | None = None,
|
|
1528
|
+
mission_id: str | None = None,
|
|
1529
|
+
engineering_action_id: str | None = None,
|
|
1530
|
+
execution_run_id: str | None = None,
|
|
1531
|
+
link_run_id: str | None = None,
|
|
1532
|
+
execution_context: dict[str, object] | None = None,
|
|
1533
|
+
forge_governance_handoff: dict[str, object] | None = None,
|
|
1534
|
+
) -> None:
|
|
1535
|
+
"""Persist the complete producer envelope before an Inbox file is consumed."""
|
|
1536
|
+
if not all(isinstance(value, str) and value for value in (submission_id, producer_id, producer_type, prompt_content, received_at)):
|
|
1537
|
+
raise EngineeringStorageError("Execution submission identity is invalid.")
|
|
1538
|
+
connection = open_storage(root)
|
|
1539
|
+
try:
|
|
1540
|
+
encoded_envelope = original_envelope if isinstance(original_envelope, str) else _encoded_payload(original_envelope)
|
|
1541
|
+
encoded_context = _encoded_payload(execution_context) if execution_context is not None else None
|
|
1542
|
+
encoded_handoff = _encoded_payload(forge_governance_handoff) if forge_governance_handoff is not None else None
|
|
1543
|
+
context_version = execution_context.get("context_version") if isinstance(execution_context, dict) else None
|
|
1544
|
+
handoff_version = forge_governance_handoff.get("version") if isinstance(forge_governance_handoff, dict) else None
|
|
1545
|
+
if context_version is not None and not isinstance(context_version, str):
|
|
1546
|
+
raise EngineeringStorageError("Execution Context snapshot version is invalid.")
|
|
1547
|
+
if handoff_version is not None and not isinstance(handoff_version, str):
|
|
1548
|
+
raise EngineeringStorageError("Forge Governance Handoff snapshot version is invalid.")
|
|
1549
|
+
connection.execute(
|
|
1550
|
+
"INSERT INTO execution_submissions(submission_id,producer_id,producer_type,producer_version,contract_version,prompt_content,prompt_metadata,target_identity,original_envelope,correlation_id,mission_id,execution_run_id,received_at,execution_context_snapshot,execution_context_version,engineering_action_id,forge_governance_handoff_snapshot,forge_governance_handoff_version) "
|
|
1551
|
+
"VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) "
|
|
1552
|
+
"ON CONFLICT(submission_id) DO NOTHING",
|
|
1553
|
+
(submission_id, producer_id, producer_type, producer_version, contract_version, prompt_content,
|
|
1554
|
+
_encoded_payload(prompt_metadata), _encoded_payload(target_identity), encoded_envelope,
|
|
1555
|
+
correlation_id, mission_id, execution_run_id, received_at, encoded_context, context_version, engineering_action_id, encoded_handoff, handoff_version),
|
|
1556
|
+
)
|
|
1557
|
+
if link_run_id:
|
|
1558
|
+
existing = connection.execute(
|
|
1559
|
+
"SELECT run_id FROM execution_submission_links WHERE submission_id=?",
|
|
1560
|
+
(submission_id,),
|
|
1561
|
+
).fetchone()
|
|
1562
|
+
if existing is not None and existing[0] != link_run_id:
|
|
1563
|
+
raise EngineeringStorageError("Execution submission is already bound to a different run.")
|
|
1564
|
+
connection.execute(
|
|
1565
|
+
"INSERT INTO execution_submission_links(submission_id,run_id,linked_at) VALUES(?,?,?) ON CONFLICT(submission_id) DO NOTHING",
|
|
1566
|
+
(submission_id, link_run_id, received_at),
|
|
1567
|
+
)
|
|
1568
|
+
current = connection.execute(
|
|
1569
|
+
"SELECT execution_run_id FROM execution_submissions WHERE submission_id=?",
|
|
1570
|
+
(submission_id,),
|
|
1571
|
+
).fetchone()
|
|
1572
|
+
if current is None:
|
|
1573
|
+
raise EngineeringStorageError("Execution submission was not persisted.")
|
|
1574
|
+
if current[0] not in (None, link_run_id):
|
|
1575
|
+
raise EngineeringStorageError("Execution submission is already bound to a different run.")
|
|
1576
|
+
# The link is the crash-safe admission-time binding. The FK-backed
|
|
1577
|
+
# column is filled as soon as its execution-run row exists.
|
|
1578
|
+
if current[0] is None and connection.execute(
|
|
1579
|
+
"SELECT 1 FROM execution_runs WHERE run_id=?", (link_run_id,)
|
|
1580
|
+
).fetchone():
|
|
1581
|
+
connection.execute(
|
|
1582
|
+
"UPDATE execution_submissions SET execution_run_id=? WHERE submission_id=? AND execution_run_id IS NULL",
|
|
1583
|
+
(link_run_id, submission_id),
|
|
1584
|
+
)
|
|
1585
|
+
finally:
|
|
1586
|
+
connection.close()
|
|
1587
|
+
|
|
1588
|
+
|
|
1589
|
+
def record_admission_decision(
|
|
1590
|
+
root: Path,
|
|
1591
|
+
*,
|
|
1592
|
+
run_id: str,
|
|
1593
|
+
submission_id: str,
|
|
1594
|
+
execution_mode: str,
|
|
1595
|
+
decision: str,
|
|
1596
|
+
failed_gate_ids: tuple[str, ...],
|
|
1597
|
+
evidence: tuple[dict[str, object], ...],
|
|
1598
|
+
observed_at: str,
|
|
1599
|
+
) -> None:
|
|
1600
|
+
"""Persist one bounded admission outcome before any provider-backed work.
|
|
1601
|
+
|
|
1602
|
+
Admission evidence intentionally contains only gate identifiers, expected and
|
|
1603
|
+
observed states, and timestamps. Prompt content, provider output and
|
|
1604
|
+
diagnostics remain outside this projection.
|
|
1605
|
+
"""
|
|
1606
|
+
if decision not in {"PASS", "FAIL"} or not run_id or not submission_id:
|
|
1607
|
+
raise EngineeringStorageError("Admission decision identity is invalid.")
|
|
1608
|
+
connection = open_storage(root)
|
|
1609
|
+
try:
|
|
1610
|
+
connection.execute(
|
|
1611
|
+
"INSERT OR IGNORE INTO execution_admission_decisions(run_id,submission_id,execution_mode,decision,failed_gate_ids,evidence,observed_at) "
|
|
1612
|
+
"VALUES(?,?,?,?,?,?,?)",
|
|
1613
|
+
(
|
|
1614
|
+
run_id,
|
|
1615
|
+
submission_id,
|
|
1616
|
+
execution_mode,
|
|
1617
|
+
decision,
|
|
1618
|
+
_encoded_payload({"gate_ids": list(failed_gate_ids)}),
|
|
1619
|
+
_encoded_payload({"gates": list(evidence)}),
|
|
1620
|
+
observed_at,
|
|
1621
|
+
),
|
|
1622
|
+
)
|
|
1623
|
+
finally:
|
|
1624
|
+
connection.close()
|
|
1625
|
+
|
|
1626
|
+
|
|
1627
|
+
def load_admission_decision(root: Path, run_id: str, *, central_database: Path | None = None) -> dict[str, object] | None:
|
|
1628
|
+
"""Load one structured provider-free admission decision for rendering."""
|
|
1629
|
+
connection = open_storage(root) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None)
|
|
1630
|
+
try:
|
|
1631
|
+
row = connection.execute(
|
|
1632
|
+
"SELECT submission_id,execution_mode,decision,failed_gate_ids,evidence,observed_at "
|
|
1633
|
+
"FROM execution_admission_decisions WHERE run_id=?",
|
|
1634
|
+
(run_id,),
|
|
1635
|
+
).fetchone()
|
|
1636
|
+
finally:
|
|
1637
|
+
connection.close()
|
|
1638
|
+
if not row:
|
|
1639
|
+
return None
|
|
1640
|
+
try:
|
|
1641
|
+
failed = json.loads(row[3])
|
|
1642
|
+
evidence = json.loads(row[4])
|
|
1643
|
+
except (TypeError, json.JSONDecodeError) as error:
|
|
1644
|
+
raise EngineeringStorageError("Admission decision evidence is corrupt.") from error
|
|
1645
|
+
if not isinstance(failed, dict) or not isinstance(evidence, dict):
|
|
1646
|
+
raise EngineeringStorageError("Admission decision evidence is invalid.")
|
|
1647
|
+
return {
|
|
1648
|
+
"run_id": run_id,
|
|
1649
|
+
"submission_id": row[0],
|
|
1650
|
+
"execution_mode": row[1],
|
|
1651
|
+
"decision": row[2],
|
|
1652
|
+
"failed_gate_ids": failed.get("gate_ids", []),
|
|
1653
|
+
"gates": evidence.get("gates", []),
|
|
1654
|
+
"observed_at": row[5],
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
|
|
1658
|
+
def load_execution_context_snapshot(root: Path, run_id: str) -> dict[str, object] | None:
|
|
1659
|
+
"""Read only the persisted immutable context snapshot linked to one run."""
|
|
1660
|
+
connection = open_storage(root)
|
|
1661
|
+
try:
|
|
1662
|
+
row = connection.execute(
|
|
1663
|
+
"SELECT submission.execution_context_snapshot FROM execution_submissions AS submission "
|
|
1664
|
+
"JOIN execution_submission_links AS link ON link.submission_id=submission.submission_id "
|
|
1665
|
+
"WHERE link.run_id=?", (run_id,)
|
|
1666
|
+
).fetchone()
|
|
1667
|
+
finally:
|
|
1668
|
+
connection.close()
|
|
1669
|
+
if not row or row[0] is None:
|
|
1670
|
+
return None
|
|
1671
|
+
try:
|
|
1672
|
+
snapshot = json.loads(row[0])
|
|
1673
|
+
except (TypeError, json.JSONDecodeError) as error:
|
|
1674
|
+
raise EngineeringStorageError("Persisted Execution Context snapshot is corrupt.") from error
|
|
1675
|
+
if not isinstance(snapshot, dict):
|
|
1676
|
+
raise EngineeringStorageError("Persisted Execution Context snapshot is invalid.")
|
|
1677
|
+
return snapshot
|
|
1678
|
+
|
|
1679
|
+
|
|
1680
|
+
def load_forge_governance_handoff_snapshot(root: Path, run_id: str) -> dict[str, object] | None:
|
|
1681
|
+
"""Read the immutable Producer-supplied Forge handoff for one run."""
|
|
1682
|
+
connection = open_storage(root)
|
|
1683
|
+
try:
|
|
1684
|
+
row = connection.execute(
|
|
1685
|
+
"SELECT submission.forge_governance_handoff_snapshot FROM execution_submissions AS submission "
|
|
1686
|
+
"JOIN execution_submission_links AS link ON link.submission_id=submission.submission_id "
|
|
1687
|
+
"WHERE link.run_id=?", (run_id,)
|
|
1688
|
+
).fetchone()
|
|
1689
|
+
finally:
|
|
1690
|
+
connection.close()
|
|
1691
|
+
if not row or row[0] is None:
|
|
1692
|
+
return None
|
|
1693
|
+
try:
|
|
1694
|
+
snapshot = json.loads(row[0])
|
|
1695
|
+
except (TypeError, json.JSONDecodeError) as error:
|
|
1696
|
+
raise EngineeringStorageError("Persisted Forge Governance Handoff snapshot is corrupt.") from error
|
|
1697
|
+
if not isinstance(snapshot, dict):
|
|
1698
|
+
raise EngineeringStorageError("Persisted Forge Governance Handoff snapshot is invalid.")
|
|
1699
|
+
return snapshot
|
|
1700
|
+
|
|
1701
|
+
|
|
1702
|
+
def load_submission_for_run(root: Path, run_id: str, *, central_database: Path | None = None) -> dict[str, object] | None:
|
|
1703
|
+
"""Load immutable Producer provenance for one linked execution without prompt inspection."""
|
|
1704
|
+
connection = open_storage(root) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None)
|
|
1705
|
+
try:
|
|
1706
|
+
row = connection.execute(
|
|
1707
|
+
"SELECT submission.submission_id,submission.producer_id,submission.producer_type,"
|
|
1708
|
+
"submission.producer_version,submission.contract_version,submission.correlation_id,"
|
|
1709
|
+
"submission.mission_id,submission.engineering_action_id,submission.execution_context_version,submission.execution_context_snapshot,"
|
|
1710
|
+
"submission.forge_governance_handoff_version,submission.forge_governance_handoff_snapshot "
|
|
1711
|
+
"FROM execution_submissions AS submission JOIN execution_submission_links AS link "
|
|
1712
|
+
"ON link.submission_id=submission.submission_id WHERE link.run_id=?", (run_id,)
|
|
1713
|
+
).fetchone()
|
|
1714
|
+
finally:
|
|
1715
|
+
connection.close()
|
|
1716
|
+
if not row:
|
|
1717
|
+
return None
|
|
1718
|
+
snapshot = None
|
|
1719
|
+
if row[9] is not None:
|
|
1720
|
+
try:
|
|
1721
|
+
snapshot = json.loads(row[9])
|
|
1722
|
+
except (TypeError, json.JSONDecodeError) as error:
|
|
1723
|
+
raise EngineeringStorageError("Persisted Execution Context snapshot is corrupt.") from error
|
|
1724
|
+
if not isinstance(snapshot, dict):
|
|
1725
|
+
raise EngineeringStorageError("Persisted Execution Context snapshot is invalid.")
|
|
1726
|
+
handoff = None
|
|
1727
|
+
if row[11] is not None:
|
|
1728
|
+
try:
|
|
1729
|
+
handoff = json.loads(row[11])
|
|
1730
|
+
except (TypeError, json.JSONDecodeError) as error:
|
|
1731
|
+
raise EngineeringStorageError("Persisted Forge Governance Handoff snapshot is corrupt.") from error
|
|
1732
|
+
if not isinstance(handoff, dict):
|
|
1733
|
+
raise EngineeringStorageError("Persisted Forge Governance Handoff snapshot is invalid.")
|
|
1734
|
+
return {
|
|
1735
|
+
"submission_id": row[0], "producer_id": row[1], "producer_type": row[2],
|
|
1736
|
+
"producer_version": row[3], "contract_version": row[4], "correlation_id": row[5],
|
|
1737
|
+
"mission_id": row[6], "engineering_action_id": row[7], "execution_context_version": row[8], "execution_context": snapshot,
|
|
1738
|
+
"forge_governance_handoff_version": row[10], "forge_governance_handoff": handoff,
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
|
|
1742
|
+
def load_run_lineage(root: Path, run_id: str) -> dict[str, object] | None:
|
|
1743
|
+
"""Load explicit qualification lineage; legacy rows deliberately remain unavailable."""
|
|
1744
|
+
connection = open_storage(root)
|
|
1745
|
+
try:
|
|
1746
|
+
row = connection.execute(
|
|
1747
|
+
"SELECT submission_id,fresh_submission,retry_parent_run_id,resume_parent_run_id,recorded_at "
|
|
1748
|
+
"FROM execution_run_qualification_context WHERE run_id=?", (run_id,)
|
|
1749
|
+
).fetchone()
|
|
1750
|
+
finally:
|
|
1751
|
+
connection.close()
|
|
1752
|
+
if row is None:
|
|
1753
|
+
return None
|
|
1754
|
+
return {
|
|
1755
|
+
"submission_id": row[0], "fresh_submission": bool(row[1]),
|
|
1756
|
+
"retry_parent": row[2], "resume_parent": row[3], "recorded_at": row[4],
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
|
|
1760
|
+
def record_run_qualification_context(
|
|
1761
|
+
root: Path, *, run_id: str, submission_id: str, fresh_submission: bool,
|
|
1762
|
+
retry_parent_run_id: str | None, resume_parent_run_id: str | None,
|
|
1763
|
+
recorded_at: str,
|
|
1764
|
+
) -> None:
|
|
1765
|
+
"""Persist one immutable lineage statement before provider work starts."""
|
|
1766
|
+
if not all(isinstance(value, str) and value for value in (run_id, submission_id, recorded_at)):
|
|
1767
|
+
raise EngineeringStorageError("Qualification lineage identity is invalid.")
|
|
1768
|
+
if retry_parent_run_id is not None and resume_parent_run_id is not None:
|
|
1769
|
+
raise EngineeringStorageError("Qualification lineage cannot have dual parents.")
|
|
1770
|
+
if fresh_submission != (retry_parent_run_id is None and resume_parent_run_id is None):
|
|
1771
|
+
raise EngineeringStorageError("Qualification lineage fresh state is inconsistent.")
|
|
1772
|
+
connection = open_storage(root)
|
|
1773
|
+
try:
|
|
1774
|
+
connection.execute(
|
|
1775
|
+
"INSERT OR IGNORE INTO execution_run_qualification_context("
|
|
1776
|
+
"run_id,submission_id,fresh_submission,retry_parent_run_id,resume_parent_run_id,recorded_at) VALUES(?,?,?,?,?,?)",
|
|
1777
|
+
(run_id, submission_id, int(fresh_submission), retry_parent_run_id, resume_parent_run_id, recorded_at),
|
|
1778
|
+
)
|
|
1779
|
+
finally:
|
|
1780
|
+
connection.close()
|
|
1781
|
+
|
|
1782
|
+
|
|
1783
|
+
def record_validation_profile(
|
|
1784
|
+
root: Path, *, run_id: str, selected_validation_tier: str, validation_profile_version: str,
|
|
1785
|
+
required_validation_controls: tuple[str, ...], recorded_at: str,
|
|
1786
|
+
profile_reference: str | None = None, profile_selection_source: str | None = None,
|
|
1787
|
+
control_bindings: tuple[dict[str, object], ...] | None = None,
|
|
1788
|
+
central_database: Path | None = None,
|
|
1789
|
+
) -> None:
|
|
1790
|
+
"""Persist the exact mandatory controls before their execution evidence."""
|
|
1791
|
+
if not run_id or not selected_validation_tier or not validation_profile_version or not recorded_at:
|
|
1792
|
+
raise EngineeringStorageError("Validation profile identity is invalid.")
|
|
1793
|
+
if not required_validation_controls or any(not control for control in required_validation_controls):
|
|
1794
|
+
raise EngineeringStorageError("Validation profile controls are invalid.")
|
|
1795
|
+
if control_bindings is None:
|
|
1796
|
+
# Compatibility for existing callers while new lifecycle paths persist
|
|
1797
|
+
# their binding explicitly before scheduling anything.
|
|
1798
|
+
from .validation_profile import control_binding
|
|
1799
|
+
resolved = tuple(control_binding(control) for control in required_validation_controls)
|
|
1800
|
+
control_bindings = tuple(
|
|
1801
|
+
binding if binding is not None else {
|
|
1802
|
+
"validation_id": control, "required": True, "category": "unavailable",
|
|
1803
|
+
"control_identity": control, "command": [],
|
|
1804
|
+
}
|
|
1805
|
+
for control, binding in zip(required_validation_controls, resolved, strict=True)
|
|
1806
|
+
)
|
|
1807
|
+
binding_ids = tuple(binding.get("validation_id") for binding in control_bindings if isinstance(binding, dict))
|
|
1808
|
+
if binding_ids != required_validation_controls:
|
|
1809
|
+
raise EngineeringStorageError("Validation profile bindings are invalid.")
|
|
1810
|
+
payload = {
|
|
1811
|
+
"validation_ids": list(required_validation_controls),
|
|
1812
|
+
"profile_reference": profile_reference or f"validation-profile-registry:{selected_validation_tier}@{validation_profile_version}",
|
|
1813
|
+
"profile_selection_source": profile_selection_source or "registry",
|
|
1814
|
+
"control_bindings": list(control_bindings),
|
|
1815
|
+
}
|
|
1816
|
+
connection = open_storage(root) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None)
|
|
1817
|
+
try:
|
|
1818
|
+
connection.execute(
|
|
1819
|
+
"INSERT OR IGNORE INTO execution_validation_profiles(run_id,selected_validation_tier,validation_profile_version,required_validation_controls,recorded_at) VALUES(?,?,?,?,?)",
|
|
1820
|
+
(run_id, selected_validation_tier, validation_profile_version, _encoded_payload(payload), recorded_at),
|
|
1821
|
+
)
|
|
1822
|
+
finally:
|
|
1823
|
+
connection.close()
|
|
1824
|
+
|
|
1825
|
+
|
|
1826
|
+
def record_validation_control_result(
|
|
1827
|
+
root: Path, *, run_id: str, validation_id: str, category: str, control_identity: str,
|
|
1828
|
+
required_for_profile: bool, execution_status: str, result: str, evidence_ref: str,
|
|
1829
|
+
observed_at: str, currentness: int, central_database: Path | None = None,
|
|
1830
|
+
) -> None:
|
|
1831
|
+
"""Append one machine-readable validation observation for its resolved profile."""
|
|
1832
|
+
if not all(isinstance(value, str) and value for value in (run_id, validation_id, category, control_identity, execution_status, result, evidence_ref, observed_at)) or currentness < 0:
|
|
1833
|
+
raise EngineeringStorageError("Validation control result is invalid.")
|
|
1834
|
+
connection = open_storage(root) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None)
|
|
1835
|
+
try:
|
|
1836
|
+
connection.execute(
|
|
1837
|
+
"INSERT OR IGNORE INTO execution_validation_control_results("
|
|
1838
|
+
"run_id,validation_id,category,control_identity,required_for_profile,execution_status,result,evidence_ref,observed_at,currentness) VALUES(?,?,?,?,?,?,?,?,?,?)",
|
|
1839
|
+
(run_id, validation_id, category, control_identity, int(required_for_profile), execution_status, result, evidence_ref, observed_at, currentness),
|
|
1840
|
+
)
|
|
1841
|
+
finally:
|
|
1842
|
+
connection.close()
|
|
1843
|
+
|
|
1844
|
+
|
|
1845
|
+
def record_validation_command_invocation(
|
|
1846
|
+
root: Path, *, run_id: str, validation_id: str, command_id: str, category: str,
|
|
1847
|
+
control_identity: str, required_for_profile: bool, started_at: str, currentness: int,
|
|
1848
|
+
central_database: Path | None = None,
|
|
1849
|
+
) -> None:
|
|
1850
|
+
"""Record authoritative command start before subprocess completion is known."""
|
|
1851
|
+
values = (run_id, validation_id, command_id, category, control_identity, started_at)
|
|
1852
|
+
if not all(isinstance(value, str) and value for value in values) or currentness < 0:
|
|
1853
|
+
raise EngineeringStorageError("Validation command invocation is invalid.")
|
|
1854
|
+
connection = open_storage(root) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None)
|
|
1855
|
+
try:
|
|
1856
|
+
connection.execute(
|
|
1857
|
+
"INSERT OR IGNORE INTO execution_validation_command_invocations("
|
|
1858
|
+
"run_id,validation_id,command_id,category,control_identity,required_for_profile,started_at,currentness,evidence_ref) "
|
|
1859
|
+
"VALUES(?,?,?,?,?,?,?,?,?)",
|
|
1860
|
+
(run_id, validation_id, command_id, category, control_identity, int(required_for_profile), started_at, currentness, "command_invocation"),
|
|
1861
|
+
)
|
|
1862
|
+
finally:
|
|
1863
|
+
connection.close()
|
|
1864
|
+
|
|
1865
|
+
|
|
1866
|
+
def record_validation_command_terminal(
|
|
1867
|
+
root: Path, *, run_id: str, command_id: str, completed_at: str, exit_code: int | None,
|
|
1868
|
+
evidence_ref: str = "command_terminal", central_database: Path | None = None,
|
|
1869
|
+
) -> None:
|
|
1870
|
+
"""Close a previously recorded command with its observed terminal outcome."""
|
|
1871
|
+
if not all(isinstance(value, str) and value for value in (run_id, command_id, completed_at, evidence_ref)):
|
|
1872
|
+
raise EngineeringStorageError("Validation command terminal evidence is invalid.")
|
|
1873
|
+
if exit_code is not None and (isinstance(exit_code, bool) or not isinstance(exit_code, int)):
|
|
1874
|
+
raise EngineeringStorageError("Validation command exit code is invalid.")
|
|
1875
|
+
result = "PASS" if exit_code == 0 else "FAIL" if exit_code is not None else "UNAVAILABLE"
|
|
1876
|
+
connection = open_storage(root) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None)
|
|
1877
|
+
try:
|
|
1878
|
+
started = connection.execute(
|
|
1879
|
+
"SELECT started_at FROM execution_validation_command_invocations WHERE run_id=? AND command_id=?",
|
|
1880
|
+
(run_id, command_id),
|
|
1881
|
+
).fetchone()
|
|
1882
|
+
duration_ms = None
|
|
1883
|
+
if started and isinstance(started[0], str):
|
|
1884
|
+
try:
|
|
1885
|
+
duration_ms = max(0, round((datetime.fromisoformat(completed_at) - datetime.fromisoformat(started[0])).total_seconds() * 1000))
|
|
1886
|
+
except ValueError:
|
|
1887
|
+
pass
|
|
1888
|
+
connection.execute(
|
|
1889
|
+
"INSERT OR IGNORE INTO execution_validation_command_terminals("
|
|
1890
|
+
"run_id,command_id,completed_at,duration_ms,exit_code,result,evidence_ref) VALUES(?,?,?,?,?,?,?)",
|
|
1891
|
+
(run_id, command_id, completed_at, duration_ms, exit_code, result, evidence_ref),
|
|
1892
|
+
)
|
|
1893
|
+
finally:
|
|
1894
|
+
connection.close()
|
|
1895
|
+
|
|
1896
|
+
|
|
1897
|
+
def load_validation_context(root: Path, run_id: str, *, central_database: Path | None = None) -> dict[str, object] | None:
|
|
1898
|
+
"""Return the resolved profile and current control evidence without inference."""
|
|
1899
|
+
connection = open_storage(root) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None)
|
|
1900
|
+
try:
|
|
1901
|
+
profile = connection.execute(
|
|
1902
|
+
"SELECT selected_validation_tier,validation_profile_version,required_validation_controls,recorded_at "
|
|
1903
|
+
"FROM execution_validation_profiles WHERE run_id=?", (run_id,)
|
|
1904
|
+
).fetchone()
|
|
1905
|
+
rows = connection.execute(
|
|
1906
|
+
"SELECT validation_id,category,control_identity,required_for_profile,execution_status,result,evidence_ref,observed_at,currentness "
|
|
1907
|
+
"FROM execution_validation_control_results WHERE run_id=? ORDER BY id", (run_id,)
|
|
1908
|
+
).fetchall()
|
|
1909
|
+
command_rows = connection.execute(
|
|
1910
|
+
"SELECT inv.validation_id,inv.command_id,inv.category,inv.control_identity,inv.required_for_profile,"
|
|
1911
|
+
"inv.started_at,inv.currentness,term.completed_at,term.duration_ms,term.exit_code,term.result,term.evidence_ref "
|
|
1912
|
+
"FROM execution_validation_command_invocations inv "
|
|
1913
|
+
"LEFT JOIN execution_validation_command_terminals term "
|
|
1914
|
+
"ON term.run_id=inv.run_id AND term.command_id=inv.command_id "
|
|
1915
|
+
"WHERE inv.run_id=? ORDER BY inv.started_at", (run_id,)
|
|
1916
|
+
).fetchall()
|
|
1917
|
+
diagnostic_artifacts = {
|
|
1918
|
+
row[0]: row[1] for row in connection.execute(
|
|
1919
|
+
"SELECT artifact_id,execution_id FROM execution_artifact_records "
|
|
1920
|
+
"WHERE artifact_type='VALIDATION_FAILURE_DIAGNOSTIC' AND projection_status='AVAILABLE'"
|
|
1921
|
+
).fetchall()
|
|
1922
|
+
}
|
|
1923
|
+
finally:
|
|
1924
|
+
connection.close()
|
|
1925
|
+
if profile is None:
|
|
1926
|
+
return None
|
|
1927
|
+
try:
|
|
1928
|
+
payload = json.loads(profile[2])
|
|
1929
|
+
required = payload.get("validation_ids", [])
|
|
1930
|
+
except (TypeError, json.JSONDecodeError, AttributeError) as error:
|
|
1931
|
+
raise EngineeringStorageError("Validation profile controls are corrupt.") from error
|
|
1932
|
+
if not isinstance(required, list) or not all(isinstance(item, str) for item in required):
|
|
1933
|
+
raise EngineeringStorageError("Validation profile controls are invalid.")
|
|
1934
|
+
bindings = payload.get("control_bindings", [])
|
|
1935
|
+
if not isinstance(bindings, list) or any(not isinstance(binding, dict) for binding in bindings):
|
|
1936
|
+
raise EngineeringStorageError("Validation profile bindings are invalid.")
|
|
1937
|
+
if bindings and tuple(binding.get("validation_id") for binding in bindings) != tuple(required):
|
|
1938
|
+
raise EngineeringStorageError("Validation profile bindings are invalid.")
|
|
1939
|
+
controls: dict[str, dict[str, object]] = {}
|
|
1940
|
+
for row in rows:
|
|
1941
|
+
validation_id, category, identity, is_required, status, result, evidence_ref, observed_at, currentness = row
|
|
1942
|
+
current = controls.get(validation_id)
|
|
1943
|
+
if current is None or int(currentness) > int(current["currentness"]):
|
|
1944
|
+
controls[validation_id] = {"validation_id": validation_id, "category": category, "control_identity": identity,
|
|
1945
|
+
"required_for_profile": bool(is_required), "execution_status": status, "result": result,
|
|
1946
|
+
"evidence_ref": evidence_ref, "observed_at": observed_at, "currentness": currentness}
|
|
1947
|
+
elif int(currentness) == int(current["currentness"]) and result != current["result"]:
|
|
1948
|
+
controls[validation_id] = {**current, "result": "UNRESOLVED", "conflict": True}
|
|
1949
|
+
for row in command_rows:
|
|
1950
|
+
validation_id, command_id, category, identity, is_required, started_at, currentness, completed_at, duration_ms, exit_code, result, terminal_ref = row
|
|
1951
|
+
diagnostic_artifact_id = f"validation-failure-diagnostic-{command_id}"
|
|
1952
|
+
diagnostic_execution_id = diagnostic_artifacts.get(diagnostic_artifact_id)
|
|
1953
|
+
controls[validation_id] = {
|
|
1954
|
+
"validation_id": validation_id, "category": category, "control_identity": identity,
|
|
1955
|
+
"required_for_profile": bool(is_required), "execution_status": "EXECUTED",
|
|
1956
|
+
"result": result or "UNAVAILABLE", "evidence_ref": terminal_ref if completed_at else "command_invocation",
|
|
1957
|
+
"observed_at": completed_at or started_at, "currentness": currentness,
|
|
1958
|
+
"started_at": started_at, "ended_at": completed_at, "duration_ms": duration_ms, "exit_code": exit_code,
|
|
1959
|
+
# The artifact id is deterministically derived from the immutable
|
|
1960
|
+
# command receipt and the writer persists that command as the
|
|
1961
|
+
# artifact execution identity. Older unbound artifacts remain
|
|
1962
|
+
# historical only; projections never backfill their association.
|
|
1963
|
+
"diagnostic_evidence_ref": f"artifact:{diagnostic_artifact_id}" if diagnostic_execution_id == command_id else "UNAVAILABLE",
|
|
1964
|
+
}
|
|
1965
|
+
return {"selected_validation_tier": profile[0], "validation_profile_version": profile[1],
|
|
1966
|
+
"profile_reference": payload.get("profile_reference", "UNAVAILABLE"),
|
|
1967
|
+
"profile_selection_source": payload.get("profile_selection_source", "UNAVAILABLE"),
|
|
1968
|
+
"required_validation_controls": tuple(required), "control_bindings": tuple(bindings),
|
|
1969
|
+
"recorded_at": profile[3], "controls": controls}
|
|
1970
|
+
|
|
1971
|
+
|
|
1972
|
+
def record_run_qualification_snapshot(root: Path, snapshot: dict[str, object]) -> dict[str, object]:
|
|
1973
|
+
"""Store the first authoritative qualification projection for one run.
|
|
1974
|
+
|
|
1975
|
+
This is intentionally insert-only: terminal reporting, receipts and the
|
|
1976
|
+
dashboard all consume this same snapshot after it has been persisted.
|
|
1977
|
+
"""
|
|
1978
|
+
run_id = snapshot.get("run_id")
|
|
1979
|
+
snapshot_id = snapshot.get("qualification_snapshot_id")
|
|
1980
|
+
required_ref = snapshot.get("required_control_snapshot_ref")
|
|
1981
|
+
terminal_ref = snapshot.get("terminal_checkpoint_ref")
|
|
1982
|
+
persisted_at = snapshot.get("persisted_at")
|
|
1983
|
+
if not all(isinstance(value, str) and value for value in (run_id, snapshot_id, required_ref, terminal_ref, persisted_at)):
|
|
1984
|
+
raise EngineeringStorageError("Run qualification snapshot identity is invalid.")
|
|
1985
|
+
if snapshot.get("required_validation_state") not in {"PASS", "FAIL", "UNRESOLVED"}:
|
|
1986
|
+
raise EngineeringStorageError("Run qualification snapshot validation state is invalid.")
|
|
1987
|
+
if snapshot.get("cleanup_outcome") not in {"COMPLETED", "NOT_REQUIRED", "FAILED", "UNAVAILABLE"}:
|
|
1988
|
+
raise EngineeringStorageError("Run qualification snapshot cleanup outcome is invalid.")
|
|
1989
|
+
if snapshot.get("run_qualification") not in {"QUALIFIED", "NOT_QUALIFIED"}:
|
|
1990
|
+
raise EngineeringStorageError("Run qualification snapshot outcome is invalid.")
|
|
1991
|
+
if snapshot.get("run_qualification") == "QUALIFIED" and snapshot.get("terminal_execution_state") != "COMPLETE":
|
|
1992
|
+
raise EngineeringStorageError("A non-complete terminal run cannot be qualified.")
|
|
1993
|
+
payload = json.dumps(snapshot, sort_keys=True, separators=(",", ":"))
|
|
1994
|
+
connection = open_storage(root)
|
|
1995
|
+
try:
|
|
1996
|
+
connection.execute(
|
|
1997
|
+
"INSERT OR IGNORE INTO execution_run_qualification_snapshots("
|
|
1998
|
+
"run_id,snapshot_version,snapshot_id,required_control_snapshot_ref,terminal_checkpoint_ref,payload,persisted_at) "
|
|
1999
|
+
"VALUES(?,?,?,?,?,?,?)",
|
|
2000
|
+
(run_id, 1, snapshot_id, required_ref, terminal_ref, payload, persisted_at),
|
|
2001
|
+
)
|
|
2002
|
+
finally:
|
|
2003
|
+
connection.close()
|
|
2004
|
+
stored = load_run_qualification_snapshot(root, run_id)
|
|
2005
|
+
if stored is None:
|
|
2006
|
+
raise EngineeringStorageError("Run qualification snapshot was not persisted.")
|
|
2007
|
+
return stored
|
|
2008
|
+
|
|
2009
|
+
|
|
2010
|
+
def load_run_qualification_snapshot(
|
|
2011
|
+
root: Path, run_id: str, *, central_database: Path | None = None,
|
|
2012
|
+
) -> dict[str, object] | None:
|
|
2013
|
+
"""Load a persisted snapshot without deriving or backfilling legacy runs."""
|
|
2014
|
+
connection = open_storage(root) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None)
|
|
2015
|
+
try:
|
|
2016
|
+
row = connection.execute(
|
|
2017
|
+
"SELECT payload FROM execution_run_qualification_snapshots WHERE run_id=?", (run_id,)
|
|
2018
|
+
).fetchone()
|
|
2019
|
+
except sqlite3.OperationalError:
|
|
2020
|
+
# A supported historical database can legitimately predate v36.
|
|
2021
|
+
return None
|
|
2022
|
+
finally:
|
|
2023
|
+
connection.close()
|
|
2024
|
+
if row is None:
|
|
2025
|
+
return None
|
|
2026
|
+
try:
|
|
2027
|
+
payload = json.loads(row[0])
|
|
2028
|
+
except (TypeError, json.JSONDecodeError) as error:
|
|
2029
|
+
raise EngineeringStorageError("Run qualification snapshot is corrupt.") from error
|
|
2030
|
+
if not isinstance(payload, dict) or payload.get("run_id") != run_id:
|
|
2031
|
+
raise EngineeringStorageError("Run qualification snapshot is invalid.")
|
|
2032
|
+
return payload
|
|
2033
|
+
|
|
2034
|
+
|
|
2035
|
+
def record_artifact(
|
|
2036
|
+
root: Path,
|
|
2037
|
+
path: Path,
|
|
2038
|
+
*,
|
|
2039
|
+
artifact_id: str,
|
|
2040
|
+
artifact_type: str,
|
|
2041
|
+
content_type: str,
|
|
2042
|
+
created_at: str,
|
|
2043
|
+
run_id: str | None = None,
|
|
2044
|
+
submission_id: str | None = None,
|
|
2045
|
+
mission_id: str | None = None,
|
|
2046
|
+
execution_id: str | None = None,
|
|
2047
|
+
producer_id: str | None = None,
|
|
2048
|
+
projection_status: str = "AVAILABLE",
|
|
2049
|
+
central_database: Path | None = None,
|
|
2050
|
+
artifact_root: Path | None = None,
|
|
2051
|
+
) -> None:
|
|
2052
|
+
"""Register immutable filesystem payload metadata in the canonical store."""
|
|
2053
|
+
try:
|
|
2054
|
+
base = artifact_root.resolve() if artifact_root is not None else (root / ".engineering").resolve()
|
|
2055
|
+
location = str(path.resolve().relative_to(base))
|
|
2056
|
+
digest = hashlib.sha256(path.read_bytes()).hexdigest()
|
|
2057
|
+
except (OSError, ValueError) as error:
|
|
2058
|
+
raise EngineeringStorageError("Artifact payload cannot be recorded safely.") from error
|
|
2059
|
+
connection = open_storage(root) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None)
|
|
2060
|
+
try:
|
|
2061
|
+
if run_id and not connection.execute("SELECT 1 FROM execution_runs WHERE run_id=?", (run_id,)).fetchone():
|
|
2062
|
+
run_id = None
|
|
2063
|
+
if submission_id and not connection.execute("SELECT 1 FROM execution_submissions WHERE submission_id=?", (submission_id,)).fetchone():
|
|
2064
|
+
submission_id = None
|
|
2065
|
+
connection.execute(
|
|
2066
|
+
"INSERT INTO execution_artifact_records(artifact_id,artifact_type,digest_algorithm,digest,content_type,run_id,submission_id,mission_id,execution_id,producer_id,created_at,integrity_status,storage_location,projection_status) "
|
|
2067
|
+
"VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?) "
|
|
2068
|
+
"ON CONFLICT(artifact_id) DO UPDATE SET digest=excluded.digest,integrity_status=excluded.integrity_status,projection_status=excluded.projection_status",
|
|
2069
|
+
(artifact_id, artifact_type, "sha256", digest, content_type, run_id, submission_id, mission_id,
|
|
2070
|
+
execution_id, producer_id, created_at, "VERIFIED", location, projection_status),
|
|
2071
|
+
)
|
|
2072
|
+
finally:
|
|
2073
|
+
connection.close()
|
|
2074
|
+
|
|
2075
|
+
|
|
2076
|
+
def verify_artifact_integrity(
|
|
2077
|
+
root: Path, artifact_id: str, *, central_database: Path | None = None,
|
|
2078
|
+
artifact_root: Path | None = None,
|
|
2079
|
+
) -> bool:
|
|
2080
|
+
"""Verify a registered payload without making the file itself authoritative."""
|
|
2081
|
+
connection = open_storage(root) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None)
|
|
2082
|
+
try:
|
|
2083
|
+
row = connection.execute(
|
|
2084
|
+
"SELECT digest_algorithm,digest,storage_location FROM execution_artifact_records WHERE artifact_id=?", (artifact_id,)
|
|
2085
|
+
).fetchone()
|
|
2086
|
+
if not row:
|
|
2087
|
+
return False
|
|
2088
|
+
algorithm, expected, location = row
|
|
2089
|
+
if algorithm != "sha256" or not isinstance(location, str):
|
|
2090
|
+
return False
|
|
2091
|
+
try:
|
|
2092
|
+
authority_root = artifact_root.resolve() if artifact_root is not None else (root / ".engineering").resolve()
|
|
2093
|
+
payload = (authority_root / location).resolve()
|
|
2094
|
+
payload.relative_to(authority_root)
|
|
2095
|
+
actual = hashlib.sha256(payload.read_bytes()).hexdigest()
|
|
2096
|
+
except (OSError, ValueError):
|
|
2097
|
+
actual = ""
|
|
2098
|
+
connection.execute(
|
|
2099
|
+
"UPDATE execution_artifact_records SET integrity_status=? WHERE artifact_id=?",
|
|
2100
|
+
("VERIFIED" if actual == expected else "MISMATCH", artifact_id),
|
|
2101
|
+
)
|
|
2102
|
+
return actual == expected
|
|
2103
|
+
finally:
|
|
2104
|
+
connection.close()
|
|
2105
|
+
|
|
2106
|
+
|
|
2107
|
+
def regenerate_status_projections(root: Path) -> None:
|
|
2108
|
+
"""Regenerate compatibility JSON copies from canonical state deterministically."""
|
|
2109
|
+
status_directory = root / ".engineering" / "status"
|
|
2110
|
+
status_directory.mkdir(mode=0o700, parents=True, exist_ok=True)
|
|
2111
|
+
for name, filename in (("watcher_status", "status.json"), ("live_status", "current.json")):
|
|
2112
|
+
payload = load_projection(root, name)
|
|
2113
|
+
if payload is None:
|
|
2114
|
+
continue
|
|
2115
|
+
temporary = status_directory / f".{filename}.tmp"
|
|
2116
|
+
temporary.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
|
2117
|
+
temporary.replace(status_directory / filename)
|
|
2118
|
+
|
|
2119
|
+
|
|
2120
|
+
def import_legacy_projection_once(root: Path, name: str, path: Path) -> dict[str, object] | None:
|
|
2121
|
+
"""Explicit, idempotent compatibility migration for a pre-v12 projection.
|
|
2122
|
+
|
|
2123
|
+
It is only used when the canonical row is absent. Subsequent reads remain
|
|
2124
|
+
database-only, and a changed source fails closed rather than overwriting
|
|
2125
|
+
the imported operational record.
|
|
2126
|
+
"""
|
|
2127
|
+
existing = load_projection(root, name)
|
|
2128
|
+
if existing is not None:
|
|
2129
|
+
return existing
|
|
2130
|
+
legacy = _legacy_payload(path)
|
|
2131
|
+
if legacy is None:
|
|
2132
|
+
return None
|
|
2133
|
+
payload, digest = legacy
|
|
2134
|
+
# A transient Codex action title belongs only to the current local status
|
|
2135
|
+
# file. It must never be promoted into canonical storage during a legacy
|
|
2136
|
+
# projection import.
|
|
2137
|
+
if name == "live_status":
|
|
2138
|
+
payload.pop("transient_action", None)
|
|
2139
|
+
connection = open_storage(root)
|
|
2140
|
+
try:
|
|
2141
|
+
prior = connection.execute(
|
|
2142
|
+
"SELECT source_digest FROM execution_migration_provenance WHERE source_location=?", (str(path),)
|
|
2143
|
+
).fetchone()
|
|
2144
|
+
if prior and prior[0] != digest:
|
|
2145
|
+
raise EngineeringStorageError("Legacy projection conflicts with completed canonical migration.")
|
|
2146
|
+
store_projection(connection, name, payload, classification="PROJECTION")
|
|
2147
|
+
connection.execute(
|
|
2148
|
+
"INSERT OR IGNORE INTO execution_migration_provenance(source_location,source_digest,imported_at,record_kind) VALUES(?,?,?,?)",
|
|
2149
|
+
(str(path), digest, datetime.now(timezone.utc).isoformat(), "PROJECTION"),
|
|
2150
|
+
)
|
|
2151
|
+
finally:
|
|
2152
|
+
connection.close()
|
|
2153
|
+
return payload
|
|
2154
|
+
|
|
2155
|
+
|
|
2156
|
+
def database_path(root: Path) -> Path:
|
|
2157
|
+
"""Resolve the sole EP authority; corrupt authority control fails closed."""
|
|
2158
|
+
central = os.environ.get(CENTRAL_OPERATIONAL_DATABASE_ENVIRONMENT)
|
|
2159
|
+
if central:
|
|
2160
|
+
path = Path(central).expanduser().resolve()
|
|
2161
|
+
if path.name != DATABASE_FILENAME or not path.is_file():
|
|
2162
|
+
raise EngineeringStorageError("CENTRAL operational database is unavailable.")
|
|
2163
|
+
return path
|
|
2164
|
+
legacy = root.resolve() / WORKSPACE_DIRECTORY / DATABASE_FILENAME
|
|
2165
|
+
pointer = _authority_pointer_path()
|
|
2166
|
+
if not pointer.exists():
|
|
2167
|
+
return legacy
|
|
2168
|
+
try:
|
|
2169
|
+
payload = json.loads(pointer.read_text(encoding="utf-8"))
|
|
2170
|
+
if not isinstance(payload, dict) or payload.get("version") != 1:
|
|
2171
|
+
raise ValueError("invalid version")
|
|
2172
|
+
path = Path(str(payload["authoritative_path"])).resolve()
|
|
2173
|
+
schema = int(payload["schema"])
|
|
2174
|
+
fingerprint = str(payload["fingerprint_sha256"])
|
|
2175
|
+
if schema != ENGINEERING_STORAGE_SCHEMA_VERSION or not path.is_file() or len(fingerprint) != 64:
|
|
2176
|
+
raise ValueError("invalid authority")
|
|
2177
|
+
return path
|
|
2178
|
+
except (OSError, ValueError, TypeError, KeyError, json.JSONDecodeError) as error:
|
|
2179
|
+
raise EngineeringStorageError("Engineering store authority pointer is invalid.") from error
|
|
2180
|
+
|
|
2181
|
+
|
|
2182
|
+
def legacy_database_path(root: Path) -> Path:
|
|
2183
|
+
"""Return the pre-cutover path without interpreting authority control."""
|
|
2184
|
+
return root.resolve() / WORKSPACE_DIRECTORY / DATABASE_FILENAME
|
|
2185
|
+
|
|
2186
|
+
|
|
2187
|
+
def _authority_pointer_path() -> Path:
|
|
2188
|
+
if os.name == "nt":
|
|
2189
|
+
base = os.environ.get("LOCALAPPDATA") or os.environ.get("APPDATA")
|
|
2190
|
+
root = Path(base) / "Engineering Platform" if base else Path.home() / "AppData" / "Local" / "Engineering Platform"
|
|
2191
|
+
elif sys.platform == "darwin":
|
|
2192
|
+
root = Path.home() / "Library" / "Application Support" / "Engineering Platform"
|
|
2193
|
+
else:
|
|
2194
|
+
root = Path(os.environ.get("XDG_DATA_HOME", Path.home() / ".local" / "share")) / "Engineering Platform"
|
|
2195
|
+
return root / "runtime" / STORE_AUTHORITY_POINTER
|
|
2196
|
+
|
|
2197
|
+
|
|
2198
|
+
def storage_activation_required(root: Path) -> bool:
|
|
2199
|
+
"""Read whether an existing shared store is behind this source's schema.
|
|
2200
|
+
|
|
2201
|
+
This deliberately opens SQLite read-only and never invokes a migration. It
|
|
2202
|
+
is used only to explain why a persistent component cannot start.
|
|
2203
|
+
"""
|
|
2204
|
+
path = database_path(root)
|
|
2205
|
+
if not path.is_file():
|
|
2206
|
+
return False
|
|
2207
|
+
try:
|
|
2208
|
+
connection = sqlite3.connect(f"file:{path}?mode=ro", uri=True)
|
|
2209
|
+
try:
|
|
2210
|
+
return _schema_version(connection) < ENGINEERING_STORAGE_SCHEMA_VERSION
|
|
2211
|
+
finally:
|
|
2212
|
+
connection.close()
|
|
2213
|
+
except (OSError, sqlite3.DatabaseError, EngineeringStorageError):
|
|
2214
|
+
return False
|
|
2215
|
+
|
|
2216
|
+
|
|
2217
|
+
def _assert_controlled_schema_activation(root: Path, path: Path) -> None:
|
|
2218
|
+
"""Refuse an upgrade while any EP execution or durable component is live.
|
|
2219
|
+
|
|
2220
|
+
This is intentionally independent of the execution-host environment. A
|
|
2221
|
+
provider child can otherwise escape its admission environment and import
|
|
2222
|
+
newer source against the shared database directly.
|
|
2223
|
+
"""
|
|
2224
|
+
if path.is_file():
|
|
2225
|
+
try:
|
|
2226
|
+
connection = sqlite3.connect(f"file:{path}?mode=ro", uri=True)
|
|
2227
|
+
try:
|
|
2228
|
+
tables = {
|
|
2229
|
+
str(row[0])
|
|
2230
|
+
for row in connection.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
|
2231
|
+
}
|
|
2232
|
+
if "execution_run_leases" in tables:
|
|
2233
|
+
active = connection.execute(
|
|
2234
|
+
"SELECT 1 FROM execution_run_leases WHERE lease_state='ACTIVE' AND expires_at>=? LIMIT 1",
|
|
2235
|
+
(datetime.now(timezone.utc).isoformat(),),
|
|
2236
|
+
).fetchone()
|
|
2237
|
+
if active is not None:
|
|
2238
|
+
raise EngineeringStorageError(
|
|
2239
|
+
"Engineering storage activation requires no active execution lease."
|
|
2240
|
+
)
|
|
2241
|
+
if "engineering_transactions" in tables:
|
|
2242
|
+
active_transaction = connection.execute(
|
|
2243
|
+
"SELECT 1 FROM engineering_transactions "
|
|
2244
|
+
"WHERE phase NOT IN ('COMPLETE','BLOCKED','FAILED') LIMIT 1"
|
|
2245
|
+
).fetchone()
|
|
2246
|
+
if active_transaction is not None:
|
|
2247
|
+
raise EngineeringStorageError(
|
|
2248
|
+
"Engineering storage activation requires no non-terminal execution."
|
|
2249
|
+
)
|
|
2250
|
+
finally:
|
|
2251
|
+
connection.close()
|
|
2252
|
+
except sqlite3.DatabaseError as error:
|
|
2253
|
+
raise EngineeringStorageError("Engineering storage activation cannot inspect the shared database.") from error
|
|
2254
|
+
def _schema_version(connection: sqlite3.Connection) -> int:
|
|
2255
|
+
tables = {
|
|
2256
|
+
str(row[0])
|
|
2257
|
+
for row in connection.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
|
2258
|
+
}
|
|
2259
|
+
if "engineering_schema_migrations" not in tables:
|
|
2260
|
+
if "ep_metadata" in tables:
|
|
2261
|
+
return 0
|
|
2262
|
+
if tables:
|
|
2263
|
+
raise EngineeringStorageError("Engineering storage has no recognized schema history.")
|
|
2264
|
+
return 0
|
|
2265
|
+
versions = [int(row[0]) for row in connection.execute("SELECT version FROM engineering_schema_migrations")]
|
|
2266
|
+
return max(versions, default=0)
|
|
2267
|
+
|
|
2268
|
+
|
|
2269
|
+
def open_storage(
|
|
2270
|
+
root: Path, *, create: bool = True, journal_mode: str = "DELETE", allow_schema_upgrade: bool = False
|
|
2271
|
+
) -> sqlite3.Connection:
|
|
2272
|
+
"""Open, upgrade and validate the private SQLite evidence database.
|
|
2273
|
+
|
|
2274
|
+
New private stores are initialized transactionally. An existing shared
|
|
2275
|
+
store never upgrades implicitly: version changes require
|
|
2276
|
+
:func:`activate_storage_schema` after all persistent EP components have
|
|
2277
|
+
stopped. SQLite rollback-journal mode intentionally avoids persistent WAL
|
|
2278
|
+
sidecars in `.engineering`. Background best-effort writers may request an
|
|
2279
|
+
in-memory journal so their temporary transaction files cannot race
|
|
2280
|
+
workspace cleanup.
|
|
2281
|
+
"""
|
|
2282
|
+
if journal_mode not in JOURNAL_MODES:
|
|
2283
|
+
raise ValueError("Unsupported SQLite journal mode.")
|
|
2284
|
+
path = database_path(root)
|
|
2285
|
+
new_store = not path.exists()
|
|
2286
|
+
if create:
|
|
2287
|
+
path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
|
|
2288
|
+
elif not path.parent.is_dir():
|
|
2289
|
+
raise EngineeringStorageError("Engineering storage is unavailable.")
|
|
2290
|
+
# Best-effort consumers (telemetry) open in read/write-existing mode. This
|
|
2291
|
+
# prevents a delayed worker from recreating a database while a workspace is
|
|
2292
|
+
# being removed.
|
|
2293
|
+
try:
|
|
2294
|
+
connection = sqlite3.connect(
|
|
2295
|
+
path if create else f"file:{path}?mode=rw",
|
|
2296
|
+
timeout=10,
|
|
2297
|
+
isolation_level=None,
|
|
2298
|
+
uri=not create,
|
|
2299
|
+
)
|
|
2300
|
+
except sqlite3.DatabaseError as error:
|
|
2301
|
+
raise EngineeringStorageError("Engineering storage could not be opened safely.") from error
|
|
2302
|
+
try:
|
|
2303
|
+
connection.execute("PRAGMA foreign_keys=ON")
|
|
2304
|
+
connection.execute("PRAGMA busy_timeout=10000")
|
|
2305
|
+
connection.execute(f"PRAGMA journal_mode={journal_mode}")
|
|
2306
|
+
current = _schema_version(connection)
|
|
2307
|
+
central = bool(os.environ.get(CENTRAL_OPERATIONAL_DATABASE_ENVIRONMENT))
|
|
2308
|
+
if current > ENGINEERING_STORAGE_SCHEMA_VERSION and not central:
|
|
2309
|
+
raise EngineeringStorageError(
|
|
2310
|
+
"Engineering storage schema is newer than this Engineering Platform supports."
|
|
2311
|
+
)
|
|
2312
|
+
admitted_ceiling = _admitted_migration_ceiling(root)
|
|
2313
|
+
if (
|
|
2314
|
+
admitted_ceiling is not None
|
|
2315
|
+
and current < ENGINEERING_STORAGE_SCHEMA_VERSION
|
|
2316
|
+
and ENGINEERING_STORAGE_SCHEMA_VERSION > admitted_ceiling
|
|
2317
|
+
):
|
|
2318
|
+
raise EngineeringStorageError(
|
|
2319
|
+
"Engineering storage migration is deferred until the Execution Host is upgraded."
|
|
2320
|
+
)
|
|
2321
|
+
if current < ENGINEERING_STORAGE_SCHEMA_VERSION and not (new_store or allow_schema_upgrade):
|
|
2322
|
+
raise EngineeringStorageError(
|
|
2323
|
+
"Engineering storage migration requires controlled post-merge activation."
|
|
2324
|
+
)
|
|
2325
|
+
connection.execute("BEGIN IMMEDIATE")
|
|
2326
|
+
for version in range(current + 1, ENGINEERING_STORAGE_SCHEMA_VERSION + 1):
|
|
2327
|
+
migration = MIGRATIONS.get(version)
|
|
2328
|
+
if migration is None:
|
|
2329
|
+
raise EngineeringStorageError(f"Engineering storage migration {version} is unavailable.")
|
|
2330
|
+
migration(connection)
|
|
2331
|
+
connection.execute(
|
|
2332
|
+
"INSERT INTO engineering_schema_migrations(version) VALUES(?)", (version,)
|
|
2333
|
+
)
|
|
2334
|
+
_import_legacy_execution_dismissals(root, connection)
|
|
2335
|
+
connection.execute("COMMIT")
|
|
2336
|
+
path.chmod(0o600)
|
|
2337
|
+
except (OSError, sqlite3.DatabaseError, EngineeringStorageError) as error:
|
|
2338
|
+
try:
|
|
2339
|
+
connection.execute("ROLLBACK")
|
|
2340
|
+
except sqlite3.DatabaseError:
|
|
2341
|
+
pass
|
|
2342
|
+
connection.close()
|
|
2343
|
+
if isinstance(error, EngineeringStorageError):
|
|
2344
|
+
raise
|
|
2345
|
+
raise EngineeringStorageError("Engineering storage could not be opened safely.") from error
|
|
2346
|
+
return connection
|
|
2347
|
+
|
|
2348
|
+
|
|
2349
|
+
def install_central_operational_compatibility_schema(connection: sqlite3.Connection) -> None:
|
|
2350
|
+
"""Install retained-runner tables in the already-versioned CENTRAL DB.
|
|
2351
|
+
|
|
2352
|
+
The historical action state machine is intentionally preserved during the
|
|
2353
|
+
cutover. Its tables are installed *inside* CENTRAL, never copied from a
|
|
2354
|
+
repository and never used to create a project-local database. The
|
|
2355
|
+
Server's schema migration owns the outer transaction and schema version.
|
|
2356
|
+
"""
|
|
2357
|
+
# Schema-41 introduced a deliberately minimal table with this name. The
|
|
2358
|
+
# retained runner's schema-5 history index has a different contract, so
|
|
2359
|
+
# preserve the bootstrap evidence under an explicit historical name before
|
|
2360
|
+
# installing the full CENTRAL-owned operational index.
|
|
2361
|
+
columns = {
|
|
2362
|
+
str(row[1])
|
|
2363
|
+
for row in connection.execute("PRAGMA table_info(prompt_execution_history)")
|
|
2364
|
+
}
|
|
2365
|
+
if columns and "terminal_state" not in columns:
|
|
2366
|
+
connection.execute(
|
|
2367
|
+
"ALTER TABLE prompt_execution_history RENAME TO ep_bootstrap_prompt_history"
|
|
2368
|
+
)
|
|
2369
|
+
server_owned_tables = {
|
|
2370
|
+
str(row[0]) for row in connection.execute("SELECT name FROM sqlite_master WHERE type='table'")
|
|
2371
|
+
}
|
|
2372
|
+
for version in range(1, ENGINEERING_STORAGE_SCHEMA_VERSION + 1):
|
|
2373
|
+
# Server schema 53 owns the credential namespace transfer inside its
|
|
2374
|
+
# own atomic migration. The retained runner compatibility installer
|
|
2375
|
+
# must never create a competing destination table first.
|
|
2376
|
+
if version == 41 and "ep_installations" in server_owned_tables:
|
|
2377
|
+
continue
|
|
2378
|
+
MIGRATIONS[version](connection)
|
|
2379
|
+
|
|
2380
|
+
|
|
2381
|
+
def activate_storage_schema(root: Path) -> sqlite3.Connection:
|
|
2382
|
+
"""Upgrade a shared EP database only at a controlled post-merge boundary."""
|
|
2383
|
+
root = root.resolve()
|
|
2384
|
+
path = database_path(root)
|
|
2385
|
+
_assert_controlled_schema_activation(root, path)
|
|
2386
|
+
return open_storage(root, allow_schema_upgrade=True)
|
|
2387
|
+
|
|
2388
|
+
|
|
2389
|
+
def main(argv: list[str] | None = None) -> int:
|
|
2390
|
+
"""Run the narrow, operator-owned shared-storage activation command."""
|
|
2391
|
+
parser = argparse.ArgumentParser(prog="engineering-storage")
|
|
2392
|
+
parser.add_argument("command", choices=("activate",))
|
|
2393
|
+
parser.add_argument("--repo", type=Path, default=Path.cwd())
|
|
2394
|
+
args = parser.parse_args(argv)
|
|
2395
|
+
try:
|
|
2396
|
+
with activate_storage_schema(args.repo.resolve()) as connection:
|
|
2397
|
+
version = connection.execute("SELECT MAX(version) FROM engineering_schema_migrations").fetchone()[0]
|
|
2398
|
+
except EngineeringStorageError as error:
|
|
2399
|
+
print(f"BLOCKED: {error}")
|
|
2400
|
+
return 2
|
|
2401
|
+
print(f"ACTIVATED: engineering storage schema {version}")
|
|
2402
|
+
return 0
|
|
2403
|
+
|
|
2404
|
+
|
|
2405
|
+
def record_readiness_evaluation(root: Path, *, run_id: str, profile_id: str, profile_version: int,
|
|
2406
|
+
execution_mode: str, passed: bool, failed_requirements: tuple[str, ...],
|
|
2407
|
+
facts: dict[str, object], evaluated_at: str, diagnostic: str | None,
|
|
2408
|
+
central_database: Path | None = None) -> None:
|
|
2409
|
+
"""Store one deterministic readiness decision for a transaction."""
|
|
2410
|
+
connection = open_storage(root) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None)
|
|
2411
|
+
try:
|
|
2412
|
+
connection.execute(
|
|
2413
|
+
"INSERT INTO execution_readiness_evaluations(run_id,profile_id,profile_version,execution_mode,passed,failed_requirements,facts,evaluated_at,diagnostic) VALUES(?,?,?,?,?,?,?,?,?) "
|
|
2414
|
+
"ON CONFLICT(run_id) DO UPDATE SET profile_id=excluded.profile_id,profile_version=excluded.profile_version,execution_mode=excluded.execution_mode,passed=excluded.passed,failed_requirements=excluded.failed_requirements,facts=excluded.facts,evaluated_at=excluded.evaluated_at,diagnostic=excluded.diagnostic",
|
|
2415
|
+
(run_id, profile_id, profile_version, execution_mode, int(passed), json.dumps(failed_requirements), json.dumps(facts, sort_keys=True), evaluated_at, diagnostic),
|
|
2416
|
+
)
|
|
2417
|
+
finally:
|
|
2418
|
+
connection.close()
|
|
2419
|
+
|
|
2420
|
+
|
|
2421
|
+
def load_readiness_evaluation(
|
|
2422
|
+
root: Path, run_id: object, *, central_database: Path | None = None,
|
|
2423
|
+
) -> dict[str, object] | None:
|
|
2424
|
+
"""Read one canonical readiness projection for dashboard/report consumers."""
|
|
2425
|
+
if not isinstance(run_id, str):
|
|
2426
|
+
return None
|
|
2427
|
+
connection = open_storage(root) if central_database is None else sqlite3.connect(central_database.resolve(), isolation_level=None)
|
|
2428
|
+
try:
|
|
2429
|
+
row = connection.execute(
|
|
2430
|
+
"SELECT profile_id,profile_version,execution_mode,passed,failed_requirements,evaluated_at,diagnostic FROM execution_readiness_evaluations WHERE run_id=?", (run_id,)
|
|
2431
|
+
).fetchone()
|
|
2432
|
+
finally:
|
|
2433
|
+
connection.close()
|
|
2434
|
+
if not row:
|
|
2435
|
+
return None
|
|
2436
|
+
return {"profile_id": row[0], "profile_version": row[1], "execution_mode": row[2], "result": "PASS" if row[3] else "BLOCKED", "failed_requirements": json.loads(row[4]), "evaluated_at": row[5], "diagnostic": row[6]}
|
|
2437
|
+
|
|
2438
|
+
|
|
2439
|
+
if __name__ == "__main__":
|
|
2440
|
+
raise SystemExit(main())
|