engineering-platform 2.3.90__tar.gz → 2.3.91__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.90 → engineering_platform-2.3.91}/PKG-INFO +1 -1
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/pyproject.toml +1 -1
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/ENGINEERING_PLATFORM_CONFIG.json +1 -1
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/ENGINEERING_PLATFORM_VERSION.json +4 -4
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_host.py +83 -4
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_repository.py +76 -0
- engineering_platform-2.3.91/src/engineering_platform/managed_workspace_readiness.py +85 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/parity_context.py +5 -2
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/parity_lifecycle_dispatcher.py +8 -1
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/platform_version.py +1 -1
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/revision_binding.py +10 -4
- engineering_platform-2.3.91/src/engineering_platform/schemas/producer-readback-v1.3.schema.json +103 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/server.py +31 -3
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/submission_service.py +42 -3
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/templates/workspace-config.json +1 -1
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/workspace_preflight.py +10 -2
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform.egg-info/PKG-INFO +1 -1
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform.egg-info/SOURCES.txt +2 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/README.md +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/setup.cfg +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/__init__.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/__main__.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/agent_state.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/agent_trust.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/dashboard.css +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/dashboard.js +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/dashboard_locales.mjs +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/dashboard_status_store.mjs +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/dashboard_translation.mjs +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/apple-touch-icon-dark.png +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/apple-touch-icon-light.png +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/icon-dark.png +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/icon-light.png +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/icon-transparent.png +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assets/operations-console/manifest.webmanifest +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/assurance_scope.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/capability_preflight.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/capability_review.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/central_data_transfer.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/central_database.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/central_operational_reset.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/central_store_migration.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/codex_capacity.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/codex_chat.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/codex_observability.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/component_lock.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/component_logging.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/console_presentation.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/console_route_ownership.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/contracts/__init__.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/contracts/ep_consumer.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/contracts/models.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/contracts/projection.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/dashboard_browser_validation.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/dashboard_state.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/dashboard_supervisor.swift +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/dashboard_translation.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/dependabot_producer.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/development_profile.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/drift_diagnostics.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/emergency_recovery.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/engineering_memory.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/ep_consumer_credentials.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/evidence_projection.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_activity.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_context.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_errors.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_evidence.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_executor.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_finalization.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_host_evidence.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_lease.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_lifecycle.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_models.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_readiness.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_reporting.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_timeout_policy.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_timing.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/execution_transaction.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/external_producer_binding.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/file_inbox.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/forensic_attribution.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/forensic_attribution_v2.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/forensic_delta.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/golden_scenario.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/historical_dashboard_configuration.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/host_admin.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/host_preflight.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_relocation.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_activation.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_admission.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_backup.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_composition.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_executor.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_migration.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_operation.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_plan.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/installation_update_preparation.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/investigation_ledger.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/legacy_inbox_migration.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/legacy_installation_adoption.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/lifecycle_worker.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/live_status.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/local_api.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/local_api_keychain.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/local_repository_binding.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/managed_autonomy.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/managed_codex_runtime.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/merge_delegation.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/operational_installation.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/operational_installation_lock.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/operational_installation_record.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/owner_credential_recovery.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/platform_admin.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/platform_api.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/platform_bootstrap.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/platform_components.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/pr_check_repair.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/pr_evidence_backfill.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/producer.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/product_installation_readback.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/project_agent.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/project_agent_service.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/project_topology.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/prompt_history.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/provider_context.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/provider_context_benchmark.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/provider_context_scope.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/provider_interruption.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/provider_process_identity.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/provider_readiness.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/provider_recovery.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/provider_usage.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/providers.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/qualification.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/qualification_runtime.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/recommendation_handoff.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/reconciliation_adoption.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/release_operation.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/report_analysis.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/repository_attachment.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/repository_handoff.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/resources.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/reviewer_evidence.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/schemas/producer-readback-v1.2.schema.json +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/schemas/producer-readback-v1.schema.json +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/schemas/repository-attachment.schema.json +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/server_console_services.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/server_relay.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/server_service.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/status_model.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/status_reconciliation.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/storage.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/submission_cli.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/submission_intake.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/system_installation_topology.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/system_server_service.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/telemetry.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/telemetry_contract.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/telemetry_export.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/telemetry_metrics.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/terminal_evidence_reconciliation.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/validation_identity.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/validation_profile.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/version_preparation_delivery.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/worktree_provenance.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/worktree_tooling.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform.egg-info/dependency_links.txt +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform.egg-info/entry_points.txt +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform.egg-info/top_level.txt +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/tests/test_adaptive_action_sizing_design.py +0 -0
- {engineering_platform-2.3.90 → engineering_platform-2.3.91}/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.91",
|
|
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.91",
|
|
10
10
|
"report_format": 2,
|
|
11
|
-
"runner_version": "2.3.
|
|
11
|
+
"runner_version": "2.3.91",
|
|
12
12
|
"status_model": 1,
|
|
13
13
|
"storage_schema": 45,
|
|
14
|
-
"watcher_version": "2.3.
|
|
14
|
+
"watcher_version": "2.3.91"
|
|
15
15
|
}
|
|
@@ -3086,6 +3086,15 @@ First implementation pull-request publication gate:
|
|
|
3086
3086
|
"Accepted repository revision binding is invalid.",
|
|
3087
3087
|
)
|
|
3088
3088
|
if revision_binding is not None:
|
|
3089
|
+
if (revision_binding.repository_identity is not None
|
|
3090
|
+
and (evidence.repository != revision_binding.repository_identity
|
|
3091
|
+
or not callable(getattr(self.repository, "trusted_origin_identity", None))
|
|
3092
|
+
or self.repository.trusted_origin_identity(self.root)
|
|
3093
|
+
!= revision_binding.repository_identity)):
|
|
3094
|
+
return self._save_terminal(
|
|
3095
|
+
state, "BLOCKED", "repository_identity_binding",
|
|
3096
|
+
"Managed workspace origin differs from the authorized repository identity.",
|
|
3097
|
+
)
|
|
3089
3098
|
if state.requested_repository_revision not in {None, revision_binding.requested_revision}:
|
|
3090
3099
|
return self._save_terminal(
|
|
3091
3100
|
state, "BLOCKED", "repository_revision_binding",
|
|
@@ -3212,10 +3221,47 @@ First implementation pull-request publication gate:
|
|
|
3212
3221
|
self.transaction = self.transaction.with_lease(self.active_lease)
|
|
3213
3222
|
self.lease_heartbeat.start()
|
|
3214
3223
|
if recovered_resume:
|
|
3224
|
+
self._heartbeat()
|
|
3225
|
+
if revision_binding is not None and context.execution_mode == "MANAGED":
|
|
3226
|
+
recovered_head = self.repository.inspect(self.root)
|
|
3227
|
+
expected_baseline = (
|
|
3228
|
+
revision_binding.allowed_baseline_revision
|
|
3229
|
+
or revision_binding.requested_revision
|
|
3230
|
+
)
|
|
3231
|
+
if (state.execution_baseline_sha != expected_baseline
|
|
3232
|
+
or recovered_head.repository != state.repository
|
|
3233
|
+
or not recovered_head.clean
|
|
3234
|
+
or self.repository.workspace_operation_active(self.root)
|
|
3235
|
+
or (state.branch is not None and recovered_head.branch not in {state.branch, "main"})
|
|
3236
|
+
or not callable(getattr(self.repository, "trusted_origin_identity", None))
|
|
3237
|
+
or self.repository.trusted_origin_identity(self.root) != state.repository
|
|
3238
|
+
or not callable(getattr(self.repository, "revision_is_ancestor", None))
|
|
3239
|
+
or not self.repository.revision_is_ancestor(
|
|
3240
|
+
self.root, expected_baseline, recovered_head.head_sha,
|
|
3241
|
+
)):
|
|
3242
|
+
return self._save_terminal(
|
|
3243
|
+
state, "BLOCKED", "recovered_workspace_baseline",
|
|
3244
|
+
"Recovered provider result lacks the authorized workspace baseline.",
|
|
3245
|
+
)
|
|
3215
3246
|
# Consume immutable attempt-two evidence before any fresh provider
|
|
3216
3247
|
# preparation or repository synchronization. This preserves the
|
|
3217
3248
|
# original branch/worktree and cannot allocate a new invocation.
|
|
3218
3249
|
result = self._invoke_agent_with_timing(state, "")
|
|
3250
|
+
if (revision_binding is not None and context.execution_mode == "MANAGED"
|
|
3251
|
+
and recovery_snapshot["lifecycle_phase"] == "EXECUTE_AGENT"
|
|
3252
|
+
and result.terminal_state == "COMPLETE"):
|
|
3253
|
+
result_head = self.repository.inspect(self.root)
|
|
3254
|
+
if (not result.commit_sha or not result.branch
|
|
3255
|
+
or result_head.head_sha != result.commit_sha
|
|
3256
|
+
or result_head.branch != result.branch or not result_head.clean
|
|
3257
|
+
or self.repository.trusted_origin_identity(self.root) != state.repository
|
|
3258
|
+
or not self.repository.revision_is_ancestor(
|
|
3259
|
+
self.root, state.execution_baseline_sha, result.commit_sha,
|
|
3260
|
+
)):
|
|
3261
|
+
return self._save_terminal(
|
|
3262
|
+
state, "BLOCKED", "recovered_workspace_result_drift",
|
|
3263
|
+
"Recovered provider result no longer matches the bound workspace commit.",
|
|
3264
|
+
)
|
|
3219
3265
|
state = self._record_agent_execution_time(state)
|
|
3220
3266
|
state = self._record_validation_evidence(state, result)
|
|
3221
3267
|
state = self._record_verified_result_commit(
|
|
@@ -3229,10 +3275,18 @@ First implementation pull-request publication gate:
|
|
|
3229
3275
|
# and so the bounded retry policy in the repository client is used.
|
|
3230
3276
|
if context.execution_mode == "MANAGED":
|
|
3231
3277
|
try:
|
|
3232
|
-
#
|
|
3233
|
-
#
|
|
3234
|
-
#
|
|
3235
|
-
if revision_binding is None
|
|
3278
|
+
# An exact pin is prepared by the owning repository client
|
|
3279
|
+
# under this lease. It may fast forward only to that SHA,
|
|
3280
|
+
# never to an ambient newer origin/main.
|
|
3281
|
+
if revision_binding is not None and revision_binding.allowed_baseline_revision is None:
|
|
3282
|
+
prepare = getattr(self.repository, "prepare_main_revision", None)
|
|
3283
|
+
if not callable(prepare):
|
|
3284
|
+
raise RunnerError("MANAGED_PREPARATION_UNAVAILABLE")
|
|
3285
|
+
evidence = prepare(
|
|
3286
|
+
self.root, revision_binding.requested_revision,
|
|
3287
|
+
revision_binding.repository_identity or state.repository,
|
|
3288
|
+
)
|
|
3289
|
+
else:
|
|
3236
3290
|
self.repository.synchronize_main(self.root)
|
|
3237
3291
|
# The initial observation predates lease acquisition. Always
|
|
3238
3292
|
# refresh it here: an exact pin skips synchronization, never
|
|
@@ -3326,6 +3380,19 @@ First implementation pull-request publication gate:
|
|
|
3326
3380
|
self.reviewer_runtime,
|
|
3327
3381
|
)
|
|
3328
3382
|
self._require_provider_dispatch_admission(state)
|
|
3383
|
+
self._heartbeat()
|
|
3384
|
+
if state.execution_mode == "MANAGED" and state.execution_baseline_sha is not None:
|
|
3385
|
+
reviewer_head = self.repository.inspect(self.root)
|
|
3386
|
+
if (reviewer_head.repository != state.repository or reviewer_head.branch != "main"
|
|
3387
|
+
or not reviewer_head.clean or reviewer_head.head_sha != state.execution_baseline_sha
|
|
3388
|
+
or self.repository.workspace_operation_active(self.root)
|
|
3389
|
+
or (state.requested_repository_revision is not None
|
|
3390
|
+
and self.repository.trusted_origin_identity(self.root) != state.repository)):
|
|
3391
|
+
complete_phase(self.root, capability_review, outcome="FAILED")
|
|
3392
|
+
return self._save_terminal(
|
|
3393
|
+
state, "BLOCKED", "managed_workspace_drift",
|
|
3394
|
+
"Managed workspace changed after baseline admission and before capability review.",
|
|
3395
|
+
)
|
|
3329
3396
|
results = run_reviews(
|
|
3330
3397
|
self.root,
|
|
3331
3398
|
selections,
|
|
@@ -3426,6 +3493,18 @@ First implementation pull-request publication gate:
|
|
|
3426
3493
|
),
|
|
3427
3494
|
)[1]
|
|
3428
3495
|
)
|
|
3496
|
+
if state.execution_mode == "MANAGED" and state.execution_baseline_sha is not None:
|
|
3497
|
+
self._heartbeat()
|
|
3498
|
+
execution_head = self.repository.inspect(self.root)
|
|
3499
|
+
if (execution_head.repository != state.repository or execution_head.branch != "main"
|
|
3500
|
+
or not execution_head.clean or execution_head.head_sha != state.execution_baseline_sha
|
|
3501
|
+
or self.repository.workspace_operation_active(self.root)
|
|
3502
|
+
or (state.requested_repository_revision is not None
|
|
3503
|
+
and self.repository.trusted_origin_identity(self.root) != state.repository)):
|
|
3504
|
+
return self._save_terminal(
|
|
3505
|
+
state, "BLOCKED", "managed_workspace_drift",
|
|
3506
|
+
"Managed workspace changed after baseline admission and before provider implementation.",
|
|
3507
|
+
)
|
|
3429
3508
|
result = self._invoke_agent_with_timing(
|
|
3430
3509
|
state,
|
|
3431
3510
|
assemble_prompt(
|
|
@@ -31,6 +31,18 @@ def github_repository_slug(remote: str) -> str:
|
|
|
31
31
|
return value
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
def trusted_github_repository_slug(remote: str) -> str | None:
|
|
35
|
+
"""Resolve only an authorized GitHub transport, never a path or alias."""
|
|
36
|
+
value = remote.strip()
|
|
37
|
+
if value.startswith("git@github.com:"):
|
|
38
|
+
slug = value.removeprefix("git@github.com:").removesuffix(".git")
|
|
39
|
+
elif value.startswith("https://github.com/"):
|
|
40
|
+
slug = value.removeprefix("https://github.com/").removesuffix(".git")
|
|
41
|
+
else:
|
|
42
|
+
return None
|
|
43
|
+
return slug if re.fullmatch(r"[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+", slug) else None
|
|
44
|
+
|
|
45
|
+
|
|
34
46
|
class RepositoryClient(Protocol):
|
|
35
47
|
def inspect(self, root: Path) -> RepositoryEvidence: ...
|
|
36
48
|
def main_contains(self, root: Path, sha: str) -> bool: ...
|
|
@@ -38,6 +50,10 @@ class RepositoryClient(Protocol):
|
|
|
38
50
|
def refresh_main_reference(self, root: Path) -> None: ...
|
|
39
51
|
def remote_main_contains(self, root: Path, sha: str) -> bool: ...
|
|
40
52
|
def synchronize_main(self, root: Path) -> None: ...
|
|
53
|
+
def prepare_main_revision(self, root: Path, revision: str, repository_id: str) -> RepositoryEvidence: ...
|
|
54
|
+
def revision_is_ancestor(self, root: Path, ancestor: str, descendant: str) -> bool: ...
|
|
55
|
+
def trusted_origin_identity(self, root: Path) -> str | None: ...
|
|
56
|
+
def workspace_operation_active(self, root: Path) -> bool: ...
|
|
41
57
|
|
|
42
58
|
|
|
43
59
|
class GitHubClient(Protocol):
|
|
@@ -73,6 +89,19 @@ class SubprocessRepositoryClient:
|
|
|
73
89
|
contained = self.provider.execute(root, "git", "merge-base", "--is-ancestor", head_sha, "main").returncode == 0
|
|
74
90
|
return RepositoryEvidence(repository, branch, head_sha, clean, contained)
|
|
75
91
|
|
|
92
|
+
def trusted_origin_identity(self, root: Path) -> str | None:
|
|
93
|
+
return trusted_github_repository_slug(self._run(root, "git", "remote", "get-url", "origin"))
|
|
94
|
+
|
|
95
|
+
def workspace_operation_active(self, root: Path) -> bool:
|
|
96
|
+
for name in ("index.lock", "MERGE_HEAD", "REBASE_HEAD", "CHERRY_PICK_HEAD",
|
|
97
|
+
"REVERT_HEAD", "BISECT_LOG", "rebase-merge", "rebase-apply"):
|
|
98
|
+
path = Path(self._run(root, "git", "rev-parse", "--git-path", name))
|
|
99
|
+
if not path.is_absolute():
|
|
100
|
+
path = root / path
|
|
101
|
+
if path.exists():
|
|
102
|
+
return True
|
|
103
|
+
return False
|
|
104
|
+
|
|
76
105
|
def main_contains(self, root: Path, sha: str) -> bool:
|
|
77
106
|
return self.provider.execute(root, "git", "merge-base", "--is-ancestor", sha, "main").returncode == 0
|
|
78
107
|
|
|
@@ -91,6 +120,13 @@ class SubprocessRepositoryClient:
|
|
|
91
120
|
def remote_main_contains(self, root: Path, sha: str) -> bool:
|
|
92
121
|
return self.provider.execute(root, "git", "merge-base", "--is-ancestor", sha, "origin/main").returncode == 0
|
|
93
122
|
|
|
123
|
+
def revision_is_ancestor(self, root: Path, ancestor: str, descendant: str) -> bool:
|
|
124
|
+
if not all(re.fullmatch(r"[0-9a-f]{40}", value) for value in (ancestor, descendant)):
|
|
125
|
+
return False
|
|
126
|
+
return self.provider.execute(
|
|
127
|
+
root, "git", "merge-base", "--is-ancestor", ancestor, descendant,
|
|
128
|
+
).returncode == 0
|
|
129
|
+
|
|
94
130
|
@staticmethod
|
|
95
131
|
def _is_transient_index_lock_conflict(error: RuntimeError) -> bool:
|
|
96
132
|
"""Recognize only Git's contention signal, never a permission failure."""
|
|
@@ -180,6 +216,46 @@ class SubprocessRepositoryClient:
|
|
|
180
216
|
f"{self._managed_sync_diagnostics(root)}; {self._managed_sync_error_detail(error)}"
|
|
181
217
|
) from error
|
|
182
218
|
|
|
219
|
+
def prepare_main_revision(self, root: Path, revision: str, repository_id: str) -> RepositoryEvidence:
|
|
220
|
+
"""Fast forward a clean Managed main to one authorized protected revision.
|
|
221
|
+
|
|
222
|
+
The caller owns the execution lease. This method never resets local
|
|
223
|
+
work, switches branches, or treats a branch name as commit evidence.
|
|
224
|
+
"""
|
|
225
|
+
if re.fullmatch(r"[0-9a-f]{40}", revision) is None:
|
|
226
|
+
raise RunnerError("MANAGED_PREPARATION_INVALID_REVISION")
|
|
227
|
+
before = self.inspect(root)
|
|
228
|
+
if self.trusted_origin_identity(root) != repository_id:
|
|
229
|
+
raise RunnerError("MANAGED_PREPARATION_ORIGIN_UNTRUSTED")
|
|
230
|
+
if before.repository != repository_id:
|
|
231
|
+
raise RunnerError("MANAGED_PREPARATION_REPOSITORY_MISMATCH")
|
|
232
|
+
if before.branch != "main" or not before.clean or not before.main_contains_head:
|
|
233
|
+
raise RunnerError("MANAGED_PREPARATION_UNSAFE_WORKTREE")
|
|
234
|
+
if self.workspace_operation_active(root):
|
|
235
|
+
raise RunnerError("MANAGED_PREPARATION_GIT_OPERATION_ACTIVE")
|
|
236
|
+
self._synchronize_command(root, "git", "fetch", "--prune", "origin", "main")
|
|
237
|
+
if not self.remote_main_contains(root, revision):
|
|
238
|
+
raise RunnerError("MANAGED_PREPARATION_REVISION_NOT_ON_PROTECTED_MAIN")
|
|
239
|
+
if before.head_sha == revision:
|
|
240
|
+
if (self.inspect(root) != before or self.trusted_origin_identity(root) != repository_id
|
|
241
|
+
or self.workspace_operation_active(root)):
|
|
242
|
+
raise RunnerError("MANAGED_PREPARATION_WORKSPACE_DRIFT")
|
|
243
|
+
return before
|
|
244
|
+
if self.provider.execute(root, "git", "merge-base", "--is-ancestor", before.head_sha, revision).returncode:
|
|
245
|
+
raise RunnerError("MANAGED_PREPARATION_NON_FAST_FORWARD")
|
|
246
|
+
after_fetch = self.inspect(root)
|
|
247
|
+
if (after_fetch != before or self.trusted_origin_identity(root) != repository_id
|
|
248
|
+
or self.workspace_operation_active(root)):
|
|
249
|
+
raise RunnerError("MANAGED_PREPARATION_WORKSPACE_DRIFT")
|
|
250
|
+
self._synchronize_command(root, "git", "merge", "--ff-only", revision)
|
|
251
|
+
prepared = self.inspect(root)
|
|
252
|
+
if (prepared.repository != repository_id or prepared.branch != "main" or not prepared.clean
|
|
253
|
+
or not prepared.main_contains_head or prepared.head_sha != revision
|
|
254
|
+
or self.trusted_origin_identity(root) != repository_id
|
|
255
|
+
or self.workspace_operation_active(root)):
|
|
256
|
+
raise RunnerError("MANAGED_PREPARATION_RESULT_UNCERTAIN")
|
|
257
|
+
return prepared
|
|
258
|
+
|
|
183
259
|
def cleanup_transaction(self, root: Path, branches: tuple[str | None, ...]) -> str:
|
|
184
260
|
self._run(root, "git", "fetch", "--prune"); self.synchronize_main(root)
|
|
185
261
|
if not self.inspect(root).clean: raise RunnerError("Cleanup blocked: workspace is not clean.")
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Read-only, project-scoped Managed workspace capability inspection."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import re
|
|
8
|
+
import sqlite3
|
|
9
|
+
|
|
10
|
+
from .execution_repository import trusted_github_repository_slug
|
|
11
|
+
from .providers import GitProvider
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def project_readiness(connection: sqlite3.Connection, *, project_id: str,
|
|
15
|
+
repository_id: str) -> dict[str, object]:
|
|
16
|
+
"""Report known blockers without planning or changing the checkout."""
|
|
17
|
+
observed_at = datetime.now(timezone.utc).isoformat()
|
|
18
|
+
binding = connection.execute(
|
|
19
|
+
"SELECT b.local_root FROM ep_local_repository_bindings b "
|
|
20
|
+
"JOIN ep_repository_registrations r ON r.repository_id=b.repository_id "
|
|
21
|
+
"AND r.project_id=b.project_id AND r.role='authority' "
|
|
22
|
+
"WHERE b.project_id=? AND b.repository_id=? AND b.state='BOUND'",
|
|
23
|
+
(project_id, repository_id),
|
|
24
|
+
).fetchone()
|
|
25
|
+
response: dict[str, object] = {
|
|
26
|
+
"contract_version": "1.0", "project_id": project_id,
|
|
27
|
+
"repository_id": repository_id,
|
|
28
|
+
"managed_workspace_id": f"{project_id}:{repository_id}",
|
|
29
|
+
"execution_mode": "MANAGED", "repository_identity": None,
|
|
30
|
+
"origin": None, "head_sha": None, "branch": None, "clean": None,
|
|
31
|
+
"busy": None, "active_lease": None,
|
|
32
|
+
"preparation_capability": "EXACT_MAIN_FAST_FORWARD_V1",
|
|
33
|
+
"status": "BLOCKED", "known_blocker": "MANAGED_WORKSPACE_UNBOUND",
|
|
34
|
+
"observed_at": observed_at,
|
|
35
|
+
}
|
|
36
|
+
if binding is None:
|
|
37
|
+
return response
|
|
38
|
+
root = Path(str(binding[0]))
|
|
39
|
+
git = GitProvider()
|
|
40
|
+
|
|
41
|
+
def read(*args: str) -> str | None:
|
|
42
|
+
try:
|
|
43
|
+
result = git.execute(root, "git", *args)
|
|
44
|
+
except (OSError, RuntimeError):
|
|
45
|
+
return None
|
|
46
|
+
return result.stdout.strip() if result.returncode == 0 else None
|
|
47
|
+
|
|
48
|
+
head = read("rev-parse", "--verify", "HEAD")
|
|
49
|
+
branch = read("branch", "--show-current")
|
|
50
|
+
remote = read("remote", "get-url", "origin")
|
|
51
|
+
status = read("status", "--porcelain", "--untracked-files=all")
|
|
52
|
+
identity = trusted_github_repository_slug(remote) if remote else None
|
|
53
|
+
active_lease = connection.execute(
|
|
54
|
+
"SELECT 1 FROM execution_run_leases l JOIN ep_execution_runs r ON r.run_id=l.run_id "
|
|
55
|
+
"WHERE r.project_id=? AND l.lease_state='ACTIVE' LIMIT 1",
|
|
56
|
+
(project_id,),
|
|
57
|
+
).fetchone() is not None
|
|
58
|
+
operation = False
|
|
59
|
+
for name in ("index.lock", "MERGE_HEAD", "REBASE_HEAD", "CHERRY_PICK_HEAD",
|
|
60
|
+
"REVERT_HEAD", "BISECT_LOG", "rebase-merge", "rebase-apply"):
|
|
61
|
+
marker = read("rev-parse", "--git-path", name)
|
|
62
|
+
if marker is None:
|
|
63
|
+
operation = True
|
|
64
|
+
break
|
|
65
|
+
path = Path(marker)
|
|
66
|
+
if (path if path.is_absolute() else root / path).exists():
|
|
67
|
+
operation = True
|
|
68
|
+
break
|
|
69
|
+
blocker = (
|
|
70
|
+
"MANAGED_WORKSPACE_UNAVAILABLE" if head is None or status is None or branch is None
|
|
71
|
+
or re.fullmatch(r"[0-9a-f]{40}", head) is None else
|
|
72
|
+
"MANAGED_ORIGIN_UNTRUSTED" if identity is None else
|
|
73
|
+
"MANAGED_BRANCH_UNEXPECTED" if branch != "main" else
|
|
74
|
+
"MANAGED_WORKSPACE_DIRTY" if status else
|
|
75
|
+
"MANAGED_GIT_OPERATION_ACTIVE" if operation else
|
|
76
|
+
"MANAGED_LEASE_ACTIVE" if active_lease else None
|
|
77
|
+
)
|
|
78
|
+
response.update({
|
|
79
|
+
"repository_identity": identity, "origin": identity,
|
|
80
|
+
"head_sha": head if head and re.fullmatch(r"[0-9a-f]{40}", head) else None,
|
|
81
|
+
"branch": branch, "clean": status == "" if status is not None else None,
|
|
82
|
+
"busy": operation or active_lease, "active_lease": active_lease,
|
|
83
|
+
"status": "READY" if blocker is None else "BLOCKED", "known_blocker": blocker,
|
|
84
|
+
})
|
|
85
|
+
return response
|
|
@@ -78,8 +78,11 @@ class ParityProjectStore:
|
|
|
78
78
|
|
|
79
79
|
def queued_submissions(self) -> list[dict[str, object]]:
|
|
80
80
|
rows = self.connection.execute(
|
|
81
|
-
"SELECT submission_id,repository_id,state,admission,created_at,prompt_digest "
|
|
82
|
-
"FROM ep_submissions
|
|
81
|
+
"SELECT s.submission_id,s.repository_id,s.state,s.admission,s.created_at,s.prompt_digest "
|
|
82
|
+
"FROM ep_submissions AS s LEFT JOIN ep_parity_lifecycle_dispatches AS d "
|
|
83
|
+
"ON d.submission_id=s.submission_id WHERE s.project_id=? AND d.submission_id IS NULL "
|
|
84
|
+
"AND s.state='QUEUED' AND s.admission='ADMITTED' "
|
|
85
|
+
"ORDER BY s.created_at,s.submission_id",
|
|
83
86
|
(self.context.project_id,),
|
|
84
87
|
).fetchall()
|
|
85
88
|
return [{"submission_id": str(row[0]), "repository_id": str(row[1]), "state": str(row[2]),
|
|
@@ -117,6 +117,8 @@ def _retry_constraints_for_current_main(
|
|
|
117
117
|
"requested_revision": binding.requested_revision,
|
|
118
118
|
"allowed_baseline_revision": current_main,
|
|
119
119
|
}
|
|
120
|
+
if binding.repository_identity is not None:
|
|
121
|
+
rebound[CONSTRAINT_KEY]["repository_identity"] = binding.repository_identity
|
|
120
122
|
return rebound
|
|
121
123
|
|
|
122
124
|
|
|
@@ -477,7 +479,12 @@ class ParityLifecycleDispatcher:
|
|
|
477
479
|
retry_parent_run_id=candidate.retry_parent_run_id, resume_parent_run_id=None, recorded_at=now,
|
|
478
480
|
)
|
|
479
481
|
host = execute_host_preflight(repository_root, run_id=run_id)
|
|
480
|
-
|
|
482
|
+
revision_binding = parse_repository_revision_binding(candidate.constraints)
|
|
483
|
+
workspace = execute_workspace_preflight(
|
|
484
|
+
repository_root, candidate.prompt, run_id=run_id,
|
|
485
|
+
**({"exact_revision_preparation": True}
|
|
486
|
+
if revision_binding is not None and revision_binding.allowed_baseline_revision is None else {}),
|
|
487
|
+
)
|
|
481
488
|
capability = execute_capability_preflight(repository_root, candidate.prompt, run_id=run_id)
|
|
482
489
|
decision, _ = _record_provider_free_admission(
|
|
483
490
|
repository_root, run_id=run_id, submission_id=canonical_submission_id,
|
|
@@ -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.91"
|
|
16
16
|
MANIFEST_FIELDS = frozenset(
|
|
17
17
|
{
|
|
18
18
|
"platform_version",
|
|
@@ -17,6 +17,7 @@ class RepositoryRevisionBinding:
|
|
|
17
17
|
|
|
18
18
|
requested_revision: str
|
|
19
19
|
allowed_baseline_revision: str | None = None
|
|
20
|
+
repository_identity: str | None = None
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
def parse_repository_revision_binding(
|
|
@@ -26,9 +27,10 @@ def parse_repository_revision_binding(
|
|
|
26
27
|
if constraints is None or CONSTRAINT_KEY not in constraints:
|
|
27
28
|
return None
|
|
28
29
|
raw = constraints[CONSTRAINT_KEY]
|
|
29
|
-
if not isinstance(raw, Mapping) or set(raw)
|
|
30
|
-
"requested_revision", "allowed_baseline_revision",
|
|
31
|
-
|
|
30
|
+
if not isinstance(raw, Mapping) or set(raw) not in (
|
|
31
|
+
{"requested_revision", "allowed_baseline_revision"},
|
|
32
|
+
{"requested_revision", "allowed_baseline_revision", "repository_identity"},
|
|
33
|
+
):
|
|
32
34
|
raise ValueError("repository revision binding shape is invalid")
|
|
33
35
|
requested = raw.get("requested_revision")
|
|
34
36
|
allowed = raw.get("allowed_baseline_revision")
|
|
@@ -36,4 +38,8 @@ def parse_repository_revision_binding(
|
|
|
36
38
|
raise ValueError("requested repository revision is invalid")
|
|
37
39
|
if allowed is not None and (not isinstance(allowed, str) or _SHA.fullmatch(allowed) is None):
|
|
38
40
|
raise ValueError("allowed baseline repository revision is invalid")
|
|
39
|
-
|
|
41
|
+
identity = raw.get("repository_identity")
|
|
42
|
+
if identity is not None and (not isinstance(identity, str)
|
|
43
|
+
or re.fullmatch(r"[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+", identity) is None):
|
|
44
|
+
raise ValueError("repository identity is invalid")
|
|
45
|
+
return RepositoryRevisionBinding(requested, allowed, identity)
|
engineering_platform-2.3.91/src/engineering_platform/schemas/producer-readback-v1.3.schema.json
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://engineering-platform.local/schemas/producer-readback-v1.3.schema.json",
|
|
4
|
+
"title": "Engineering Platform producer readback v1.3 current execution disposition",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["contract_version", "submission", "producer", "correlation", "provenance", "disposition", "run", "result", "evidence"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"contract_version": {"const": "1.3"},
|
|
10
|
+
"submission": {
|
|
11
|
+
"type": "object", "additionalProperties": false,
|
|
12
|
+
"required": ["id", "project_id", "repository_id", "state", "admission", "transport", "created_at", "accepted_request_digest"],
|
|
13
|
+
"properties": {
|
|
14
|
+
"id": {"type": "string"}, "project_id": {"type": "string"},
|
|
15
|
+
"repository_id": {"type": "string"}, "state": {"type": "string"},
|
|
16
|
+
"admission": {"type": "string"}, "transport": {"type": "string"},
|
|
17
|
+
"created_at": {"type": "string"},
|
|
18
|
+
"accepted_request_digest": {"type": "string", "pattern": "^sha256:[0-9a-f]{64}$"}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"producer": {
|
|
22
|
+
"type": "object", "additionalProperties": false,
|
|
23
|
+
"required": ["id", "type", "version"],
|
|
24
|
+
"properties": {
|
|
25
|
+
"id": {"type": "string"}, "type": {"type": "string"},
|
|
26
|
+
"version": {"type": ["string", "null"]}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"correlation": {
|
|
30
|
+
"type": "object", "additionalProperties": false,
|
|
31
|
+
"required": ["correlation_id", "mission_id", "engineering_action_id"],
|
|
32
|
+
"properties": {
|
|
33
|
+
"correlation_id": {"type": ["string", "null"]},
|
|
34
|
+
"mission_id": {"type": ["string", "null"]},
|
|
35
|
+
"engineering_action_id": {"type": ["string", "null"]}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"provenance": {
|
|
39
|
+
"type": "object", "additionalProperties": false,
|
|
40
|
+
"required": ["status", "forge_execution"],
|
|
41
|
+
"properties": {
|
|
42
|
+
"status": {"type": "string"},
|
|
43
|
+
"forge_execution": {"type": ["object", "null"]}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"disposition": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"additionalProperties": false,
|
|
49
|
+
"required": ["state", "terminal", "execution_eligible", "revision", "operation_id", "event_reference", "reason", "actor_reference", "recorded_at", "resolution_submission_id", "retry_parent_run_id"],
|
|
50
|
+
"properties": {
|
|
51
|
+
"state": {"enum": ["QUEUED", "DEFERRED", "QUARANTINED", "DECLINED", "CLAIMED", "RUNNING", "COMPLETE", "BLOCKED", "FAILED", "DISMISSED"]},
|
|
52
|
+
"terminal": {"type": "boolean"},
|
|
53
|
+
"execution_eligible": {"type": "boolean"},
|
|
54
|
+
"revision": {"type": "integer", "minimum": 0},
|
|
55
|
+
"operation_id": {"type": ["string", "null"]},
|
|
56
|
+
"event_reference": {"type": ["string", "null"]},
|
|
57
|
+
"reason": {"type": "string"},
|
|
58
|
+
"actor_reference": {"type": "string"},
|
|
59
|
+
"recorded_at": {"type": ["string", "null"]},
|
|
60
|
+
"resolution_submission_id": {"type": ["string", "null"]},
|
|
61
|
+
"retry_parent_run_id": {"type": ["string", "null"]}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"run": {
|
|
65
|
+
"type": ["object", "null"],
|
|
66
|
+
"properties": {
|
|
67
|
+
"id": {"type": "string", "minLength": 1},
|
|
68
|
+
"state": {"type": "string", "minLength": 1},
|
|
69
|
+
"terminal": {"type": "boolean"},
|
|
70
|
+
"operator_resolution": {"type": "string"},
|
|
71
|
+
"updated_at": {"type": "string"},
|
|
72
|
+
"execution_started_at": {"type": "string", "format": "date-time"},
|
|
73
|
+
"execution_completed_at": {"type": "string", "format": "date-time"},
|
|
74
|
+
"execution_duration_ms": {"type": "integer", "minimum": 1}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"result": {
|
|
78
|
+
"type": "object", "additionalProperties": false,
|
|
79
|
+
"required": ["outcome", "terminal", "delivery_qualified"],
|
|
80
|
+
"properties": {
|
|
81
|
+
"outcome": {"type": "string"},
|
|
82
|
+
"terminal": {"type": "boolean"},
|
|
83
|
+
"delivery_qualified": {"type": "boolean"}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"evidence": {
|
|
87
|
+
"type": "object", "additionalProperties": false,
|
|
88
|
+
"required": ["status", "terminal_artifact", "repository"],
|
|
89
|
+
"properties": {
|
|
90
|
+
"status": {"type": "string"},
|
|
91
|
+
"terminal_artifact": {"type": ["object", "null"]},
|
|
92
|
+
"repository": {
|
|
93
|
+
"type": "object", "additionalProperties": false,
|
|
94
|
+
"required": ["id", "revision"],
|
|
95
|
+
"properties": {
|
|
96
|
+
"id": {"type": ["string", "null"]},
|
|
97
|
+
"revision": {"type": ["string", "null"]}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
{engineering_platform-2.3.90 → engineering_platform-2.3.91}/src/engineering_platform/server.py
RENAMED
|
@@ -77,6 +77,7 @@ from . import system_server_service
|
|
|
77
77
|
from . import storage
|
|
78
78
|
from . import telemetry_export
|
|
79
79
|
from . import managed_codex_runtime
|
|
80
|
+
from . import managed_workspace_readiness
|
|
80
81
|
from . import provider_readiness
|
|
81
82
|
from .platform_components import (
|
|
82
83
|
PLATFORM_COMPONENT_BY_ID,
|
|
@@ -3972,7 +3973,7 @@ def _no_project_console_snapshot(data_root: Path) -> dict[str, object]:
|
|
|
3972
3973
|
).fetchone()[0])
|
|
3973
3974
|
queue_depth = int(connection.execute(
|
|
3974
3975
|
"""SELECT COUNT(*) FROM ep_submissions AS submission
|
|
3975
|
-
WHERE submission.state
|
|
3976
|
+
WHERE submission.state='QUEUED' AND submission.admission='ADMITTED'
|
|
3976
3977
|
AND NOT EXISTS (
|
|
3977
3978
|
SELECT 1 FROM ep_parity_lifecycle_dispatches AS dispatch
|
|
3978
3979
|
WHERE dispatch.submission_id=submission.submission_id
|
|
@@ -6971,7 +6972,7 @@ class _HealthHandler(http.server.BaseHTTPRequestHandler):
|
|
|
6971
6972
|
"producer": {"id": "engineering-platform", "version": CURRENT_PLATFORM_VERSION},
|
|
6972
6973
|
"instance": {"id": identity},
|
|
6973
6974
|
"contracts": {
|
|
6974
|
-
"producer_readback": [submission_service.PRODUCER_READBACK_CONTRACT_VERSION],
|
|
6975
|
+
"producer_readback": [submission_service.PRODUCER_READBACK_CONTRACT_VERSION, "1.3"],
|
|
6975
6976
|
"terminal_evidence": [submission_service.TERMINAL_EVIDENCE_CONTRACT_VERSION],
|
|
6976
6977
|
},
|
|
6977
6978
|
}
|
|
@@ -7044,6 +7045,29 @@ class _HealthHandler(http.server.BaseHTTPRequestHandler):
|
|
|
7044
7045
|
return
|
|
7045
7046
|
self._send(200, declaration, identity)
|
|
7046
7047
|
return
|
|
7048
|
+
workspace_readiness = re.fullmatch(
|
|
7049
|
+
r"/v1/projects/([^/]+)/managed-workspace-readiness", request.path,
|
|
7050
|
+
)
|
|
7051
|
+
if workspace_readiness:
|
|
7052
|
+
project_id = workspace_readiness.group(1)
|
|
7053
|
+
repository_id = self.headers.get("EP-Repository-ID")
|
|
7054
|
+
authorization = self.headers.get("Authorization", "")
|
|
7055
|
+
token = authorization[7:] if authorization.startswith("Bearer ") else None
|
|
7056
|
+
try:
|
|
7057
|
+
with storage.sqlite_connection(self.server.data_root / SERVER_DATABASE_FILENAME) as connection: # type: ignore[attr-defined]
|
|
7058
|
+
if _authenticated_consumer(connection, token, project_id) is None:
|
|
7059
|
+
self._send(401, {"error": "UNAUTHENTICATED"})
|
|
7060
|
+
return
|
|
7061
|
+
if not repository_id:
|
|
7062
|
+
self._send(400, {"error": "REPOSITORY_SCOPE_REQUIRED"})
|
|
7063
|
+
return
|
|
7064
|
+
readiness = managed_workspace_readiness.project_readiness(
|
|
7065
|
+
connection, project_id=project_id, repository_id=repository_id,
|
|
7066
|
+
)
|
|
7067
|
+
self._send(200, readiness, initialize(self.server.data_root).instance_id) # type: ignore[attr-defined]
|
|
7068
|
+
except sqlite3.Error:
|
|
7069
|
+
self._send(503, {"error": "CENTRAL_UNAVAILABLE"})
|
|
7070
|
+
return
|
|
7047
7071
|
if request.path == "/diagnostics/topology":
|
|
7048
7072
|
try:
|
|
7049
7073
|
self._send(200, operations_projection(self.server.data_root), initialize(self.server.data_root).instance_id) # type: ignore[attr-defined]
|
|
@@ -7121,6 +7145,10 @@ class _HealthHandler(http.server.BaseHTTPRequestHandler):
|
|
|
7121
7145
|
return
|
|
7122
7146
|
projection = submission_service.producer_readback(
|
|
7123
7147
|
connection, project_id=project_id, submission_id=submission_id,
|
|
7148
|
+
contract_version=(
|
|
7149
|
+
"1.3" if self.headers.get("EP-Producer-Readback-Contract") == "1.3"
|
|
7150
|
+
else "1.2"
|
|
7151
|
+
),
|
|
7124
7152
|
)
|
|
7125
7153
|
if projection is None:
|
|
7126
7154
|
self._send(404, {"error": "SUBMISSION_NOT_FOUND"})
|
|
@@ -8258,7 +8286,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
8258
8286
|
early = {"diagnostic_code": "EARLY_FAILURE_EVIDENCE_UNAVAILABLE"}
|
|
8259
8287
|
receipt_complete = transport != "FILE_INBOX" or (isinstance(receipt_id, str) and bool(receipt_id) and isinstance(received_at, str) and bool(received_at))
|
|
8260
8288
|
scope_complete = run_id is not None and (dispatch_project, dispatch_repository) == (project_id, repository_id)
|
|
8261
|
-
result = {"submission_id": args.submission_id, "project_id": project_id, "repository_id": repository_id, "submission_state": state, "admission": admission, "run_id": run_id, "dispatch_state": dispatch_state, "operator_resolution": resolution, "transport_provenance": "COMPLETE" if receipt_complete else "INCOMPLETE", "admission_audit_provenance": "PRESENT" if admission_audit else "UNAVAILABLE", "receipt_run_provenance": "PRESENT" if run_id else "UNAVAILABLE", "dispatch_scope_provenance": "COMPLETE" if scope_complete else "UNAVAILABLE", "lane_blocker": {"run_id": blocked[0], "state": blocked[1]} if blocked else None, "early_failure": early, "worker_eligible": state == "QUEUED" and admission == "ADMITTED" and blocked is None}
|
|
8289
|
+
result = {"submission_id": args.submission_id, "project_id": project_id, "repository_id": repository_id, "submission_state": state, "admission": admission, "run_id": run_id, "dispatch_state": dispatch_state, "operator_resolution": resolution, "transport_provenance": "COMPLETE" if receipt_complete else "INCOMPLETE", "admission_audit_provenance": "PRESENT" if admission_audit else "UNAVAILABLE", "receipt_run_provenance": "PRESENT" if run_id else "UNAVAILABLE", "dispatch_scope_provenance": "COMPLETE" if scope_complete else "UNAVAILABLE", "lane_blocker": {"run_id": blocked[0], "state": blocked[1]} if blocked else None, "early_failure": early, "worker_eligible": state == "QUEUED" and admission == "ADMITTED" and run_id is None and blocked is None}
|
|
8262
8290
|
elif args.command == "register-topology":
|
|
8263
8291
|
if args.declaration is None:
|
|
8264
8292
|
raise ServerConfigurationError("--declaration is required for explicit topology registration.")
|