From fb83dfaaeb79ca6137a8ed56828531b132c82dcd Mon Sep 17 00:00:00 2001 From: pcvantol Date: Sat, 12 Sep 2026 13:35:36 +0200 Subject: [PATCH] fix: qualify verified managed no-op delivery evidence --- .../EP_PRODUCER_READBACK_CONTRACT.md | 11 ++++-- package-lock.json | 4 +- package.json | 2 +- pyproject.toml | 2 +- .../ENGINEERING_PLATFORM_CONFIG.json | 2 +- .../ENGINEERING_PLATFORM_VERSION.json | 8 ++-- src/engineering_platform/agent_state.py | 1 + src/engineering_platform/execution_host.py | 36 ++++++++++++++++++ src/engineering_platform/platform_version.py | 2 +- .../submission_service.py | 32 +++++++++++++--- .../templates/workspace-config.json | 2 +- tests/engineering/test_execution_host.py | 37 +++++++++++++++++++ tests/engineering/test_submission_service.py | 20 ++++++++++ 13 files changed, 139 insertions(+), 20 deletions(-) diff --git a/docs/engineering/EP_PRODUCER_READBACK_CONTRACT.md b/docs/engineering/EP_PRODUCER_READBACK_CONTRACT.md index 36ede0e7..a9d605e4 100644 --- a/docs/engineering/EP_PRODUCER_READBACK_CONTRACT.md +++ b/docs/engineering/EP_PRODUCER_READBACK_CONTRACT.md @@ -65,10 +65,13 @@ authentication and digest verification. It never parses and reserializes the artifact response. A missing, corrupt, or mismatched artifact is represented as `MISSING`, `CORRUPT`, or `INCOMPLETE`; the reader never fills it in. -`COMPLETE` mutating delivery is qualified only when a recorded merge revision -is present in the checkpoint's verified commit evidence. `VALIDATION_ONLY`, -`BLOCKED`, and `FAILED` may have valid terminal evidence with a null revision; -they are not fabricated into successful delivery. +`COMPLETE` mutating delivery is qualified only when the checkpoint contains +verified, run-bound delivery evidence: normally a recorded merge revision, or +an explicit host-verified Managed no-op revision after the unchanged, +synchronized `main` checkout was rechecked in that transaction. EP never uses +the ambient checkout `HEAD` as a substitute. `VALIDATION_ONLY`, `BLOCKED`, and +`FAILED` may have valid terminal evidence with a null revision; they are not +fabricated into successful delivery. The machine-readable response shape is [`producer-readback-v1.2.schema.json`](../../src/engineering_platform/schemas/producer-readback-v1.2.schema.json). diff --git a/package-lock.json b/package-lock.json index 5ddad544..0afdeb59 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "engineering-platform-browser-validation", - "version": "2.3.15", + "version": "2.3.16", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "engineering-platform-browser-validation", - "version": "2.3.15", + "version": "2.3.16", "devDependencies": { "@playwright/test": "1.62.1" } diff --git a/package.json b/package.json index 92183f3d..680936eb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "engineering-platform-browser-validation", "private": true, - "version": "2.3.15", + "version": "2.3.16", "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 d476d372..c7d2ba8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "engineering-platform" -version = "2.3.15" +version = "2.3.16" 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 79f723a5..259611d8 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.15", + "version": "2.3.16", "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 2ae7a15f..997ad7bd 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.15", + "dashboard_version": "2.3.16", "handoff_protocol": 1, "memory_format": 2, "minimum_codex_cli": "0.146.0", "inbox_protocol": 1, - "platform_version": "2.3.15", + "platform_version": "2.3.16", "report_format": 2, - "runner_version": "2.3.15", + "runner_version": "2.3.16", "status_model": 1, "storage_schema": 44, - "watcher_version": "2.3.15" + "watcher_version": "2.3.16" } diff --git a/src/engineering_platform/agent_state.py b/src/engineering_platform/agent_state.py index 0446671f..dca37ac0 100644 --- a/src/engineering_platform/agent_state.py +++ b/src/engineering_platform/agent_state.py @@ -47,6 +47,7 @@ "implementation_merge_verified", "finalization_merge_verified", "reconciliation_merge_verified", + "managed_noop_repository_reconciled", }) SENSITIVE_DIAGNOSTIC_PATTERN = re.compile( r"(?i)\b(api[_ -]?key|oauth|access[_ -]?token|refresh[_ -]?token|secret|cookie|authorization|password)\b\s*[:=]\s*\S+|\bbearer\s+\S+|\b[A-Z][A-Z0-9_]{2,}\s*=\s*\S+" diff --git a/src/engineering_platform/execution_host.py b/src/engineering_platform/execution_host.py index 6f501eb8..f4557bf2 100644 --- a/src/engineering_platform/execution_host.py +++ b/src/engineering_platform/execution_host.py @@ -2271,6 +2271,16 @@ def _advance_after_primary_agent_result( latest_repository_evidence=_repository_summary(evidence), terminal_condition="repository_reconciled", ) + # A verified Managed no-op is still a delivery decision. Preserve + # the exact, host-observed main revision that made the decision + # safe, rather than letting a later terminal artifact infer an + # ambient checkout revision. + reconciled = self._append_verified_commit_evidence( + reconciled, + phase="EXECUTE_AGENT", + commit_sha=evidence.head_sha, + description="managed_noop_repository_reconciled", + ) return self._poll(reconciled, result) recoverable_local_failure = self._is_recoverable_implementation_validation_failure(state, result) if state.transaction_kind == "IMPLEMENTATION" and state.action_intent == "MUTATING_DELIVERY" and ( @@ -3059,6 +3069,32 @@ def _poll(self, state: TransactionState, result: AgentResult | None = None) -> T if result and result.terminal_state == "COMPLETE": evidence = self.repository.inspect(self.root) if evidence.clean and evidence.main_contains_head: + merged_revisions = { + revision + for revision in (state.implementation_merge_commit, state.finalization_merge_commit) + if isinstance(revision, str) and re.fullmatch(r"[0-9a-f]{40}", revision) + } + has_verified_merge = any( + item.get("commit_sha") in merged_revisions + for item in state.commit_evidence + ) + has_verified_managed_noop = any( + item.get("description") == "managed_noop_repository_reconciled" + and item.get("phase") == "EXECUTE_AGENT" + and item.get("commit_sha") == state.last_verified_sha + for item in state.commit_evidence + ) + if ( + state.owner_authorized + and state.action_intent == "MUTATING_DELIVERY" + and not (has_verified_merge or has_verified_managed_noop) + ): + return self._save_terminal( + state, + "BLOCKED", + "mutating_delivery_evidence_required", + "A mutating delivery requires a merged revision or a host-verified managed no-op revision.", + ) return self._cleanup(state) return replace( state, phase="WAIT_FOR_TERMINAL_EVIDENCE", next_action="obtain_repository_evidence" diff --git a/src/engineering_platform/platform_version.py b/src/engineering_platform/platform_version.py index bc86e5b3..700eb3f7 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.15" +CURRENT_PLATFORM_VERSION = "2.3.16" MANIFEST_FIELDS = frozenset( { "platform_version", diff --git a/src/engineering_platform/submission_service.py b/src/engineering_platform/submission_service.py index b5d5c533..16c65a98 100644 --- a/src/engineering_platform/submission_service.py +++ b/src/engineering_platform/submission_service.py @@ -621,11 +621,33 @@ def _repository_revision(state: object, outcome: str) -> tuple[str | None, bool] return None, True revision = getattr(state, "finalization_merge_commit", None) or getattr(state, "implementation_merge_commit", None) evidence = getattr(state, "commit_evidence", ()) - if not isinstance(revision, str) or not __import__("re").fullmatch(r"[0-9a-f]{40}", revision): - return None, False - if not any(isinstance(item, dict) and item.get("commit_sha") == revision for item in evidence): - return None, False - return revision, True + if isinstance(revision, str) and __import__("re").fullmatch(r"[0-9a-f]{40}", revision): + if any(isinstance(item, dict) and item.get("commit_sha") == revision for item in evidence): + return revision, True + + # A Managed no-op is only delivery-qualified when the lifecycle recorded + # the exact inspected main revision as explicit evidence. This is not an + # ambient-HEAD fallback: the record is written only after the host proves + # the unchanged, synchronized main checkout during the same transaction. + noop_revision = getattr(state, "last_verified_sha", None) + if ( + getattr(state, "action_intent", None) == "MUTATING_DELIVERY" + and getattr(state, "transaction_kind", None) == "IMPLEMENTATION" + and getattr(state, "terminal_condition", None) == "repository_reconciled" + and getattr(state, "implementation_merge_commit", None) is None + and getattr(state, "finalization_merge_commit", None) is None + and isinstance(noop_revision, str) + and __import__("re").fullmatch(r"[0-9a-f]{40}", noop_revision) + and any( + isinstance(item, dict) + and item.get("phase") == "EXECUTE_AGENT" + and item.get("commit_sha") == noop_revision + and item.get("description") == "managed_noop_repository_reconciled" + for item in evidence + ) + ): + return noop_revision, True + return None, False def write_terminal_evidence( diff --git a/src/engineering_platform/templates/workspace-config.json b/src/engineering_platform/templates/workspace-config.json index d8380926..ee9ada72 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.15", + "version": "2.3.16", "generation": 2, "documentation_namespace": "engineering-platform", "capability_registry_version": 1 diff --git a/tests/engineering/test_execution_host.py b/tests/engineering/test_execution_host.py index 4cc03fc0..daae02ed 100644 --- a/tests/engineering/test_execution_host.py +++ b/tests/engineering/test_execution_host.py @@ -2653,6 +2653,8 @@ def test_verified_managed_noop_reconciles_an_unchanged_main_checkout(self) -> No self.assertEqual(completed.phase, "COMPLETE") self.assertEqual(completed.terminal_condition, "repository_reconciled") + self.assertEqual(completed.commit_evidence[-1]["description"], "managed_noop_repository_reconciled") + self.assertEqual(completed.commit_evidence[-1]["commit_sha"], "a" * 40) self.assertEqual(repository.cleanup_calls, [(None, None)]) def test_unverified_managed_noop_remains_blocked(self) -> None: @@ -2672,6 +2674,41 @@ def test_unverified_managed_noop_remains_blocked(self) -> None: self.assertEqual(blocked.next_action, "local_validation_scope") self.assertEqual(repository.cleanup_calls, []) + def test_unproven_managed_delivery_cannot_complete_without_a_merge_or_verified_noop(self) -> None: + state = TransactionState( + "unproven-managed-delivery", "pcvantol/djconnect", str(self.prompt), + "WAIT_FOR_TERMINAL_EVIDENCE", owner_authorized=True, action_intent="MUTATING_DELIVERY", + ) + runner = EngineeringRunner( + self.root, self.store, FakeRepository(), FakeGitHub([]), FakeAgent(AgentResult("WAITING")), lambda _: None, + ) + + blocked = runner._poll(state, AgentResult("COMPLETE", terminal_condition="repository_reconciled")) + + self.assertEqual(blocked.phase, "BLOCKED") + self.assertEqual(blocked.next_action, "mutating_delivery_evidence_required") + self.assertEqual(runner.repository.cleanup_calls, []) + + def test_verified_merge_remains_a_qualified_mutating_delivery(self) -> None: + revision = "a" * 40 + state = TransactionState( + "merged-managed-delivery", "pcvantol/djconnect", str(self.prompt), + "WAIT_FOR_TERMINAL_EVIDENCE", owner_authorized=True, action_intent="MUTATING_DELIVERY", + implementation_merge_commit=revision, + commit_evidence=({ + "phase": "WAIT_FOR_OPERATOR_MERGE", "observed_at": "2026-01-01T00:00:00+00:00", + "commit_sha": revision, "description": "implementation_merge_verified", + },), + ) + runner = EngineeringRunner( + self.root, self.store, FakeRepository(), FakeGitHub([]), FakeAgent(AgentResult("WAITING")), lambda _: None, + ) + + completed = runner._poll(state, AgentResult("COMPLETE")) + + self.assertEqual(completed.phase, "COMPLETE") + self.assertEqual(runner.repository.cleanup_calls, [(None, None)]) + def test_quality_assurance_does_not_create_or_replace_the_implementation_pr(self) -> None: agent = SequencedFakeAgent([ AgentResult("COMPLETE", "codex/implementation", 701), diff --git a/tests/engineering/test_submission_service.py b/tests/engineering/test_submission_service.py index b3c8873f..ca5faa79 100644 --- a/tests/engineering/test_submission_service.py +++ b/tests/engineering/test_submission_service.py @@ -57,6 +57,26 @@ def forge_payload(self, key: str = "forge-receipt") -> dict[str, object]: }) return payload + def test_verified_managed_noop_uses_its_explicit_run_bound_revision(self) -> None: + revision = "a" * 40 + verified = TransactionState( + "verified-managed-noop", "djconnect", "prompt", "COMPLETE", terminal=True, + action_intent="MUTATING_DELIVERY", transaction_kind="IMPLEMENTATION", + terminal_condition="repository_reconciled", last_verified_sha=revision, + commit_evidence=({ + "phase": "EXECUTE_AGENT", "observed_at": "2026-01-01T00:00:00+00:00", + "commit_sha": revision, "description": "managed_noop_repository_reconciled", + },), + ) + unproven = TransactionState( + "unproven-managed-noop", "djconnect", "prompt", "COMPLETE", terminal=True, + action_intent="MUTATING_DELIVERY", transaction_kind="IMPLEMENTATION", + terminal_condition="repository_reconciled", last_verified_sha=revision, + ) + + self.assertEqual(submission_service._repository_revision(verified, "COMPLETE"), (revision, True)) + self.assertEqual(submission_service._repository_revision(unproven, "COMPLETE"), (None, False)) + def test_versioned_forge_submission_receipt_is_durable_and_idempotent(self) -> None: with sqlite3.connect(self.root / server.SERVER_DATABASE_FILENAME) as connection: request = submission_service.request_from_mapping(