engineering-platform 2.3.73__tar.gz → 2.3.75__tar.gz
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-2.3.73 → engineering_platform-2.3.75}/PKG-INFO +1 -1
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/pyproject.toml +1 -1
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/ENGINEERING_PLATFORM_CONFIG.json +1 -1
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/ENGINEERING_PLATFORM_VERSION.json +4 -4
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/execution_host.py +24 -2
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/platform_version.py +1 -1
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/server.py +27 -2
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/submission_service.py +63 -10
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/templates/workspace-config.json +1 -1
- engineering_platform-2.3.75/src/engineering_platform/terminal_evidence_reconciliation.py +244 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform.egg-info/PKG-INFO +1 -1
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform.egg-info/SOURCES.txt +1 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/README.md +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/setup.cfg +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/__init__.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/__main__.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/agent_state.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/agent_trust.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/assets/dashboard.css +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/assets/dashboard.js +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/assets/dashboard_locales.mjs +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/assets/dashboard_status_store.mjs +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/assets/dashboard_translation.mjs +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/assets/operations-console/apple-touch-icon-dark.png +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/assets/operations-console/apple-touch-icon-light.png +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/assets/operations-console/icon-dark.png +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/assets/operations-console/icon-light.png +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/assets/operations-console/icon-transparent.png +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/assets/operations-console/manifest.webmanifest +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/capability_preflight.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/capability_review.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/central_data_transfer.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/central_database.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/central_store_migration.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/codex_capacity.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/codex_chat.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/codex_observability.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/component_lock.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/component_logging.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/console_presentation.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/console_route_ownership.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/contracts/__init__.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/contracts/ep_consumer.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/contracts/models.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/contracts/projection.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/dashboard_browser_validation.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/dashboard_state.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/dashboard_supervisor.swift +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/dashboard_translation.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/dependabot_producer.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/development_profile.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/drift_diagnostics.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/emergency_recovery.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/engineering_memory.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/ep_consumer_credentials.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/evidence_projection.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/execution_activity.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/execution_context.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/execution_errors.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/execution_evidence.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/execution_executor.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/execution_finalization.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/execution_host_evidence.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/execution_lease.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/execution_lifecycle.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/execution_models.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/execution_readiness.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/execution_reporting.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/execution_repository.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/execution_timeout_policy.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/execution_timing.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/execution_transaction.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/external_producer_binding.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/file_inbox.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/forensic_attribution.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/forensic_attribution_v2.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/forensic_delta.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/golden_scenario.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/historical_dashboard_configuration.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/host_admin.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/host_preflight.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/installation_relocation.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/installation_update_activation.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/installation_update_admission.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/installation_update_backup.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/installation_update_composition.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/installation_update_executor.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/installation_update_migration.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/installation_update_operation.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/installation_update_plan.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/installation_update_preparation.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/investigation_ledger.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/legacy_inbox_migration.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/legacy_installation_adoption.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/lifecycle_worker.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/live_status.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/local_api.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/local_api_keychain.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/local_repository_binding.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/managed_autonomy.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/managed_codex_runtime.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/operational_installation.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/operational_installation_lock.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/operational_installation_record.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/owner_credential_recovery.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/parity_context.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/parity_lifecycle_dispatcher.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/platform_admin.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/platform_api.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/platform_bootstrap.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/platform_components.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/pr_check_repair.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/pr_evidence_backfill.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/producer.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/product_installation_readback.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/project_agent.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/project_agent_service.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/project_topology.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/prompt_history.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/provider_context.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/provider_context_benchmark.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/provider_context_scope.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/provider_interruption.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/provider_process_identity.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/provider_readiness.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/provider_recovery.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/provider_usage.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/providers.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/qualification.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/qualification_runtime.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/recommendation_handoff.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/reconciliation_adoption.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/release_operation.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/report_analysis.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/repository_attachment.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/repository_handoff.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/resources.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/reviewer_evidence.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/revision_binding.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/schemas/producer-readback-v1.2.schema.json +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/schemas/producer-readback-v1.schema.json +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/schemas/repository-attachment.schema.json +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/server_console_services.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/server_relay.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/server_service.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/status_model.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/status_reconciliation.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/storage.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/submission_cli.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/submission_intake.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/system_installation_topology.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/system_server_service.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/telemetry.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/validation_identity.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/validation_profile.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/version_preparation_delivery.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/workspace_preflight.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/worktree_provenance.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/worktree_tooling.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform.egg-info/dependency_links.txt +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform.egg-info/entry_points.txt +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform.egg-info/top_level.txt +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/tests/test_adaptive_action_sizing_design.py +0 -0
- {engineering_platform-2.3.73 → engineering_platform-2.3.75}/tests/test_role_task_model_policy_design.py +0 -0
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bootstrap_contract": "2026.12",
|
|
3
3
|
"checkpoint_format": 1,
|
|
4
|
-
"dashboard_version": "2.3.
|
|
4
|
+
"dashboard_version": "2.3.75",
|
|
5
5
|
"handoff_protocol": 1,
|
|
6
6
|
"memory_format": 2,
|
|
7
7
|
"minimum_codex_cli": "0.146.0",
|
|
8
8
|
"inbox_protocol": 1,
|
|
9
|
-
"platform_version": "2.3.
|
|
9
|
+
"platform_version": "2.3.75",
|
|
10
10
|
"report_format": 2,
|
|
11
|
-
"runner_version": "2.3.
|
|
11
|
+
"runner_version": "2.3.75",
|
|
12
12
|
"status_model": 1,
|
|
13
13
|
"storage_schema": 45,
|
|
14
|
-
"watcher_version": "2.3.
|
|
14
|
+
"watcher_version": "2.3.75"
|
|
15
15
|
}
|
|
@@ -3663,6 +3663,8 @@ First implementation pull-request publication gate:
|
|
|
3663
3663
|
action = {"IMPLEMENTATION": "IMPLEMENTATION_MERGE", "FINALIZATION": "FINALIZATION_MERGE", "RECONCILIATION": "RECONCILIATION_MERGE"}[state.transaction_kind]
|
|
3664
3664
|
self._managed_action(state, action, "EXPECTED_OPERATOR_GATE", actor="operator", evidence_ref="github_merge")
|
|
3665
3665
|
state = self._record_merged_evidence(state, pr, evidence)
|
|
3666
|
+
if state.terminal:
|
|
3667
|
+
return state
|
|
3666
3668
|
if state.owner_authorized and state.transaction_kind == "IMPLEMENTATION":
|
|
3667
3669
|
return self._start_finalization(state, pr.number)
|
|
3668
3670
|
if state.owner_authorized and state.transaction_kind == "FINALIZATION":
|
|
@@ -4145,6 +4147,26 @@ First implementation pull-request publication gate:
|
|
|
4145
4147
|
def _record_merged_evidence(
|
|
4146
4148
|
self, state: TransactionState, pr: PullRequestEvidence, evidence: RepositoryEvidence
|
|
4147
4149
|
) -> TransactionState:
|
|
4150
|
+
expected_head = (
|
|
4151
|
+
(state.implementation_head_sha or state.last_verified_sha)
|
|
4152
|
+
if state.transaction_kind == "IMPLEMENTATION"
|
|
4153
|
+
else (state.finalization_head_sha or state.last_verified_sha)
|
|
4154
|
+
if state.transaction_kind == "FINALIZATION"
|
|
4155
|
+
else None
|
|
4156
|
+
)
|
|
4157
|
+
if (
|
|
4158
|
+
state.transaction_kind in {"IMPLEMENTATION", "FINALIZATION"}
|
|
4159
|
+
and isinstance(expected_head, str)
|
|
4160
|
+
and isinstance(pr.head_sha, str)
|
|
4161
|
+
and pr.head_sha != expected_head
|
|
4162
|
+
):
|
|
4163
|
+
return self._save_terminal(
|
|
4164
|
+
state,
|
|
4165
|
+
"BLOCKED",
|
|
4166
|
+
"merged_pull_request_candidate_mismatch",
|
|
4167
|
+
"Merged pull-request evidence does not match the exact phase candidate.",
|
|
4168
|
+
)
|
|
4169
|
+
phase_head = pr.head_sha or expected_head
|
|
4148
4170
|
common = {
|
|
4149
4171
|
"last_verified_sha": evidence.head_sha,
|
|
4150
4172
|
"latest_repository_evidence": _repository_summary(evidence),
|
|
@@ -4155,7 +4177,7 @@ First implementation pull-request publication gate:
|
|
|
4155
4177
|
state,
|
|
4156
4178
|
implementation_branch=state.branch,
|
|
4157
4179
|
implementation_pull_request=pr.number,
|
|
4158
|
-
implementation_head_sha=
|
|
4180
|
+
implementation_head_sha=phase_head,
|
|
4159
4181
|
implementation_merge_commit=pr.merge_commit,
|
|
4160
4182
|
**common,
|
|
4161
4183
|
)
|
|
@@ -4168,7 +4190,7 @@ First implementation pull-request publication gate:
|
|
|
4168
4190
|
state,
|
|
4169
4191
|
finalization_branch=state.branch or state.finalization_branch,
|
|
4170
4192
|
finalization_pull_request=pr.number,
|
|
4171
|
-
finalization_head_sha=
|
|
4193
|
+
finalization_head_sha=phase_head,
|
|
4172
4194
|
finalization_merge_commit=pr.merge_commit,
|
|
4173
4195
|
**common,
|
|
4174
4196
|
)
|
|
@@ -12,7 +12,7 @@ from .storage import ENGINEERING_STORAGE_SCHEMA_VERSION
|
|
|
12
12
|
|
|
13
13
|
SEMVER = re.compile(r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$")
|
|
14
14
|
CONTRACT = re.compile(r"^(\d{4})\.(0[1-9]|1[0-2])$")
|
|
15
|
-
CURRENT_PLATFORM_VERSION = "2.3.
|
|
15
|
+
CURRENT_PLATFORM_VERSION = "2.3.75"
|
|
16
16
|
MANIFEST_FIELDS = frozenset(
|
|
17
17
|
{
|
|
18
18
|
"platform_version",
|
{engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/server.py
RENAMED
|
@@ -124,7 +124,7 @@ SERVER_CONFIGURATION_VERSION = 3
|
|
|
124
124
|
# bootstrap is deliberately separate from the retired predecessor migration
|
|
125
125
|
# machinery: it creates a clean installation only and never accepts a source
|
|
126
126
|
# database path.
|
|
127
|
-
SERVER_STORE_SCHEMA_VERSION =
|
|
127
|
+
SERVER_STORE_SCHEMA_VERSION = 67
|
|
128
128
|
SERVER_ENVIRONMENT_DATA_ROOT = "EP_SERVER_DATA_ROOT"
|
|
129
129
|
FILE_INBOX_DIRECTORY = "file-inbox"
|
|
130
130
|
HTTP_JSON_OPENAPI_PATH = "/v1/openapi.json"
|
|
@@ -591,6 +591,7 @@ SERVER_REQUIRED_TABLES = frozenset(
|
|
|
591
591
|
"ep_forge_planning_context_envelopes",
|
|
592
592
|
"ep_execution_host_evidence",
|
|
593
593
|
"ep_technical_diagnostics",
|
|
594
|
+
"ep_terminal_evidence_reconciliation_operations",
|
|
594
595
|
}
|
|
595
596
|
)
|
|
596
597
|
SERVER_REQUIRED_INDEXES = frozenset(
|
|
@@ -614,6 +615,7 @@ SERVER_REQUIRED_INDEXES = frozenset(
|
|
|
614
615
|
"ep_forge_action_context_envelopes_project_lookup",
|
|
615
616
|
"ep_forge_planning_context_envelopes_project_lookup",
|
|
616
617
|
"ep_technical_diagnostics_created_lookup",
|
|
618
|
+
"execution_artifact_records_active_terminal_run",
|
|
617
619
|
}
|
|
618
620
|
)
|
|
619
621
|
SERVER_REQUIRED_VIEWS = frozenset({"execution_submission_run_links"})
|
|
@@ -813,6 +815,7 @@ def _install_current_schema(connection: sqlite3.Connection, identity: RuntimeIde
|
|
|
813
815
|
_install_execution_host_evidence_schema(connection)
|
|
814
816
|
_install_technical_diagnostics_schema(connection)
|
|
815
817
|
owner_credential_recovery.install_schema(connection)
|
|
818
|
+
submission_service.install_terminal_evidence_reconciliation_schema(connection)
|
|
816
819
|
|
|
817
820
|
connection.execute(
|
|
818
821
|
"INSERT INTO engineering_schema_migrations(version) VALUES(?)",
|
|
@@ -1960,6 +1963,25 @@ def _migrate_schema_66(connection: sqlite3.Connection) -> None:
|
|
|
1960
1963
|
connection.execute("UPDATE ep_installations SET schema_version=66")
|
|
1961
1964
|
|
|
1962
1965
|
|
|
1966
|
+
def _migrate_schema_67(connection: sqlite3.Connection) -> None:
|
|
1967
|
+
"""Add guarded terminal-evidence projection reconciliation."""
|
|
1968
|
+
connection.execute("ALTER TABLE ep_installations RENAME TO ep_installations_schema66")
|
|
1969
|
+
connection.execute(
|
|
1970
|
+
"CREATE TABLE ep_installations (instance_id TEXT PRIMARY KEY,created_at TEXT NOT NULL,"
|
|
1971
|
+
"schema_version INTEGER NOT NULL CHECK(schema_version IN "
|
|
1972
|
+
"(41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67)))"
|
|
1973
|
+
)
|
|
1974
|
+
connection.execute(
|
|
1975
|
+
"INSERT INTO ep_installations SELECT instance_id,created_at,67 "
|
|
1976
|
+
"FROM ep_installations_schema66"
|
|
1977
|
+
)
|
|
1978
|
+
connection.execute("DROP TABLE ep_installations_schema66")
|
|
1979
|
+
submission_service.install_terminal_evidence_reconciliation_schema(connection)
|
|
1980
|
+
connection.execute("INSERT OR IGNORE INTO engineering_schema_migrations(version) VALUES(67)")
|
|
1981
|
+
connection.execute("UPDATE engineering_metadata SET value='67' WHERE key='installation.schema_version'")
|
|
1982
|
+
connection.execute("UPDATE ep_installations SET schema_version=67")
|
|
1983
|
+
|
|
1984
|
+
|
|
1963
1985
|
_SERVER_SCHEMA_UPGRADE_STEPS = (
|
|
1964
1986
|
(42, _migrate_schema_42),
|
|
1965
1987
|
(43, _migrate_schema_43),
|
|
@@ -1986,6 +2008,7 @@ _SERVER_SCHEMA_UPGRADE_STEPS = (
|
|
|
1986
2008
|
(64, _migrate_schema_64),
|
|
1987
2009
|
(65, _migrate_schema_65),
|
|
1988
2010
|
(66, _migrate_schema_66),
|
|
2011
|
+
(67, _migrate_schema_67),
|
|
1989
2012
|
)
|
|
1990
2013
|
_SUPPORTED_SERVER_SCHEMA_VERSIONS = frozenset(
|
|
1991
2014
|
range(41, SERVER_STORE_SCHEMA_VERSION + 1)
|
|
@@ -2026,6 +2049,8 @@ def validate_store(data_root: Path, identity: RuntimeIdentity) -> dict[str, obje
|
|
|
2026
2049
|
"ep_execution_host_evidence_immutable_delete",
|
|
2027
2050
|
"ep_technical_diagnostics_immutable_update",
|
|
2028
2051
|
"ep_technical_diagnostics_immutable_delete",
|
|
2052
|
+
"ep_terminal_evidence_reconciliation_immutable_update",
|
|
2053
|
+
"ep_terminal_evidence_reconciliation_immutable_delete",
|
|
2029
2054
|
} <= triggers and integrity == ["ok"] and metadata == {"installation.instance_id": identity.instance_id, "installation.schema_version": str(SERVER_STORE_SCHEMA_VERSION)} and installation is not None
|
|
2030
2055
|
if not valid:
|
|
2031
2056
|
raise ServerConfigurationError(
|
|
@@ -6174,7 +6199,7 @@ class _HealthHandler(http.server.BaseHTTPRequestHandler):
|
|
|
6174
6199
|
self._send(503, {"error": "CENTRAL_UNAVAILABLE"})
|
|
6175
6200
|
return
|
|
6176
6201
|
artifact = re.fullmatch(
|
|
6177
|
-
r"/v1/projects/([^/]+)/artifacts/((?:terminal-evidence
|
|
6202
|
+
r"/v1/projects/([^/]+)/artifacts/((?:terminal-evidence(?:-reconciled)?|assurance-findings):[^/]+)",
|
|
6178
6203
|
unquote(request.path),
|
|
6179
6204
|
)
|
|
6180
6205
|
if artifact:
|
|
@@ -770,6 +770,51 @@ def _findings_artifact_id(run_id: str) -> str:
|
|
|
770
770
|
return f"assurance-findings:{run_id}"
|
|
771
771
|
|
|
772
772
|
|
|
773
|
+
def install_terminal_evidence_reconciliation_schema(connection: sqlite3.Connection) -> None:
|
|
774
|
+
"""Install the one-run/one-current-projection repair authority."""
|
|
775
|
+
connection.executescript("""
|
|
776
|
+
CREATE TABLE IF NOT EXISTS ep_terminal_evidence_reconciliation_operations (
|
|
777
|
+
operation_id TEXT PRIMARY KEY,
|
|
778
|
+
run_id TEXT NOT NULL UNIQUE REFERENCES ep_execution_runs(run_id),
|
|
779
|
+
project_id TEXT NOT NULL REFERENCES ep_project_registrations(project_id),
|
|
780
|
+
source_artifact_id TEXT NOT NULL REFERENCES execution_artifact_records(artifact_id),
|
|
781
|
+
source_digest TEXT NOT NULL,
|
|
782
|
+
replacement_artifact_id TEXT NOT NULL UNIQUE REFERENCES execution_artifact_records(artifact_id),
|
|
783
|
+
replacement_digest TEXT NOT NULL,
|
|
784
|
+
reason_code TEXT NOT NULL CHECK(reason_code='MERGE_CANDIDATE_PROJECTION_V1'),
|
|
785
|
+
recorded_at TEXT NOT NULL
|
|
786
|
+
);
|
|
787
|
+
CREATE UNIQUE INDEX IF NOT EXISTS execution_artifact_records_active_terminal_run
|
|
788
|
+
ON execution_artifact_records(COALESCE(ep_run_id,run_id))
|
|
789
|
+
WHERE artifact_type='EP_TERMINAL_EVIDENCE'
|
|
790
|
+
AND projection_status='AVAILABLE'
|
|
791
|
+
AND COALESCE(ep_run_id,run_id) IS NOT NULL;
|
|
792
|
+
CREATE TRIGGER IF NOT EXISTS ep_terminal_evidence_reconciliation_immutable_update
|
|
793
|
+
BEFORE UPDATE ON ep_terminal_evidence_reconciliation_operations
|
|
794
|
+
BEGIN SELECT RAISE(ABORT, 'Terminal evidence reconciliation is immutable'); END;
|
|
795
|
+
CREATE TRIGGER IF NOT EXISTS ep_terminal_evidence_reconciliation_immutable_delete
|
|
796
|
+
BEFORE DELETE ON ep_terminal_evidence_reconciliation_operations
|
|
797
|
+
BEGIN SELECT RAISE(ABORT, 'Terminal evidence reconciliation is immutable'); END;
|
|
798
|
+
""")
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
def _active_terminal_artifact(
|
|
802
|
+
connection: sqlite3.Connection, run_id: str,
|
|
803
|
+
) -> tuple[object, ...] | None:
|
|
804
|
+
rows = connection.execute(
|
|
805
|
+
"""SELECT artifact_id,digest_algorithm,digest,content_type,integrity_status,storage_location
|
|
806
|
+
FROM execution_artifact_records
|
|
807
|
+
WHERE artifact_type='EP_TERMINAL_EVIDENCE'
|
|
808
|
+
AND projection_status='AVAILABLE'
|
|
809
|
+
AND (ep_run_id=? OR (ep_run_id IS NULL AND run_id=?))
|
|
810
|
+
ORDER BY artifact_id LIMIT 2""",
|
|
811
|
+
(run_id, run_id),
|
|
812
|
+
).fetchall()
|
|
813
|
+
if len(rows) > 1:
|
|
814
|
+
raise SubmissionError("TERMINAL_EVIDENCE_PROJECTION_AMBIGUOUS", 500)
|
|
815
|
+
return tuple(rows[0]) if rows else None
|
|
816
|
+
|
|
817
|
+
|
|
773
818
|
def _current_assurance(checkpoint: object) -> tuple[str, list[dict[str, object]], list[dict[str, object]]]:
|
|
774
819
|
"""Project final assurance from one complete current review set.
|
|
775
820
|
|
|
@@ -973,6 +1018,17 @@ def write_terminal_evidence(
|
|
|
973
1018
|
artifact_id = _terminal_artifact_id(run_id)
|
|
974
1019
|
report_id = f"report:{run_id}"
|
|
975
1020
|
target = data_root / "artifacts" / "projects" / str(row[1]) / "runs" / run_id / "terminal-evidence-v1.json"
|
|
1021
|
+
with sqlite_connection(database) as connection:
|
|
1022
|
+
active_artifact = _active_terminal_artifact(connection, run_id)
|
|
1023
|
+
if active_artifact is not None and verify_artifact_integrity(
|
|
1024
|
+
repository_root, str(active_artifact[0]), central_database=database,
|
|
1025
|
+
artifact_root=data_root / "artifacts",
|
|
1026
|
+
):
|
|
1027
|
+
# Reconciliation may revisit a run after its original immutable
|
|
1028
|
+
# projection was superseded through the guarded owning repair route.
|
|
1029
|
+
# Return the one currently selected, integrity-verified artifact; do
|
|
1030
|
+
# not reconstruct or overwrite either historical payload.
|
|
1031
|
+
return str(active_artifact[0])
|
|
976
1032
|
if _is_retained_v13_terminal_artifact(
|
|
977
1033
|
target, run_id=run_id, submission_id=str(row[0]), project_id=str(row[1]),
|
|
978
1034
|
repository_id=str(row[2]),
|
|
@@ -980,10 +1036,6 @@ def write_terminal_evidence(
|
|
|
980
1036
|
repository_root, artifact_id, central_database=database,
|
|
981
1037
|
artifact_root=data_root / "artifacts",
|
|
982
1038
|
):
|
|
983
|
-
# Reconciliation may revisit terminal runs long after a new artifact
|
|
984
|
-
# contract ships. The older evidence remains immutable and truthful;
|
|
985
|
-
# require its pre-existing CENTRAL digest record rather than adopting
|
|
986
|
-
# a same-account filesystem payload as evidence.
|
|
987
1039
|
return artifact_id
|
|
988
1040
|
assurance_status, current_reviews, reviews = _current_assurance(checkpoint)
|
|
989
1041
|
findings = [finding for review in reviews for finding in review.get("findings", [])]
|
|
@@ -1162,13 +1214,14 @@ def producer_readback(
|
|
|
1162
1214
|
evidence["status"] = "INCOMPLETE"
|
|
1163
1215
|
elif terminal:
|
|
1164
1216
|
run.update(timing)
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1217
|
+
try:
|
|
1218
|
+
artifact = _active_terminal_artifact(connection, str(run_id))
|
|
1219
|
+
except SubmissionError:
|
|
1220
|
+
artifact = None
|
|
1221
|
+
evidence["status"] = "CORRUPT"
|
|
1170
1222
|
if artifact is None:
|
|
1171
|
-
evidence["status"]
|
|
1223
|
+
if evidence["status"] != "CORRUPT":
|
|
1224
|
+
evidence["status"] = "MISSING"
|
|
1172
1225
|
elif artifact[1] != "sha256" or not isinstance(artifact[2], str) or len(str(artifact[2])) != 64:
|
|
1173
1226
|
evidence["status"] = "CORRUPT"
|
|
1174
1227
|
else:
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"""Guarded reconciliation of one known terminal-evidence projection defect.
|
|
2
|
+
|
|
3
|
+
The route never changes a terminal run, accepted request, assurance record or
|
|
4
|
+
historical artifact bytes. It may add one corrected v1.4 projection only when
|
|
5
|
+
the retained checkpoint and complete current assurance set prove that the
|
|
6
|
+
legacy merge writer projected the execution baseline as its candidate.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import hashlib
|
|
12
|
+
import json
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
import re
|
|
15
|
+
import sqlite3
|
|
16
|
+
|
|
17
|
+
from . import central_database, submission_service
|
|
18
|
+
from .agent_state import RUN_ID_PATTERN, StateError, TransactionState
|
|
19
|
+
from .storage import record_artifact, sqlite_connection
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
_OPERATION_ID = re.compile(r"[a-z0-9][a-z0-9._:-]{7,127}\Z")
|
|
23
|
+
_SHA = re.compile(r"[0-9a-f]{40}\Z")
|
|
24
|
+
_REASON = "MERGE_CANDIDATE_PROJECTION_V1"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TerminalEvidenceReconciliationError(ValueError):
|
|
28
|
+
"""A stable, secret-free refusal of a terminal-evidence repair."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _result(row: tuple[object, ...]) -> dict[str, object]:
|
|
32
|
+
return {
|
|
33
|
+
"operation_id": str(row[0]),
|
|
34
|
+
"run_id": str(row[1]),
|
|
35
|
+
"project_id": str(row[2]),
|
|
36
|
+
"source_artifact_id": str(row[3]),
|
|
37
|
+
"source_digest": "sha256:" + str(row[4]),
|
|
38
|
+
"replacement_artifact_id": str(row[5]),
|
|
39
|
+
"replacement_digest": "sha256:" + str(row[6]),
|
|
40
|
+
"reason_code": str(row[7]),
|
|
41
|
+
"recorded_at": str(row[8]),
|
|
42
|
+
"state": "SUCCEEDED",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _operation(connection: sqlite3.Connection, operation_id: str) -> tuple[object, ...] | None:
|
|
47
|
+
row = connection.execute(
|
|
48
|
+
"""SELECT operation_id,run_id,project_id,source_artifact_id,source_digest,
|
|
49
|
+
replacement_artifact_id,replacement_digest,reason_code,recorded_at
|
|
50
|
+
FROM ep_terminal_evidence_reconciliation_operations
|
|
51
|
+
WHERE operation_id=?""",
|
|
52
|
+
(operation_id,),
|
|
53
|
+
).fetchone()
|
|
54
|
+
return tuple(row) if row is not None else None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def reconcile_terminal_evidence(
|
|
58
|
+
data_root: Path, *, run_id: str, operation_id: str,
|
|
59
|
+
) -> dict[str, object]:
|
|
60
|
+
"""Add and select one corrected projection while retaining original bytes."""
|
|
61
|
+
if RUN_ID_PATTERN.fullmatch(run_id) is None:
|
|
62
|
+
raise TerminalEvidenceReconciliationError("RUN_ID_INVALID")
|
|
63
|
+
if _OPERATION_ID.fullmatch(operation_id) is None:
|
|
64
|
+
raise TerminalEvidenceReconciliationError("OPERATION_ID_INVALID")
|
|
65
|
+
data_root = data_root.resolve()
|
|
66
|
+
database = central_database.path(data_root)
|
|
67
|
+
artifact_root = data_root / "artifacts"
|
|
68
|
+
with sqlite_connection(database) as connection:
|
|
69
|
+
existing = _operation(connection, operation_id)
|
|
70
|
+
if existing is not None:
|
|
71
|
+
if str(existing[1]) != run_id:
|
|
72
|
+
raise TerminalEvidenceReconciliationError("OPERATION_ID_CONFLICT")
|
|
73
|
+
return _result(existing)
|
|
74
|
+
conflicting = connection.execute(
|
|
75
|
+
"SELECT operation_id FROM ep_terminal_evidence_reconciliation_operations WHERE run_id=?",
|
|
76
|
+
(run_id,),
|
|
77
|
+
).fetchone()
|
|
78
|
+
if conflicting is not None:
|
|
79
|
+
raise TerminalEvidenceReconciliationError("RUN_ALREADY_RECONCILED")
|
|
80
|
+
active = submission_service._active_terminal_artifact(connection, run_id)
|
|
81
|
+
row = connection.execute(
|
|
82
|
+
"""SELECT d.project_id,d.repository_id,d.submission_id,t.payload
|
|
83
|
+
FROM ep_parity_lifecycle_dispatches d
|
|
84
|
+
JOIN engineering_transactions t ON t.run_id=d.run_id
|
|
85
|
+
WHERE d.run_id=? AND d.state='COMPLETE'""",
|
|
86
|
+
(run_id,),
|
|
87
|
+
).fetchone()
|
|
88
|
+
if active is None or row is None:
|
|
89
|
+
raise TerminalEvidenceReconciliationError("TERMINAL_EVIDENCE_UNAVAILABLE")
|
|
90
|
+
source_id, algorithm, source_digest, _content_type, _integrity, source_location = active
|
|
91
|
+
if algorithm != "sha256" or not isinstance(source_digest, str):
|
|
92
|
+
raise TerminalEvidenceReconciliationError("SOURCE_ARTIFACT_INVALID")
|
|
93
|
+
try:
|
|
94
|
+
source_path = (artifact_root / str(source_location)).resolve()
|
|
95
|
+
source_path.relative_to(artifact_root.resolve())
|
|
96
|
+
source_bytes = source_path.read_bytes()
|
|
97
|
+
document = json.loads(source_bytes)
|
|
98
|
+
checkpoint = TransactionState.from_dict(json.loads(str(row[3])))
|
|
99
|
+
except (OSError, ValueError, TypeError, json.JSONDecodeError, StateError):
|
|
100
|
+
raise TerminalEvidenceReconciliationError("SOURCE_ARTIFACT_INVALID") from None
|
|
101
|
+
if hashlib.sha256(source_bytes).hexdigest() != source_digest:
|
|
102
|
+
raise TerminalEvidenceReconciliationError("SOURCE_ARTIFACT_INVALID")
|
|
103
|
+
assurance_status, current_reviews, _reviews = submission_service._current_assurance(checkpoint)
|
|
104
|
+
profile = checkpoint.assurance_profile
|
|
105
|
+
candidate = profile.get("candidate_sha") if isinstance(profile, dict) else None
|
|
106
|
+
repository = document.get("repository") if isinstance(document, dict) else None
|
|
107
|
+
submission = document.get("submission") if isinstance(document, dict) else None
|
|
108
|
+
producer = document.get("producer") if isinstance(document, dict) else None
|
|
109
|
+
run = document.get("run") if isinstance(document, dict) else None
|
|
110
|
+
assurance = document.get("assurance") if isinstance(document, dict) else None
|
|
111
|
+
if (
|
|
112
|
+
document.get("artifact_type") != "EP_TERMINAL_EVIDENCE"
|
|
113
|
+
or document.get("contract_version") != "1.4"
|
|
114
|
+
or not isinstance(repository, dict)
|
|
115
|
+
or not isinstance(submission, dict)
|
|
116
|
+
or not isinstance(producer, dict)
|
|
117
|
+
or not isinstance(run, dict)
|
|
118
|
+
or not isinstance(assurance, dict)
|
|
119
|
+
or run.get("id") != run_id
|
|
120
|
+
or run.get("outcome") != "COMPLETE"
|
|
121
|
+
or submission.get("id") != row[2]
|
|
122
|
+
or submission.get("project_id") != row[0]
|
|
123
|
+
or repository.get("id") != row[1]
|
|
124
|
+
or not isinstance(producer.get("id"), str)
|
|
125
|
+
or checkpoint.run_id != run_id
|
|
126
|
+
or not checkpoint.terminal
|
|
127
|
+
or checkpoint.phase != "COMPLETE"
|
|
128
|
+
or checkpoint.action_intent != "MUTATING_DELIVERY"
|
|
129
|
+
or assurance_status != "PASS"
|
|
130
|
+
or not isinstance(candidate, str)
|
|
131
|
+
or _SHA.fullmatch(candidate) is None
|
|
132
|
+
or assurance.get("profile") != profile
|
|
133
|
+
or len(current_reviews) < 2
|
|
134
|
+
):
|
|
135
|
+
raise TerminalEvidenceReconciliationError("RECONCILIATION_PRECONDITION_FAILED")
|
|
136
|
+
projected_candidate = repository.get("candidate")
|
|
137
|
+
if (
|
|
138
|
+
projected_candidate != checkpoint.implementation_head_sha
|
|
139
|
+
or projected_candidate != checkpoint.execution_baseline_sha
|
|
140
|
+
or projected_candidate == candidate
|
|
141
|
+
):
|
|
142
|
+
raise TerminalEvidenceReconciliationError("RECONCILIATION_DEFECT_NOT_PRESENT")
|
|
143
|
+
|
|
144
|
+
corrected = dict(document)
|
|
145
|
+
corrected["repository"] = {**repository, "candidate": candidate}
|
|
146
|
+
replacement_bytes = submission_service._canonical_json_bytes(corrected)
|
|
147
|
+
replacement_digest = hashlib.sha256(replacement_bytes).hexdigest()
|
|
148
|
+
replacement_id = f"terminal-evidence-reconciled:{run_id}:{replacement_digest[:16]}"
|
|
149
|
+
replacement_path = source_path.with_name(
|
|
150
|
+
f"terminal-evidence-v1-reconciled-{replacement_digest[:16]}.json"
|
|
151
|
+
)
|
|
152
|
+
submission_service._write_immutable_artifact(replacement_path, replacement_bytes)
|
|
153
|
+
record_artifact(
|
|
154
|
+
data_root,
|
|
155
|
+
replacement_path,
|
|
156
|
+
artifact_id=replacement_id,
|
|
157
|
+
artifact_type="EP_TERMINAL_EVIDENCE",
|
|
158
|
+
content_type="application/json",
|
|
159
|
+
created_at=datetime_now(),
|
|
160
|
+
run_id=run_id,
|
|
161
|
+
submission_id=str(row[2]),
|
|
162
|
+
producer_id=str(producer["id"]),
|
|
163
|
+
ep_run_id=run_id,
|
|
164
|
+
ep_submission_id=str(row[2]),
|
|
165
|
+
projection_status="CANDIDATE",
|
|
166
|
+
central_database=database,
|
|
167
|
+
artifact_root=artifact_root,
|
|
168
|
+
)
|
|
169
|
+
recorded_at = datetime_now()
|
|
170
|
+
with sqlite_connection(database) as connection:
|
|
171
|
+
try:
|
|
172
|
+
connection.execute("BEGIN IMMEDIATE")
|
|
173
|
+
existing = _operation(connection, operation_id)
|
|
174
|
+
if existing is not None:
|
|
175
|
+
connection.execute("ROLLBACK")
|
|
176
|
+
if str(existing[1]) != run_id:
|
|
177
|
+
raise TerminalEvidenceReconciliationError("OPERATION_ID_CONFLICT")
|
|
178
|
+
return _result(existing)
|
|
179
|
+
active_now = submission_service._active_terminal_artifact(connection, run_id)
|
|
180
|
+
if active_now is None or str(active_now[0]) != str(source_id) or str(active_now[2]) != source_digest:
|
|
181
|
+
raise TerminalEvidenceReconciliationError("SOURCE_ARTIFACT_CHANGED")
|
|
182
|
+
connection.execute(
|
|
183
|
+
"""INSERT INTO ep_terminal_evidence_reconciliation_operations(
|
|
184
|
+
operation_id,run_id,project_id,source_artifact_id,source_digest,
|
|
185
|
+
replacement_artifact_id,replacement_digest,reason_code,recorded_at
|
|
186
|
+
) VALUES(?,?,?,?,?,?,?,?,?)""",
|
|
187
|
+
(
|
|
188
|
+
operation_id, run_id, str(row[0]), str(source_id), source_digest,
|
|
189
|
+
replacement_id, replacement_digest, _REASON, recorded_at,
|
|
190
|
+
),
|
|
191
|
+
)
|
|
192
|
+
source_updated = connection.execute(
|
|
193
|
+
"""UPDATE execution_artifact_records SET projection_status='SUPERSEDED'
|
|
194
|
+
WHERE artifact_id=? AND digest=? AND projection_status='AVAILABLE'""",
|
|
195
|
+
(str(source_id), source_digest),
|
|
196
|
+
)
|
|
197
|
+
replacement_updated = connection.execute(
|
|
198
|
+
"""UPDATE execution_artifact_records SET projection_status='AVAILABLE'
|
|
199
|
+
WHERE artifact_id=? AND digest=? AND projection_status='CANDIDATE'""",
|
|
200
|
+
(replacement_id, replacement_digest),
|
|
201
|
+
)
|
|
202
|
+
if source_updated.rowcount != 1 or replacement_updated.rowcount != 1:
|
|
203
|
+
raise TerminalEvidenceReconciliationError("PROJECTION_TRANSITION_CONFLICT")
|
|
204
|
+
connection.execute("COMMIT")
|
|
205
|
+
except Exception:
|
|
206
|
+
try:
|
|
207
|
+
connection.execute("ROLLBACK")
|
|
208
|
+
except sqlite3.DatabaseError:
|
|
209
|
+
pass
|
|
210
|
+
raise
|
|
211
|
+
result = _operation(connection, operation_id)
|
|
212
|
+
if result is None:
|
|
213
|
+
raise TerminalEvidenceReconciliationError("RECONCILIATION_RECEIPT_UNAVAILABLE")
|
|
214
|
+
return _result(result)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def datetime_now() -> str:
|
|
218
|
+
from datetime import datetime, timezone
|
|
219
|
+
|
|
220
|
+
return datetime.now(timezone.utc).isoformat()
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def main(argv: list[str] | None = None) -> int:
|
|
224
|
+
parser = argparse.ArgumentParser(
|
|
225
|
+
prog="engineering-terminal-evidence-reconcile",
|
|
226
|
+
description="Reconcile one exact historical terminal candidate projection.",
|
|
227
|
+
)
|
|
228
|
+
parser.add_argument("--data-root", type=Path, required=True)
|
|
229
|
+
parser.add_argument("--run-id", required=True)
|
|
230
|
+
parser.add_argument("--operation-id", required=True)
|
|
231
|
+
args = parser.parse_args(argv)
|
|
232
|
+
try:
|
|
233
|
+
result = reconcile_terminal_evidence(
|
|
234
|
+
args.data_root, run_id=args.run_id, operation_id=args.operation_id,
|
|
235
|
+
)
|
|
236
|
+
except TerminalEvidenceReconciliationError as error:
|
|
237
|
+
print(json.dumps({"state": "BLOCKED", "reason_code": str(error)}, sort_keys=True))
|
|
238
|
+
return 2
|
|
239
|
+
print(json.dumps(result, sort_keys=True))
|
|
240
|
+
return 0
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
if __name__ == "__main__":
|
|
244
|
+
raise SystemExit(main())
|
|
@@ -127,6 +127,7 @@ src/engineering_platform/submission_service.py
|
|
|
127
127
|
src/engineering_platform/system_installation_topology.py
|
|
128
128
|
src/engineering_platform/system_server_service.py
|
|
129
129
|
src/engineering_platform/telemetry.py
|
|
130
|
+
src/engineering_platform/terminal_evidence_reconciliation.py
|
|
130
131
|
src/engineering_platform/validation_identity.py
|
|
131
132
|
src/engineering_platform/validation_profile.py
|
|
132
133
|
src/engineering_platform/version_preparation_delivery.py
|
|
File without changes
|
|
File without changes
|
{engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/__init__.py
RENAMED
|
File without changes
|
{engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/__main__.py
RENAMED
|
File without changes
|
{engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/agent_state.py
RENAMED
|
File without changes
|
{engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/agent_trust.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{engineering_platform-2.3.73 → engineering_platform-2.3.75}/src/engineering_platform/codex_chat.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|