From b38b4487bc393f9e71a7d4108253e5e62fa001b0 Mon Sep 17 00:00:00 2001 From: pcvantol Date: Sat, 12 Sep 2026 18:46:16 +0200 Subject: [PATCH 1/2] fix: project terminal revision timeline centrally --- docs/engineering/EXECUTION_HOST_OPERATIONS.md | 6 ++ .../P_CENTRAL_CONSOLE_AUTHORITY_MAP.md | 2 +- ...RANSPORT_CONSOLE_ROUTE_OWNERSHIP_MATRIX.md | 3 + package-lock.json | 4 +- package.json | 2 +- pyproject.toml | 2 +- .../ENGINEERING_PLATFORM_CONFIG.json | 2 +- .../ENGINEERING_PLATFORM_VERSION.json | 8 +-- src/engineering_platform/assets/dashboard.js | 1 + .../assets/dashboard_locales.mjs | 6 ++ src/engineering_platform/platform_version.py | 2 +- src/engineering_platform/server.py | 69 ++++++++++++++++++- .../templates/workspace-config.json | 2 +- tests/engineering/dashboard.spec.mjs | 20 ++++++ .../test_coverage_runtime_boundaries.py | 64 ++++++++++++++++- 15 files changed, 178 insertions(+), 15 deletions(-) diff --git a/docs/engineering/EXECUTION_HOST_OPERATIONS.md b/docs/engineering/EXECUTION_HOST_OPERATIONS.md index 76428f3b..1ef85934 100644 --- a/docs/engineering/EXECUTION_HOST_OPERATIONS.md +++ b/docs/engineering/EXECUTION_HOST_OPERATIONS.md @@ -296,6 +296,12 @@ checkpoint. The execution-details modal renders them chronologically in a bounded, vertically scrollable card beside AI-provider usage; missing evidence is shown as missing rather than reconstructed from report text. +For every terminal run, the Console may additionally show one **verified final +repository revision**. It is not a phase commit: the projection exists only +when the immutable terminal-evidence artifact passes integrity verification and +is bound to that exact project and run. This makes a successful no-change run +auditable without inventing an implementation or finalization commit. + - **Pull-request merge hand-off** is shown as a persistent, dashboard-native wait state with a direct GitHub link once required checks are green. Closing the browser does not cancel it: the watcher polls the persisted run and diff --git a/docs/engineering/P_CENTRAL_CONSOLE_AUTHORITY_MAP.md b/docs/engineering/P_CENTRAL_CONSOLE_AUTHORITY_MAP.md index d5b68dcb..9825fda0 100644 --- a/docs/engineering/P_CENTRAL_CONSOLE_AUTHORITY_MAP.md +++ b/docs/engineering/P_CENTRAL_CONSOLE_AUTHORITY_MAP.md @@ -14,7 +14,7 @@ physical execution binding only; it is not Console authority. | CENTRAL database controls | CENTRAL_NATIVE | Already served by `central_database`. | | Provider capacity | SERVER_PLATFORM_NATIVE | Already scoped to the installed runtime/CENTRAL policy. | | Selected-project queue | CENTRAL_NATIVE | `/api/dashboard-snapshot` reads CENTRAL submissions by `project_id`. | -| Active execution, history, lifecycle | CENTRAL_NATIVE (read projection) | Snapshot, prompt history and run detail resolve `(project_id, run_id)` from CENTRAL; both the active card and terminal detail receive the complete persisted step flow. | +| Active execution, history, lifecycle | CENTRAL_NATIVE (read projection) | Snapshot, prompt history and run detail resolve `(project_id, run_id)` from CENTRAL; both the active card and terminal detail receive the complete persisted step flow. Terminal detail may also show an integrity-verified final repository revision for that exact run, without treating it as a phase commit. | | Active execution diagnostic | CENTRAL_NATIVE (read projection) | `/api/execution-diagnostic/current` reads the active run's redacted component-log diagnostic and returns only `text/plain`; JSON is never a Console display contract. | | Telemetry and timing detail | CENTRAL_NATIVE (read projection) | Daily telemetry and day detail join CENTRAL telemetry to canonical project/run lineage. | | Provider usage | SERVER_PLATFORM_NATIVE | Current single-runtime Codex capacity is a Server/CENTRAL projection. | diff --git a/docs/engineering/P_TRANSPORT_CONSOLE_ROUTE_OWNERSHIP_MATRIX.md b/docs/engineering/P_TRANSPORT_CONSOLE_ROUTE_OWNERSHIP_MATRIX.md index 4d202cdd..3ed5ca07 100644 --- a/docs/engineering/P_TRANSPORT_CONSOLE_ROUTE_OWNERSHIP_MATRIX.md +++ b/docs/engineering/P_TRANSPORT_CONSOLE_ROUTE_OWNERSHIP_MATRIX.md @@ -52,6 +52,9 @@ state** is the FIFO dispatch record's orchestration state. They can both be The active projection and every terminal history detail include the full persisted lifecycle path (the read-only step-bubble flow) for that exact run. +Terminal detail additionally projects a final repository revision only when an +integrity-verified terminal-evidence artifact binds it to that same run; this +is labelled as a final revision rather than inferred as a phase commit. The active diagnostic endpoint reads only that run's redacted CENTRAL component-log diagnostics and responds as `text/plain`; absent, malformed, or JSON-shaped content becomes the localized unavailable state. Raw JSON error diff --git a/package-lock.json b/package-lock.json index 06192208..0a77d8a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "engineering-platform-browser-validation", - "version": "2.3.21", + "version": "2.3.22", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "engineering-platform-browser-validation", - "version": "2.3.21", + "version": "2.3.22", "devDependencies": { "@playwright/test": "1.62.1" } diff --git a/package.json b/package.json index 79737864..e0f98873 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "engineering-platform-browser-validation", "private": true, - "version": "2.3.21", + "version": "2.3.22", "scripts": { "test:engineering-dashboard": "PYTHONPATH=src python3 -m engineering_platform.dashboard_browser_validation", "test:engineering-dashboard-logic": "node --test tests/engineering/dashboard_status_store.test.mjs tests/engineering/ui_localization_contract.test.mjs", diff --git a/pyproject.toml b/pyproject.toml index f58fc1be..098fa9ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "engineering-platform" -version = "2.3.21" +version = "2.3.22" description = "Local-first Engineering Platform execution operations runtime" readme = "README.md" requires-python = ">=3.11" diff --git a/src/engineering_platform/ENGINEERING_PLATFORM_CONFIG.json b/src/engineering_platform/ENGINEERING_PLATFORM_CONFIG.json index 8fe4e9dc..c235d007 100644 --- a/src/engineering_platform/ENGINEERING_PLATFORM_CONFIG.json +++ b/src/engineering_platform/ENGINEERING_PLATFORM_CONFIG.json @@ -3,7 +3,7 @@ "platform": { "id": "engineering-platform", "name": "Engineering Platform", - "version": "2.3.21", + "version": "2.3.22", "generation": 2, "documentation_namespace": "engineering-platform", "capability_registry_version": 1 diff --git a/src/engineering_platform/ENGINEERING_PLATFORM_VERSION.json b/src/engineering_platform/ENGINEERING_PLATFORM_VERSION.json index b0bb8a04..16086bf6 100644 --- a/src/engineering_platform/ENGINEERING_PLATFORM_VERSION.json +++ b/src/engineering_platform/ENGINEERING_PLATFORM_VERSION.json @@ -1,15 +1,15 @@ { "bootstrap_contract": "2026.12", "checkpoint_format": 1, - "dashboard_version": "2.3.21", + "dashboard_version": "2.3.22", "handoff_protocol": 1, "memory_format": 2, "minimum_codex_cli": "0.146.0", "inbox_protocol": 1, - "platform_version": "2.3.21", + "platform_version": "2.3.22", "report_format": 2, - "runner_version": "2.3.21", + "runner_version": "2.3.22", "status_model": 1, "storage_schema": 44, - "watcher_version": "2.3.21" + "watcher_version": "2.3.22" } diff --git a/src/engineering_platform/assets/dashboard.js b/src/engineering_platform/assets/dashboard.js index eeb4171a..6b302c21 100644 --- a/src/engineering_platform/assets/dashboard.js +++ b/src/engineering_platform/assets/dashboard.js @@ -7694,6 +7694,7 @@ function promptDetailExecutionActivitySection(activity) { ], false, "prompt-detail-card--execution-activity"); } function commitTimelineKind(item) { + if (item.description === "terminal_repository_revision_verified") return "terminal_revision"; const mergeKinds = { implementation_merge_verified: "implementation_merge", finalization_merge_verified: "finalization_merge", diff --git a/src/engineering_platform/assets/dashboard_locales.mjs b/src/engineering_platform/assets/dashboard_locales.mjs index fed0a470..d375faed 100644 --- a/src/engineering_platform/assets/dashboard_locales.mjs +++ b/src/engineering_platform/assets/dashboard_locales.mjs @@ -4145,3 +4145,9 @@ Object.assign(DASHBOARD_MESSAGES.nl, {"status.running":"In uitvoering", "state.R Object.assign(DASHBOARD_MESSAGES.de, {"status.running":"Wird ausgeführt", "state.RUNNING":"Wird ausgeführt"}); Object.assign(DASHBOARD_MESSAGES.fr, {"status.running":"En cours", "state.RUNNING":"En cours"}); Object.assign(DASHBOARD_MESSAGES.es, {"status.running":"En ejecución", "state.RUNNING":"En ejecución"}); + +Object.assign(DASHBOARD_MESSAGES.en, {"detail.commit_type.terminal_revision":"Verified final revision","detail.commit_description.terminal_repository_revision_verified":"Final repository revision verified from immutable terminal evidence.","state.TERMINAL":"Execution outcome"}); +Object.assign(DASHBOARD_MESSAGES.nl, {"detail.commit_type.terminal_revision":"Geverifieerde eindrevisie","detail.commit_description.terminal_repository_revision_verified":"Eindrevisie van de repository geverifieerd uit onveranderlijke terminale uitvoeringsevidentie.","state.TERMINAL":"Uitvoeringsresultaat"}); +Object.assign(DASHBOARD_MESSAGES.de, {"detail.commit_type.terminal_revision":"Verifizierte Endrevision","detail.commit_description.terminal_repository_revision_verified":"Endrevision des Repositorys aus unveränderlicher terminaler Evidenz verifiziert.","state.TERMINAL":"Ausführungsergebnis"}); +Object.assign(DASHBOARD_MESSAGES.fr, {"detail.commit_type.terminal_revision":"Révision finale vérifiée","detail.commit_description.terminal_repository_revision_verified":"Révision finale du dépôt vérifiée à partir de la preuve terminale immuable.","state.TERMINAL":"Résultat d’exécution"}); +Object.assign(DASHBOARD_MESSAGES.es, {"detail.commit_type.terminal_revision":"Revisión final verificada","detail.commit_description.terminal_repository_revision_verified":"Revisión final del repositorio verificada a partir de evidencia terminal inmutable.","state.TERMINAL":"Resultado de ejecución"}); diff --git a/src/engineering_platform/platform_version.py b/src/engineering_platform/platform_version.py index 07360bc9..c2281be1 100644 --- a/src/engineering_platform/platform_version.py +++ b/src/engineering_platform/platform_version.py @@ -12,7 +12,7 @@ SEMVER = re.compile(r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$") CONTRACT = re.compile(r"^(\d{4})\.(0[1-9]|1[0-2])$") -CURRENT_PLATFORM_VERSION = "2.3.21" +CURRENT_PLATFORM_VERSION = "2.3.22" MANIFEST_FIELDS = frozenset( { "platform_version", diff --git a/src/engineering_platform/server.py b/src/engineering_platform/server.py index dfe3f4d2..e8d15602 100644 --- a/src/engineering_platform/server.py +++ b/src/engineering_platform/server.py @@ -2287,6 +2287,70 @@ def _central_console_lifecycle(data_root: Path, run_id: str) -> dict[str, object ) +def _central_console_terminal_revision_timeline( + data_root: Path, project_id: str, run_id: str, +) -> list[dict[str, str]]: + """Project an exact terminal artifact's verified repository revision. + + A successful no-change run has no phase commit to display, but its + integrity-verified terminal artifact can still attest the final repository + revision. This stays distinct from a phase commit: malformed or tampered + payloads never become Console evidence. + """ + try: + with sqlite3.connect(data_root / SERVER_DATABASE_FILENAME) as connection: + row = connection.execute( + """SELECT a.artifact_id,a.created_at + FROM execution_artifact_records AS a + JOIN ep_parity_lifecycle_dispatches AS d + ON a.ep_run_id=d.run_id OR a.run_id=d.run_id + WHERE d.project_id=? AND d.run_id=? + AND a.artifact_type='EP_TERMINAL_EVIDENCE' + AND a.integrity_status='VERIFIED' + ORDER BY a.created_at DESC,a.artifact_id DESC LIMIT 1""", + (project_id, run_id), + ).fetchone() + if row is None: + return [] + payload = submission_service.producer_evidence_artifact( + connection, project_id=project_id, artifact_id=str(row[0]), + ) + except sqlite3.Error: + return [] + if payload is None: + return [] + try: + evidence = json.loads(payload) + except (UnicodeDecodeError, json.JSONDecodeError): + return [] + if not isinstance(evidence, Mapping) or evidence.get("artifact_type") != "EP_TERMINAL_EVIDENCE": + return [] + submission = evidence.get("submission") + terminal_run = evidence.get("run") + repository = evidence.get("repository") + if not all(isinstance(value, Mapping) for value in (submission, terminal_run, repository)): + return [] + revision = repository.get("revision") + outcome = terminal_run.get("outcome") + if ( + submission.get("project_id") != project_id + or terminal_run.get("id") != run_id + or outcome not in {"COMPLETE", "BLOCKED", "FAILED"} + or not isinstance(revision, str) + or re.fullmatch(r"[0-9a-f]{40}", revision) is None + ): + return [] + observed_at = row[1] + if not isinstance(observed_at, str) or not observed_at.strip(): + return [] + return [{ + "phase": "TERMINAL", + "observed_at": observed_at, + "commit_sha": revision, + "description": "terminal_repository_revision_verified", + }] + + def _central_console_current_execution_diagnostic(data_root: Path, project_id: str) -> str | None: """Return the selected active run's latest safe diagnostic, if any. @@ -2411,6 +2475,9 @@ def _central_console_run_detail(data_root: Path, project_id: str, run_id: str) - # the active card. Terminal history remains separate in the table, # while its exact step evidence stays available on demand. "lifecycle": _central_console_lifecycle(data_root, run_id), + "commit_timeline": _central_console_terminal_revision_timeline( + data_root, project_id, run_id, + ), } @@ -4025,7 +4092,7 @@ def _delegate_dashboard(self, method: str) -> None: "runtime": {}, "reviewers": [], "commits": {}, - "commit_timeline": [], + "commit_timeline": detail.get("commit_timeline", []), "pull_requests": [], "usage": {}, "evidence": [], diff --git a/src/engineering_platform/templates/workspace-config.json b/src/engineering_platform/templates/workspace-config.json index d56b4168..e9644fed 100644 --- a/src/engineering_platform/templates/workspace-config.json +++ b/src/engineering_platform/templates/workspace-config.json @@ -3,7 +3,7 @@ "platform": { "id": "engineering-platform", "name": "Engineering Platform", - "version": "2.3.21", + "version": "2.3.22", "generation": 2, "documentation_namespace": "engineering-platform", "capability_registry_version": 1 diff --git a/tests/engineering/dashboard.spec.mjs b/tests/engineering/dashboard.spec.mjs index b0cf571d..a884ca1c 100644 --- a/tests/engineering/dashboard.spec.mjs +++ b/tests/engineering/dashboard.spec.mjs @@ -2495,6 +2495,26 @@ test.describe("Engineering Status browser smoke", () => { await expect(page.locator("dialog[open]")).toHaveCount(1); }); + test("labels a terminal repository revision without calling it a phase commit", async ({ page }) => { + await page.goto(dashboardUrl, { waitUntil: "domcontentloaded" }); + await page.evaluate(() => { + renderPromptHistoryDetail({ + history: { run_id: "inbox-terminal-revision", status: "COMPLETE", title: "No-change run" }, + commit_timeline: [{ + phase: "TERMINAL", + observed_at: "2026-09-12T12:40:55+00:00", + commit_sha: "b".repeat(40), + description: "terminal_repository_revision_verified", + }], + }, "No-change run"); + }); + const content = page.locator("#promptHistoryDetailContent"); + await expect(content).toContainText("Geverifieerde eindrevisie"); + await expect(content).toContainText("Uitvoeringsresultaat"); + await expect(content).toContainText("Eindrevisie van de repository geverifieerd"); + await expect(content).toContainText("b".repeat(40)); + }); + test("renders terminal status recovery as a historical detail card", async ({ page }) => { const runId = "inbox-status-recovery"; await page.route("**/api/prompt-history", (route) => route.fulfill({ json: { runs: [{ diff --git a/tests/engineering/test_coverage_runtime_boundaries.py b/tests/engineering/test_coverage_runtime_boundaries.py index afb8b307..3a2180be 100644 --- a/tests/engineering/test_coverage_runtime_boundaries.py +++ b/tests/engineering/test_coverage_runtime_boundaries.py @@ -22,7 +22,7 @@ from contextlib import redirect_stdout from threading import RLock -from engineering_platform import agent_trust, central_database, project_topology, server, server_relay +from engineering_platform import agent_trust, central_database, project_topology, server, server_relay, storage from engineering_platform import providers from engineering_platform import codex_capacity from engineering_platform import pr_check_repair @@ -1720,7 +1720,15 @@ def test_central_console_history_detail_uses_the_dashboard_history_contract(self "terminal_state": "BLOCKED", "steps": [{"id": "START", "state": "START"}, {"id": "TERMINAL", "state": "BLOCKED"}], } - detail = {"run_id": "run-terminal", "status": "BLOCKED", "lifecycle": lifecycle} + timeline = [{ + "phase": "TERMINAL", "observed_at": "2026-09-12T12:40:55+00:00", + "commit_sha": "a" * 40, + "description": "terminal_repository_revision_verified", + }] + detail = { + "run_id": "run-terminal", "status": "BLOCKED", "lifecycle": lifecycle, + "commit_timeline": timeline, + } with patch("engineering_platform.server._console_projects", return_value=projects), patch( "engineering_platform.server._central_console_run_detail", return_value=detail ): @@ -1733,8 +1741,60 @@ def test_central_console_history_detail_uses_the_dashboard_history_contract(self payload = responses[-1][1] self.assertEqual(payload["history"], detail) self.assertEqual(payload["lifecycle"], lifecycle) + self.assertEqual(payload["commit_timeline"], timeline) self.assertNotIn("run", payload) + def test_central_terminal_revision_timeline_requires_verified_exact_artifact(self) -> None: + """Only immutable terminal evidence can supply a final revision item.""" + run_id = "run-terminal" + project_id = "project-a" + submission_id = "submission-terminal" + with sqlite3.connect(self.root / server.SERVER_DATABASE_FILENAME) as connection: + connection.execute( + "INSERT INTO ep_project_registrations(project_id,attachment_contract,status,created_at,updated_at) VALUES(?,?,?,?,?)", + (project_id, "DECLARATION", "ACTIVE", "now", "now"), + ) + connection.execute( + "INSERT INTO ep_repository_registrations(repository_id,project_id,authority_repository_id,role,attachment_contract,created_at,updated_at) VALUES(?,?,?,?,?,?,?)", + ("repo-a", project_id, "repo-a", "authority", "DECLARATION", "now", "now"), + ) + connection.execute( + "INSERT INTO ep_execution_runs(run_id,project_id,state,created_at,updated_at) VALUES(?,?,?,?,?)", + (run_id, project_id, "COMPLETE", "2026-09-12T12:00:00+00:00", "2026-09-12T12:40:55+00:00"), + ) + connection.execute( + "INSERT INTO ep_submissions(submission_id,project_id,repository_id,producer_id,producer_type,transport,prompt,prompt_digest,constraints,state,admission,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)", + (submission_id, project_id, "repo-a", "forge", "FORGE", "HTTP", "bounded", "sha256:prompt", "{}", "QUEUED", "ADMITTED", "now"), + ) + connection.execute( + "INSERT INTO ep_parity_lifecycle_dispatches(submission_id,project_id,repository_id,run_id,state,prompt_path,claimed_at,updated_at) VALUES(?,?,?,?,?,?,?,?)", + (submission_id, project_id, "repo-a", run_id, "COMPLETE", "CENTRAL:prompt", "now", "now"), + ) + artifact = self.root / "artifacts" / "projects" / project_id / "runs" / run_id / "terminal-evidence-v1.json" + artifact.parent.mkdir(parents=True) + artifact.write_text(json.dumps({ + "artifact_type": "EP_TERMINAL_EVIDENCE", + "submission": {"id": submission_id, "project_id": project_id, "repository_id": "repo-a"}, + "run": {"id": run_id, "outcome": "COMPLETE"}, + "repository": {"id": "repo-a", "revision": "b" * 40, "revision_required": True}, + }), encoding="utf-8") + storage.record_artifact( + self.root, artifact, artifact_id=f"terminal-evidence:{run_id}", artifact_type="EP_TERMINAL_EVIDENCE", + content_type="application/json", created_at="2026-09-12T12:40:55+00:00", run_id=run_id, + submission_id=submission_id, producer_id="forge", ep_run_id=run_id, + ep_submission_id=submission_id, central_database=self.root / server.SERVER_DATABASE_FILENAME, + artifact_root=self.root / "artifacts", + ) + + self.assertEqual(server._central_console_terminal_revision_timeline(self.root, project_id, run_id), [{ + "phase": "TERMINAL", "observed_at": "2026-09-12T12:40:55+00:00", + "commit_sha": "b" * 40, + "description": "terminal_repository_revision_verified", + }]) + + artifact.write_text("tampered", encoding="utf-8") + self.assertEqual(server._central_console_terminal_revision_timeline(self.root, project_id, run_id), []) + def test_console_event_and_report_helpers_reject_unowned_or_unavailable_central_artifacts(self) -> None: """Central report/chat helpers cannot be tricked into reading a checkout artifact.""" connection = Mock() From 812be6c2b350a20f447d3562de32328e8fb67d79 Mon Sep 17 00:00:00 2001 From: pcvantol Date: Sat, 12 Sep 2026 18:59:18 +0200 Subject: [PATCH 2/2] test: cover terminal revision evidence rejection --- .../test_coverage_runtime_boundaries.py | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/tests/engineering/test_coverage_runtime_boundaries.py b/tests/engineering/test_coverage_runtime_boundaries.py index 3a2180be..450ddf26 100644 --- a/tests/engineering/test_coverage_runtime_boundaries.py +++ b/tests/engineering/test_coverage_runtime_boundaries.py @@ -1772,19 +1772,24 @@ def test_central_terminal_revision_timeline_requires_verified_exact_artifact(sel ) artifact = self.root / "artifacts" / "projects" / project_id / "runs" / run_id / "terminal-evidence-v1.json" artifact.parent.mkdir(parents=True) - artifact.write_text(json.dumps({ + terminal_evidence = { "artifact_type": "EP_TERMINAL_EVIDENCE", "submission": {"id": submission_id, "project_id": project_id, "repository_id": "repo-a"}, "run": {"id": run_id, "outcome": "COMPLETE"}, "repository": {"id": "repo-a", "revision": "b" * 40, "revision_required": True}, - }), encoding="utf-8") - storage.record_artifact( - self.root, artifact, artifact_id=f"terminal-evidence:{run_id}", artifact_type="EP_TERMINAL_EVIDENCE", - content_type="application/json", created_at="2026-09-12T12:40:55+00:00", run_id=run_id, - submission_id=submission_id, producer_id="forge", ep_run_id=run_id, - ep_submission_id=submission_id, central_database=self.root / server.SERVER_DATABASE_FILENAME, - artifact_root=self.root / "artifacts", - ) + } + + def record_terminal_evidence(payload: object) -> None: + artifact.write_text(json.dumps(payload), encoding="utf-8") + storage.record_artifact( + self.root, artifact, artifact_id=f"terminal-evidence:{run_id}", artifact_type="EP_TERMINAL_EVIDENCE", + content_type="application/json", created_at="2026-09-12T12:40:55+00:00", run_id=run_id, + submission_id=submission_id, producer_id="forge", ep_run_id=run_id, + ep_submission_id=submission_id, central_database=self.root / server.SERVER_DATABASE_FILENAME, + artifact_root=self.root / "artifacts", + ) + + record_terminal_evidence(terminal_evidence) self.assertEqual(server._central_console_terminal_revision_timeline(self.root, project_id, run_id), [{ "phase": "TERMINAL", "observed_at": "2026-09-12T12:40:55+00:00", @@ -1794,6 +1799,25 @@ def test_central_terminal_revision_timeline_requires_verified_exact_artifact(sel artifact.write_text("tampered", encoding="utf-8") self.assertEqual(server._central_console_terminal_revision_timeline(self.root, project_id, run_id), []) + record_terminal_evidence({}) + self.assertEqual(server._central_console_terminal_revision_timeline(self.root, project_id, run_id), []) + record_terminal_evidence({ + "artifact_type": "EP_TERMINAL_EVIDENCE", "submission": {}, "run": {}, "repository": {}, + }) + self.assertEqual(server._central_console_terminal_revision_timeline(self.root, project_id, run_id), []) + wrong_project_evidence = { + **terminal_evidence, + "submission": {**terminal_evidence["submission"], "project_id": "other-project"}, + } + record_terminal_evidence(wrong_project_evidence) + self.assertEqual(server._central_console_terminal_revision_timeline(self.root, project_id, run_id), []) + record_terminal_evidence(terminal_evidence) + with sqlite3.connect(self.root / server.SERVER_DATABASE_FILENAME) as connection: + connection.execute( + "UPDATE execution_artifact_records SET created_at='' WHERE artifact_id=?", + (f"terminal-evidence:{run_id}",), + ) + self.assertEqual(server._central_console_terminal_revision_timeline(self.root, project_id, run_id), []) def test_console_event_and_report_helpers_reject_unowned_or_unavailable_central_artifacts(self) -> None: """Central report/chat helpers cannot be tricked into reading a checkout artifact."""