engineering-platform 2.3.68__tar.gz → 2.3.71__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.68 → engineering_platform-2.3.71}/PKG-INFO +1 -1
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/pyproject.toml +2 -1
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/ENGINEERING_PLATFORM_CONFIG.json +1 -1
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/ENGINEERING_PLATFORM_VERSION.json +5 -5
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/assets/dashboard.js +18 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/assets/dashboard_locales.mjs +10 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/contracts/projection.py +7 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/execution_finalization.py +4 -1
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/execution_host.py +150 -52
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/execution_lifecycle.py +3 -2
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/execution_reporting.py +4 -1
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/execution_repository.py +13 -3
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/managed_autonomy.py +21 -4
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/platform_version.py +1 -1
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/qualification_runtime.py +71 -17
- engineering_platform-2.3.71/src/engineering_platform/reconciliation_adoption.py +186 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/server.py +21 -1
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/storage.py +54 -1
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/templates/workspace-config.json +1 -1
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform.egg-info/PKG-INFO +1 -1
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform.egg-info/SOURCES.txt +1 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform.egg-info/entry_points.txt +1 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/README.md +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/setup.cfg +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/__init__.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/__main__.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/agent_state.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/agent_trust.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/assets/dashboard.css +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/assets/dashboard_status_store.mjs +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/assets/dashboard_translation.mjs +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/assets/operations-console/apple-touch-icon-dark.png +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/assets/operations-console/apple-touch-icon-light.png +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/assets/operations-console/icon-dark.png +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/assets/operations-console/icon-light.png +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/assets/operations-console/icon-transparent.png +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/assets/operations-console/manifest.webmanifest +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/capability_preflight.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/capability_review.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/central_data_transfer.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/central_database.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/central_store_migration.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/codex_capacity.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/codex_chat.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/codex_observability.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/component_lock.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/component_logging.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/console_presentation.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/console_route_ownership.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/contracts/__init__.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/contracts/ep_consumer.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/contracts/models.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/dashboard_browser_validation.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/dashboard_state.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/dashboard_supervisor.swift +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/dashboard_translation.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/dependabot_producer.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/development_profile.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/drift_diagnostics.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/emergency_recovery.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/engineering_memory.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/ep_consumer_credentials.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/evidence_projection.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/execution_activity.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/execution_context.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/execution_errors.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/execution_evidence.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/execution_executor.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/execution_host_evidence.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/execution_lease.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/execution_models.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/execution_readiness.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/execution_timeout_policy.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/execution_timing.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/execution_transaction.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/external_producer_binding.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/file_inbox.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/forensic_attribution.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/forensic_attribution_v2.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/forensic_delta.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/golden_scenario.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/historical_dashboard_configuration.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/host_admin.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/host_preflight.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/installation_relocation.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_activation.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_admission.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_backup.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_composition.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_executor.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_migration.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_operation.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_plan.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/installation_update_preparation.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/investigation_ledger.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/legacy_inbox_migration.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/legacy_installation_adoption.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/lifecycle_worker.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/live_status.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/local_api.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/local_api_keychain.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/local_repository_binding.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/managed_codex_runtime.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/operational_installation.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/operational_installation_lock.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/operational_installation_record.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/owner_credential_recovery.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/parity_context.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/parity_lifecycle_dispatcher.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/platform_admin.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/platform_api.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/platform_bootstrap.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/platform_components.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/pr_check_repair.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/pr_evidence_backfill.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/producer.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/product_installation_readback.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/project_agent.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/project_agent_service.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/project_topology.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/prompt_history.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/provider_context.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/provider_context_benchmark.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/provider_context_scope.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/provider_interruption.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/provider_process_identity.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/provider_readiness.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/provider_recovery.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/provider_usage.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/providers.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/qualification.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/recommendation_handoff.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/release_operation.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/report_analysis.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/repository_attachment.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/repository_handoff.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/resources.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/reviewer_evidence.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/revision_binding.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/schemas/producer-readback-v1.2.schema.json +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/schemas/producer-readback-v1.schema.json +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/schemas/repository-attachment.schema.json +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/server_console_services.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/server_relay.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/server_service.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/status_model.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/status_reconciliation.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/submission_cli.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/submission_intake.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/submission_service.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/system_installation_topology.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/system_server_service.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/telemetry.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/validation_identity.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/validation_profile.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/version_preparation_delivery.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/workspace_preflight.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/worktree_provenance.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform/worktree_tooling.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform.egg-info/dependency_links.txt +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/src/engineering_platform.egg-info/top_level.txt +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/tests/test_adaptive_action_sizing_design.py +0 -0
- {engineering_platform-2.3.68 → engineering_platform-2.3.71}/tests/test_role_task_model_policy_design.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "engineering-platform"
|
|
7
|
-
version = "2.3.
|
|
7
|
+
version = "2.3.71"
|
|
8
8
|
description = "Local-first Engineering Platform execution operations runtime"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.14,<3.15"
|
|
@@ -18,6 +18,7 @@ engineering-platform-host = "engineering_platform.__main__:main"
|
|
|
18
18
|
engineering-platform-server = "engineering_platform.server:main"
|
|
19
19
|
engineering-platform = "engineering_platform.submission_cli:main"
|
|
20
20
|
engineering-project-agent = "engineering_platform.project_agent:main"
|
|
21
|
+
engineering-reconciliation-adopt = "engineering_platform.reconciliation_adoption:main"
|
|
21
22
|
|
|
22
23
|
[tool.setuptools]
|
|
23
24
|
package-dir = {"" = "src"}
|
|
@@ -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.71",
|
|
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.71",
|
|
10
10
|
"report_format": 2,
|
|
11
|
-
"runner_version": "2.3.
|
|
11
|
+
"runner_version": "2.3.71",
|
|
12
12
|
"status_model": 1,
|
|
13
|
-
"storage_schema":
|
|
14
|
-
"watcher_version": "2.3.
|
|
13
|
+
"storage_schema": 45,
|
|
14
|
+
"watcher_version": "2.3.71"
|
|
15
15
|
}
|
|
@@ -349,10 +349,22 @@ function phaseAwareRange(estimate) {
|
|
|
349
349
|
if (estimate?.phase_aware !== true || samples < 2 || !Number.isFinite(lower) || !Number.isFinite(upper)) return null;
|
|
350
350
|
return [Math.max(1, Math.round(lower / 60)), Math.max(1, Math.ceil(upper / 60))];
|
|
351
351
|
}
|
|
352
|
+
function exceededEstimateContext(durationEstimate, elapsedContext = "") {
|
|
353
|
+
const values = [elapsedContext, t("estimate.exceeded_context")];
|
|
354
|
+
if (hasHistoricalEstimate(durationEstimate)) values.push(historicalContext(durationEstimate, ""));
|
|
355
|
+
return values.filter(Boolean).join("\n");
|
|
356
|
+
}
|
|
352
357
|
function estimate(x, durationEstimate = {}) {
|
|
353
358
|
const phase = x.current_phase || "";
|
|
354
359
|
const phaseRange = phaseAwareRange(durationEstimate);
|
|
355
360
|
if (["INITIALIZE", "EXECUTE_AGENT", "REPAIR_AGENT", "FINALIZATION_REPAIR_AGENT", "FINALIZE_AGENT", "REPOSITORY_CLEANUP"].includes(phase) && phaseRange) {
|
|
361
|
+
const remainingUpperSeconds = Number(durationEstimate?.remaining_upper_seconds);
|
|
362
|
+
if (Number.isFinite(remainingUpperSeconds) && remainingUpperSeconds <= 0) {
|
|
363
|
+
return {
|
|
364
|
+
summary: t("estimate.exceeded"),
|
|
365
|
+
context: exceededEstimateContext(durationEstimate),
|
|
366
|
+
};
|
|
367
|
+
}
|
|
356
368
|
const [minimum, maximum] = phaseRange;
|
|
357
369
|
return {
|
|
358
370
|
summary: t("estimate.remaining", { minimum, maximum }),
|
|
@@ -375,6 +387,12 @@ function estimate(x, durationEstimate = {}) {
|
|
|
375
387
|
remainingMinimum = Math.max(1, minimum - elapsed),
|
|
376
388
|
remainingMaximum = Math.max(remainingMinimum, maximum - elapsed);
|
|
377
389
|
const elapsedContext = t("estimate.elapsed", { elapsed, minutes: pluralMinutes(elapsed) });
|
|
390
|
+
if (maximum - elapsed <= 0) {
|
|
391
|
+
return {
|
|
392
|
+
summary: t("estimate.exceeded"),
|
|
393
|
+
context: exceededEstimateContext(durationEstimate, elapsedContext),
|
|
394
|
+
};
|
|
395
|
+
}
|
|
378
396
|
return {
|
|
379
397
|
summary: t("estimate.remaining", { minimum: remainingMinimum, maximum: remainingMaximum }),
|
|
380
398
|
context: hasHistoricalEstimate(durationEstimate)
|
|
@@ -251,6 +251,8 @@ export const DASHBOARD_MESSAGES = {
|
|
|
251
251
|
"estimate.cleanup": "Cleanup in progress",
|
|
252
252
|
"estimate.cleanup_context": "Remaining time depends on the local repository.",
|
|
253
253
|
"estimate.elapsed": "{elapsed} {minutes} elapsed.\nBased on execution size, phase and elapsed time. No live Codex progress or token usage.",
|
|
254
|
+
"estimate.exceeded": "Estimate exceeded; remaining time uncertain",
|
|
255
|
+
"estimate.exceeded_context": "The prior range has elapsed; no reliable remaining time is available.",
|
|
254
256
|
"estimate.finalizing": "Finalization in progress",
|
|
255
257
|
"estimate.finalizing_context": "Remaining time is only reliable with live Codex progress.",
|
|
256
258
|
"estimate.historical_context": "Refined with {count} comparable completed executions using the same reported runtime settings.",
|
|
@@ -899,6 +901,8 @@ export const DASHBOARD_MESSAGES = {
|
|
|
899
901
|
"estimate.cleanup": "Opschoning in uitvoering",
|
|
900
902
|
"estimate.cleanup_context": "De resterende tijd hangt af van de lokale repository.",
|
|
901
903
|
"estimate.elapsed": "{elapsed} {minutes} verstreken.\nGebaseerd op opdrachtomvang, fase en verstreken tijd. Geen live Codex-voortgang of tokenverbruik.",
|
|
904
|
+
"estimate.exceeded": "Schatting overschreden; resterende tijd onzeker",
|
|
905
|
+
"estimate.exceeded_context": "De eerdere bandbreedte is verstreken; er is geen betrouwbare resterende tijd beschikbaar.",
|
|
902
906
|
"estimate.finalizing": "Finalisatie in uitvoering",
|
|
903
907
|
"estimate.finalizing_context": "De resterende tijd is pas betrouwbaar met live Codex-voortgang.",
|
|
904
908
|
"estimate.historical_context": "Verfijnd met {count} vergelijkbare voltooide uitvoeringen met dezelfde gerapporteerde runtime-instellingen.",
|
|
@@ -1547,6 +1551,8 @@ export const DASHBOARD_MESSAGES = {
|
|
|
1547
1551
|
"estimate.cleanup": "Bereinigung läuft",
|
|
1548
1552
|
"estimate.cleanup_context": "Die Restzeit hängt vom lokalen Repository ab.",
|
|
1549
1553
|
"estimate.elapsed": "{elapsed} {minutes} vergangen.\nBasierend auf Auftragsumfang, Phase und verstrichener Zeit. Kein Live-Codex-Fortschritt oder Tokenverbrauch.",
|
|
1554
|
+
"estimate.exceeded": "Schätzung überschritten; Restzeit ungewiss",
|
|
1555
|
+
"estimate.exceeded_context": "Die bisherige Zeitspanne ist verstrichen; es ist keine verlässliche Restzeit verfügbar.",
|
|
1550
1556
|
"estimate.finalizing": "Finalisierung läuft",
|
|
1551
1557
|
"estimate.finalizing_context": "Die Restzeit ist nur mit Live-Codex-Fortschritt zuverlässig.",
|
|
1552
1558
|
"estimate.historical_context": "Verfeinert mit {count} vergleichbaren abgeschlossenen Ausführungen mit denselben gemeldeten Laufzeiteinstellungen.",
|
|
@@ -2129,6 +2135,8 @@ export const DASHBOARD_MESSAGES = {
|
|
|
2129
2135
|
"estimate.cleanup": "Nettoyage en cours",
|
|
2130
2136
|
"estimate.cleanup_context": "Le temps restant dépend du dépôt local.",
|
|
2131
2137
|
"estimate.elapsed": "{elapsed} {minutes} écoulées.\nBasé sur la périmètre de l’exécution, la phase et le temps écoulé. Aucune progression Codex en direct ni consommation de jetons.",
|
|
2138
|
+
"estimate.exceeded": "Estimation dépassée ; temps restant incertain",
|
|
2139
|
+
"estimate.exceeded_context": "La plage précédente est écoulée ; aucun temps restant fiable n’est disponible.",
|
|
2132
2140
|
"estimate.finalizing": "Finalisation en cours",
|
|
2133
2141
|
"estimate.finalizing_context": "Le temps restant n’est fiable qu’avec la progression Codex en direct.",
|
|
2134
2142
|
"estimate.historical_context": "Affiné à partir de {count} exécutions terminées comparables utilisant les mêmes paramètres d’exécution signalés.",
|
|
@@ -2711,6 +2719,8 @@ export const DASHBOARD_MESSAGES = {
|
|
|
2711
2719
|
"estimate.cleanup": "Limpieza en curso",
|
|
2712
2720
|
"estimate.cleanup_context": "El tiempo restante depende del repositorio local.",
|
|
2713
2721
|
"estimate.elapsed": "{elapsed} {minutes} transcurridos.\nBasado en el alcance de la ejecución, la fase y el tiempo transcurrido. Sin progreso en directo de Codex ni consumo de tokens.",
|
|
2722
|
+
"estimate.exceeded": "Estimación superada; tiempo restante incierto",
|
|
2723
|
+
"estimate.exceeded_context": "El intervalo anterior ya transcurrió; no hay un tiempo restante fiable.",
|
|
2714
2724
|
"estimate.finalizing": "Finalización en curso",
|
|
2715
2725
|
"estimate.finalizing_context": "El tiempo restante solo es fiable con progreso de Codex en directo.",
|
|
2716
2726
|
"estimate.historical_context": "Afinado con {count} ejecuciones completadas comparables que usan la misma configuración de ejecución informada.",
|
|
@@ -306,6 +306,7 @@ def get_run_context(root: Path, run_id: str) -> dict[str, object]:
|
|
|
306
306
|
snapshot_checks = qualification_source.get("pr_checks", {}) if qualification_source else {}
|
|
307
307
|
implementation = snapshot_checks.get("IMPLEMENTATION", {}) if qualification_source and isinstance(snapshot_checks, dict) else checks.get("IMPLEMENTATION", {})
|
|
308
308
|
finalization = snapshot_checks.get("FINALIZATION", {}) if qualification_source and isinstance(snapshot_checks, dict) else checks.get("FINALIZATION", {})
|
|
309
|
+
reconciliation = snapshot_checks.get("RECONCILIATION", {}) if qualification_source and isinstance(snapshot_checks, dict) else checks.get("RECONCILIATION", {})
|
|
309
310
|
objective = _safe_objective(submission[3] if submission else {})
|
|
310
311
|
validation_only = action_intent == "VALIDATION_ONLY"
|
|
311
312
|
persisted_required_validation = qualification_source.get("validation_profile", UNAVAILABLE) if qualification_source else qualification_validation or UNAVAILABLE
|
|
@@ -337,8 +338,14 @@ def get_run_context(root: Path, run_id: str) -> dict[str, object]:
|
|
|
337
338
|
"finalization_pr": "NOT_REQUIRED" if validation_only else _value(qualification_source.get("finalization_pr") if qualification_source else checkpoint.get("finalization_pull_request") or finalization.get("pr_number")), "finalization_pr_current_state": "NOT_REQUIRED" if validation_only else _value(finalization.get("pr_state")),
|
|
338
339
|
"finalization_merge_state": _value(finalization.get("merge_state")), "finalization_merge_commit": _value(finalization.get("merge_commit") if qualification_source else checkpoint.get("finalization_merge_commit") or finalization.get("merge_commit")),
|
|
339
340
|
"finalization_required_checks_state": _value(finalization.get("required_checks_state")), "finalization_merge_gate": "EXPECTED_OPERATOR_GATE" if phase == "WAIT_FOR_FINALIZATION_MERGE" else UNAVAILABLE,
|
|
341
|
+
"reconciliation_pr": "NOT_REQUIRED" if validation_only else _value(qualification_source.get("reconciliation_pr") if qualification_source else checkpoint.get("reconciliation_pull_request") or reconciliation.get("pr_number")),
|
|
342
|
+
"reconciliation_pr_current_state": "NOT_REQUIRED" if validation_only else _value(reconciliation.get("pr_state")),
|
|
343
|
+
"reconciliation_merge_state": _value(reconciliation.get("merge_state")),
|
|
344
|
+
"reconciliation_required_checks_state": _value(reconciliation.get("required_checks_state")),
|
|
345
|
+
"reconciliation_merge_gate": "EXPECTED_OPERATOR_GATE" if phase == "WAIT_FOR_RECONCILIATION_MERGE" else UNAVAILABLE,
|
|
340
346
|
"implementation_delivery": _value(qualification_source.get("implementation_delivery")),
|
|
341
347
|
"finalization_delivery": _value(qualification_source.get("finalization_delivery")),
|
|
348
|
+
"reconciliation_delivery": _value(qualification_source.get("reconciliation_delivery")),
|
|
342
349
|
"run_delivery_commit": _value(checkpoint.get("implementation_head_sha") or checkpoint.get("last_verified_sha")), "current_repository_head": _value(checkpoint.get("last_verified_sha")), "delivery_commit_head_relationship": UNAVAILABLE},
|
|
343
350
|
"validation": {"engineering_platform_qualification": UNAVAILABLE, "controls": validation_controls,
|
|
344
351
|
"required_validation": persisted_required_validation,
|
|
@@ -33,7 +33,10 @@ class FinalizationCoordinator:
|
|
|
33
33
|
if not callable(operation):
|
|
34
34
|
return save_terminal(cleanup, "BLOCKED", "cleanup_unavailable", "Cleanup client is unavailable; resume with repository cleanup evidence.")
|
|
35
35
|
try:
|
|
36
|
-
|
|
36
|
+
branches = (cleanup.implementation_branch, cleanup.finalization_branch)
|
|
37
|
+
if cleanup.transaction_kind == "RECONCILIATION":
|
|
38
|
+
branches += (cleanup.branch,)
|
|
39
|
+
result = operation(root, branches)
|
|
37
40
|
except RunnerError as error:
|
|
38
41
|
return save_terminal(cleanup, "BLOCKED", "repository_cleanup_required", str(error))
|
|
39
42
|
return save_terminal(
|
|
@@ -82,7 +82,7 @@ from .reviewer_evidence import ReviewerEvidence
|
|
|
82
82
|
from .investigation_ledger import InvocationInvestigationLedger
|
|
83
83
|
from .execution_errors import CodexHandoffTimeout, CodexInvocationError, RunnerError
|
|
84
84
|
from .execution_errors import ProviderReadinessBlocked
|
|
85
|
-
from .execution_timeout_policy import FINALIZATION, REPAIR, agent_timeout
|
|
85
|
+
from .execution_timeout_policy import END_RECONCILIATION, FINALIZATION, REPAIR, agent_timeout
|
|
86
86
|
from .provider_readiness import failures as provider_readiness_failures
|
|
87
87
|
from .execution_repository import GitHubClient as ProviderGitHubClient, RepositoryClient as ProviderRepositoryClient
|
|
88
88
|
from .execution_repository import GhCliClient as ProviderGhCliClient, SubprocessRepositoryClient as ProviderRepositoryClientImpl
|
|
@@ -125,6 +125,7 @@ from .component_logging import component_logger, record_technical_diagnostic, sh
|
|
|
125
125
|
|
|
126
126
|
# Compatibility exports for integrations that already import these names.
|
|
127
127
|
FINALIZATION_PR_HANDOFF_MAX_SECONDS = FINALIZATION.seconds
|
|
128
|
+
RECONCILIATION_PR_HANDOFF_MAX_SECONDS = END_RECONCILIATION.seconds
|
|
128
129
|
REPAIR_AGENT_MAX_SECONDS = REPAIR.seconds
|
|
129
130
|
|
|
130
131
|
# A repair remains scoped to its original PR, but it must also have a finite
|
|
@@ -305,11 +306,12 @@ def assemble_prompt(
|
|
|
305
306
|
)
|
|
306
307
|
authority = (
|
|
307
308
|
"""This is the sole automatic post-Finalization reconciliation. You may only update
|
|
308
|
-
the four canonical rolling records
|
|
309
|
-
|
|
310
|
-
authority, lifecycle, retry, validation, provider, Forge, queue, or delivery semantics.
|
|
311
|
-
|
|
312
|
-
|
|
309
|
+
the four canonical rolling records on the exact checkpointed reconciliation branch, commit and
|
|
310
|
+
push that branch, and create one draft pull request to `main`. Do not change runtime code,
|
|
311
|
+
authority, lifecycle, retry, validation, provider, Forge, queue, or delivery semantics. Do not
|
|
312
|
+
merge the pull request or push directly to `main`. Return `COMPLETE`, `repository_reconciled`,
|
|
313
|
+
the branch, pull-request number, and exact candidate commit SHA only after verifying a clean
|
|
314
|
+
workspace."""
|
|
313
315
|
if state and state.transaction_kind == "RECONCILIATION"
|
|
314
316
|
else
|
|
315
317
|
"""The runner holds explicit owner authorization for this exact bounded transaction. You may create, commit and push one bounded branch and draft pull request, or repair that same pull request. The runner may mark that pull request ready for review, but only the human operator may merge it. Do not merge, release, deploy, tag, publish, upload, change repository settings, bypass protection, or expand the objective."""
|
|
@@ -1090,7 +1092,7 @@ class EngineeringRunner:
|
|
|
1090
1092
|
def _managed_pr_check(self, state: TransactionState, pr: PullRequestEvidence) -> None:
|
|
1091
1093
|
"""Persist current GitHub required-check evidence separately from historical waits."""
|
|
1092
1094
|
role = state.transaction_kind
|
|
1093
|
-
if role not in {"IMPLEMENTATION", "FINALIZATION"}:
|
|
1095
|
+
if role not in {"IMPLEMENTATION", "FINALIZATION", "RECONCILIATION"}:
|
|
1094
1096
|
return
|
|
1095
1097
|
check_state = "PASS" if pr.checks_terminal and pr.checks_passed else "FAIL" if pr.checks_terminal else "WAITING"
|
|
1096
1098
|
try:
|
|
@@ -1418,12 +1420,20 @@ class EngineeringRunner:
|
|
|
1418
1420
|
"""Return whether an agent supplied bounded evidence of a failed local check."""
|
|
1419
1421
|
if not result.validation_evidence:
|
|
1420
1422
|
return False
|
|
1421
|
-
|
|
1422
|
-
item
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1423
|
+
for item in result.validation_evidence:
|
|
1424
|
+
if not isinstance(item, dict):
|
|
1425
|
+
continue
|
|
1426
|
+
summary = item.get("result", "").strip().casefold()
|
|
1427
|
+
# Result summaries are dispositions followed by bounded detail.
|
|
1428
|
+
# A passing disposition takes precedence over words such as
|
|
1429
|
+
# "fail-closed" or "failure mode" in that descriptive detail.
|
|
1430
|
+
if re.match(r"^(?:pass(?:ed)?|success(?:ful|fully)?|succeeded)\b", summary):
|
|
1431
|
+
continue
|
|
1432
|
+
if re.match(r"^(?:fail(?:ed|ure)?|error|timeout|timed\s+out)\b", summary):
|
|
1433
|
+
return True
|
|
1434
|
+
if re.search(r"\b(?:failed|failure|error|timeout|timed\s+out)\b", summary):
|
|
1435
|
+
return True
|
|
1436
|
+
return False
|
|
1427
1437
|
|
|
1428
1438
|
@staticmethod
|
|
1429
1439
|
def _is_external_agent_block(result: AgentResult) -> bool:
|
|
@@ -2723,6 +2733,8 @@ First implementation pull-request publication gate:
|
|
|
2723
2733
|
return self._advance_after_repair_agent_result(state, result)
|
|
2724
2734
|
if lifecycle_phase == "FINALIZE_AGENT":
|
|
2725
2735
|
return self._advance_after_finalization_agent_result(state, result)
|
|
2736
|
+
if lifecycle_phase == "RECONCILE_AGENT":
|
|
2737
|
+
return self._advance_after_reconciliation_agent_result(state, result)
|
|
2726
2738
|
return self._save_terminal(state, "BLOCKED", "recovered_provider_phase_invalid", "Recovered provider result has an unsupported lifecycle phase.")
|
|
2727
2739
|
|
|
2728
2740
|
def run(
|
|
@@ -2890,7 +2902,7 @@ First implementation pull-request publication gate:
|
|
|
2890
2902
|
recovered_resume = (
|
|
2891
2903
|
isinstance(recovery_snapshot, dict)
|
|
2892
2904
|
and recovery_snapshot.get("state") == "RECOVERED"
|
|
2893
|
-
and recovery_snapshot.get("lifecycle_phase") in {"EXECUTE_AGENT", "QUALITY_CONTROL_AGENT", "REPAIR_AGENT", "FINALIZE_AGENT"}
|
|
2905
|
+
and recovery_snapshot.get("lifecycle_phase") in {"EXECUTE_AGENT", "QUALITY_CONTROL_AGENT", "REPAIR_AGENT", "FINALIZE_AGENT", "RECONCILE_AGENT"}
|
|
2894
2906
|
)
|
|
2895
2907
|
try:
|
|
2896
2908
|
persisted_submission = load_submission_for_run(self.root, state.run_id, central_database=self.store.central_database)
|
|
@@ -3273,6 +3285,8 @@ First implementation pull-request publication gate:
|
|
|
3273
3285
|
state = self._record_agent_execution_time(state)
|
|
3274
3286
|
self.console_detail = error.console_detail
|
|
3275
3287
|
return self._terminalize_provider_invocation_error(state, error)
|
|
3288
|
+
if state.transaction_kind == "RECONCILIATION":
|
|
3289
|
+
return self._advance_after_reconciliation_agent_result(state, result)
|
|
3276
3290
|
return self._advance_after_primary_agent_result(state, result, evidence)
|
|
3277
3291
|
|
|
3278
3292
|
def _active_genesis_transaction(self, target: Path, run_id: str) -> str | None:
|
|
@@ -3389,10 +3403,7 @@ First implementation pull-request publication gate:
|
|
|
3389
3403
|
next_action="create_finalization",
|
|
3390
3404
|
)
|
|
3391
3405
|
if state.transaction_kind == "RECONCILIATION" and not state.reconciliation_pull_request:
|
|
3392
|
-
return
|
|
3393
|
-
state, phase="RECONCILE_AGENT", last_verified_sha=evidence.head_sha,
|
|
3394
|
-
next_action="create_reconciliation",
|
|
3395
|
-
)
|
|
3406
|
+
return self._recover_reconciliation_pull_request(state, evidence)
|
|
3396
3407
|
if (
|
|
3397
3408
|
state.transaction_kind == "FINALIZATION"
|
|
3398
3409
|
and state.finalization_merge_commit
|
|
@@ -3470,6 +3481,54 @@ First implementation pull-request publication gate:
|
|
|
3470
3481
|
write_live_status(self.root, recovered, recovered.next_action)
|
|
3471
3482
|
return self._poll(recovered)
|
|
3472
3483
|
|
|
3484
|
+
def _recover_reconciliation_pull_request(
|
|
3485
|
+
self, state: TransactionState, evidence: RepositoryEvidence,
|
|
3486
|
+
) -> TransactionState:
|
|
3487
|
+
"""Recover only the PR bound to the durable reconciliation branch."""
|
|
3488
|
+
if state.reconciliation_pull_request:
|
|
3489
|
+
return replace(
|
|
3490
|
+
state, pull_request=state.reconciliation_pull_request,
|
|
3491
|
+
phase="WAIT_FOR_TERMINAL_EVIDENCE", next_action="poll_required_checks",
|
|
3492
|
+
)
|
|
3493
|
+
if (
|
|
3494
|
+
not state.branch
|
|
3495
|
+
or state.branch == "main"
|
|
3496
|
+
or not state.finalization_merge_commit
|
|
3497
|
+
or evidence.branch not in {"main", state.branch}
|
|
3498
|
+
or not evidence.clean
|
|
3499
|
+
or not self.repository.main_contains(self.root, state.finalization_merge_commit)
|
|
3500
|
+
):
|
|
3501
|
+
return self._save_terminal(
|
|
3502
|
+
state, "BLOCKED", "reconciliation_recovery_evidence_required",
|
|
3503
|
+
"Reconciliation recovery requires its checkpointed branch and a verified Finalization merge.",
|
|
3504
|
+
)
|
|
3505
|
+
candidate = self.github.pull_request_for_head_branch(state.branch)
|
|
3506
|
+
if candidate is None:
|
|
3507
|
+
return self._save_terminal(
|
|
3508
|
+
state, "BLOCKED", "reconciliation_recovery_evidence_required",
|
|
3509
|
+
"No reconciliation pull request matches the checkpointed branch; no replacement was created.",
|
|
3510
|
+
)
|
|
3511
|
+
if (
|
|
3512
|
+
candidate.head_branch != state.branch
|
|
3513
|
+
or candidate.base_branch != "main"
|
|
3514
|
+
or candidate.state not in {"OPEN", "MERGED"}
|
|
3515
|
+
):
|
|
3516
|
+
return self._save_terminal(
|
|
3517
|
+
state, "BLOCKED", "reconciliation_recovery_evidence_invalid",
|
|
3518
|
+
"Reconciliation pull request evidence does not match the checkpointed branch and main base.",
|
|
3519
|
+
)
|
|
3520
|
+
recovered = replace(
|
|
3521
|
+
state, pull_request=candidate.number,
|
|
3522
|
+
reconciliation_pull_request=candidate.number,
|
|
3523
|
+
phase="WAIT_FOR_TERMINAL_EVIDENCE", next_action="poll_required_checks",
|
|
3524
|
+
last_verified_sha=evidence.head_sha,
|
|
3525
|
+
latest_repository_evidence=_repository_summary(evidence),
|
|
3526
|
+
latest_github_evidence=_pull_request_summary(candidate),
|
|
3527
|
+
)
|
|
3528
|
+
self.store.save(recovered)
|
|
3529
|
+
write_live_status(self.root, recovered, recovered.next_action)
|
|
3530
|
+
return self._poll(recovered)
|
|
3531
|
+
|
|
3473
3532
|
def _poll(self, state: TransactionState, result: AgentResult | None = None) -> TransactionState:
|
|
3474
3533
|
if state.pull_request:
|
|
3475
3534
|
state = self._provider_readiness_gate(
|
|
@@ -3489,32 +3548,10 @@ First implementation pull-request publication gate:
|
|
|
3489
3548
|
"Agent result referenced a pull request without a transaction branch; the current main branch cannot be reused as execution evidence.",
|
|
3490
3549
|
)
|
|
3491
3550
|
if not state.pull_request:
|
|
3492
|
-
if state.transaction_kind == "RECONCILIATION" and result
|
|
3493
|
-
return self._save_terminal(
|
|
3494
|
-
state, "BLOCKED", "reconciliation_pull_request_forbidden",
|
|
3495
|
-
"Automatic reconciliation must commit directly to main and must not create a pull request.",
|
|
3496
|
-
)
|
|
3497
|
-
if state.transaction_kind == "RECONCILIATION" and result and result.terminal_state == "COMPLETE":
|
|
3498
|
-
evidence = self.repository.inspect(self.root)
|
|
3499
|
-
if (
|
|
3500
|
-
result.terminal_condition == "repository_reconciled"
|
|
3501
|
-
and result.commit_sha
|
|
3502
|
-
and evidence.clean
|
|
3503
|
-
and evidence.branch == "main"
|
|
3504
|
-
and evidence.head_sha == result.commit_sha
|
|
3505
|
-
and evidence.main_contains_head
|
|
3506
|
-
):
|
|
3507
|
-
reconciled = replace(state, last_verified_sha=result.commit_sha)
|
|
3508
|
-
reconciled = self._append_verified_commit_evidence(
|
|
3509
|
-
reconciled,
|
|
3510
|
-
phase="RECONCILE_AGENT",
|
|
3511
|
-
commit_sha=result.commit_sha,
|
|
3512
|
-
description="end_reconciliation_commit_verified",
|
|
3513
|
-
)
|
|
3514
|
-
return self._cleanup(reconciled)
|
|
3551
|
+
if state.transaction_kind == "RECONCILIATION" and result:
|
|
3515
3552
|
return self._save_terminal(
|
|
3516
|
-
state, "BLOCKED", "
|
|
3517
|
-
"Automatic reconciliation requires
|
|
3553
|
+
state, "BLOCKED", "reconciliation_pr_required",
|
|
3554
|
+
"Automatic reconciliation requires its checkpointed pull request before cleanup.",
|
|
3518
3555
|
)
|
|
3519
3556
|
if result and result.terminal_state == "COMPLETE":
|
|
3520
3557
|
evidence = self.repository.inspect(self.root)
|
|
@@ -3877,12 +3914,7 @@ First implementation pull-request publication gate:
|
|
|
3877
3914
|
return self._poll(finalization, result)
|
|
3878
3915
|
|
|
3879
3916
|
def _start_automatic_reconciliation(self, state: TransactionState) -> TransactionState:
|
|
3880
|
-
"""Apply the bounded rolling-record update
|
|
3881
|
-
|
|
3882
|
-
This is deliberately a direct, post-merge `main` commit: it has no PR,
|
|
3883
|
-
review, approval, or operator merge boundary. Its scope is enforced by
|
|
3884
|
-
the supplied reconciliation prompt and the exact commit evidence below.
|
|
3885
|
-
"""
|
|
3917
|
+
"""Apply the bounded rolling-record update through a protected PR."""
|
|
3886
3918
|
synchronize = getattr(self.repository, "synchronize_main", None)
|
|
3887
3919
|
if callable(synchronize):
|
|
3888
3920
|
synchronize(self.root)
|
|
@@ -3892,22 +3924,37 @@ First implementation pull-request publication gate:
|
|
|
3892
3924
|
state,
|
|
3893
3925
|
"End reconciliation is waiting for a clean, synchronized main checkout.",
|
|
3894
3926
|
)
|
|
3927
|
+
expected_branch = (
|
|
3928
|
+
state.branch
|
|
3929
|
+
if state.transaction_kind == "RECONCILIATION" and state.branch != "main"
|
|
3930
|
+
else None
|
|
3931
|
+
) or f"codex/reconcile-{state.run_id}"
|
|
3895
3932
|
reconciliation = replace(
|
|
3896
3933
|
state, phase="RECONCILE_AGENT", transaction_kind="RECONCILIATION",
|
|
3897
|
-
branch=
|
|
3898
|
-
next_action="
|
|
3934
|
+
branch=expected_branch, pull_request=None, reconciliation_pull_request=None,
|
|
3935
|
+
next_action="create_reconciliation_pull_request", waiting_for_merge_since=None,
|
|
3899
3936
|
latest_repository_evidence=_repository_summary(evidence),
|
|
3900
3937
|
)
|
|
3901
3938
|
self._managed_action(reconciliation, "AUTOMATIC_RECONCILIATION")
|
|
3902
3939
|
self.store.save(reconciliation)
|
|
3903
3940
|
write_live_status(self.root, reconciliation, reconciliation.next_action)
|
|
3941
|
+
reconciliation_span = self._start_phase(state.run_id, "RECONCILIATION")
|
|
3942
|
+
handoff_started = time.monotonic()
|
|
3943
|
+
set_handoff_deadline = getattr(self.agent, "set_handoff_deadline_callback", None)
|
|
3944
|
+
if callable(set_handoff_deadline):
|
|
3945
|
+
set_handoff_deadline(
|
|
3946
|
+
lambda: time.monotonic() - handoff_started
|
|
3947
|
+
>= RECONCILIATION_PR_HANDOFF_MAX_SECONDS
|
|
3948
|
+
)
|
|
3904
3949
|
try:
|
|
3905
3950
|
result = self._invoke_agent_with_timing(
|
|
3906
3951
|
reconciliation,
|
|
3907
3952
|
assemble_prompt(
|
|
3908
3953
|
Path(reconciliation.prompt_path), reconciliation,
|
|
3909
3954
|
managed_target=self.root if reconciliation.execution_mode == "MANAGED" else None,
|
|
3910
|
-
)
|
|
3955
|
+
)
|
|
3956
|
+
+ "\n\nReconcile only the four canonical rolling current-state records after the verified Finalization merge. "
|
|
3957
|
+
f"Preserve immutable Prompt History and create one draft pull request on exactly `{expected_branch}`.",
|
|
3911
3958
|
)
|
|
3912
3959
|
reconciliation = self._record_agent_execution_time(reconciliation)
|
|
3913
3960
|
reconciliation = self._record_validation_evidence(reconciliation, result)
|
|
@@ -3918,12 +3965,63 @@ First implementation pull-request publication gate:
|
|
|
3918
3965
|
description="end_reconciliation_commit_verified",
|
|
3919
3966
|
)
|
|
3920
3967
|
self._persist_agent_usage(reconciliation.run_id)
|
|
3968
|
+
except CodexHandoffTimeout:
|
|
3969
|
+
complete_phase(self.root, reconciliation_span, outcome="FAILED")
|
|
3970
|
+
reconciliation = self._record_agent_execution_time(reconciliation)
|
|
3971
|
+
return self._recover_reconciliation_pull_request(
|
|
3972
|
+
reconciliation, self.repository.inspect(self.root)
|
|
3973
|
+
)
|
|
3921
3974
|
except ProviderReadinessBlocked as blocked:
|
|
3975
|
+
complete_phase(self.root, reconciliation_span, outcome="BLOCKED")
|
|
3922
3976
|
return blocked.state
|
|
3923
3977
|
except CodexInvocationError as error:
|
|
3978
|
+
complete_phase(
|
|
3979
|
+
self.root, reconciliation_span,
|
|
3980
|
+
outcome="INTERRUPTED" if error.provider_turn_interrupted else "FAILED",
|
|
3981
|
+
)
|
|
3924
3982
|
reconciliation = self._record_agent_execution_time(reconciliation)
|
|
3925
3983
|
self.console_detail = error.console_detail
|
|
3926
3984
|
return self._terminalize_provider_invocation_error(reconciliation, error)
|
|
3985
|
+
finally:
|
|
3986
|
+
if callable(set_handoff_deadline):
|
|
3987
|
+
set_handoff_deadline(None)
|
|
3988
|
+
complete_phase(self.root, reconciliation_span)
|
|
3989
|
+
return self._advance_after_reconciliation_agent_result(reconciliation, result)
|
|
3990
|
+
|
|
3991
|
+
def _advance_after_reconciliation_agent_result(
|
|
3992
|
+
self, reconciliation: TransactionState, result: AgentResult,
|
|
3993
|
+
) -> TransactionState:
|
|
3994
|
+
"""Bind a live or recovered reconciliation result to its one PR."""
|
|
3995
|
+
if result.terminal_state in {"BLOCKED", "FAILED"} or not result.pull_request:
|
|
3996
|
+
return self._save_terminal(
|
|
3997
|
+
reconciliation,
|
|
3998
|
+
result.terminal_state
|
|
3999
|
+
if result.terminal_state in {"BLOCKED", "FAILED"}
|
|
4000
|
+
else "BLOCKED",
|
|
4001
|
+
"reconciliation_pr_required",
|
|
4002
|
+
result.diagnostic or "Reconciliation pull request was not created.",
|
|
4003
|
+
)
|
|
4004
|
+
expected_branch = reconciliation.branch
|
|
4005
|
+
if not expected_branch or expected_branch == "main" or result.branch != expected_branch:
|
|
4006
|
+
return self._save_terminal(
|
|
4007
|
+
reconciliation, "BLOCKED", "reconciliation_branch_mismatch",
|
|
4008
|
+
"Reconciliation returned a pull request outside the durable reconciliation branch.",
|
|
4009
|
+
)
|
|
4010
|
+
reconciliation = replace(
|
|
4011
|
+
reconciliation,
|
|
4012
|
+
phase="WAIT_FOR_TERMINAL_EVIDENCE",
|
|
4013
|
+
pull_request=result.pull_request,
|
|
4014
|
+
reconciliation_pull_request=result.pull_request,
|
|
4015
|
+
terminal_condition="repository_reconciled",
|
|
4016
|
+
next_action="poll_required_checks",
|
|
4017
|
+
)
|
|
4018
|
+
self.store.save(reconciliation)
|
|
4019
|
+
write_live_status(self.root, reconciliation, reconciliation.next_action)
|
|
4020
|
+
evidence = self.github.pull_request(result.pull_request)
|
|
4021
|
+
if evidence.state == "MERGED":
|
|
4022
|
+
return self._poll(reconciliation, result)
|
|
4023
|
+
self.github.normalize_markdown_body(result.pull_request)
|
|
4024
|
+
self.github.ready(result.pull_request)
|
|
3927
4025
|
return self._poll(reconciliation, result)
|
|
3928
4026
|
|
|
3929
4027
|
def _save_terminal(
|
|
@@ -19,12 +19,12 @@ TERMINAL = frozenset({"COMPLETE", "BLOCKED", "FAILED"})
|
|
|
19
19
|
_MANAGED_PATH = (
|
|
20
20
|
"START", "INITIALIZE", "CAPABILITY_REVIEW", "EXECUTE_AGENT", "LOCAL_REPOSITORY_VALIDATION", "QUALITY_CONTROL_AGENT", "REPAIR_AGENT",
|
|
21
21
|
"WAIT_FOR_OPERATOR_MERGE", "FINALIZE_AGENT", "WAIT_FOR_FINALIZATION_MERGE",
|
|
22
|
-
"RECONCILE_AGENT", "REPOSITORY_CLEANUP", "TERMINAL",
|
|
22
|
+
"RECONCILE_AGENT", "WAIT_FOR_RECONCILIATION_MERGE", "REPOSITORY_CLEANUP", "TERMINAL",
|
|
23
23
|
)
|
|
24
24
|
_FINALIZATION_REPAIR_PATH = (
|
|
25
25
|
"START", "INITIALIZE", "CAPABILITY_REVIEW", "EXECUTE_AGENT", "QUALITY_CONTROL_AGENT", "REPAIR_AGENT",
|
|
26
26
|
"WAIT_FOR_OPERATOR_MERGE", "FINALIZE_AGENT", "FINALIZATION_REPAIR_AGENT", "WAIT_FOR_FINALIZATION_MERGE",
|
|
27
|
-
"RECONCILE_AGENT", "REPOSITORY_CLEANUP", "TERMINAL",
|
|
27
|
+
"RECONCILE_AGENT", "WAIT_FOR_RECONCILIATION_MERGE", "REPOSITORY_CLEANUP", "TERMINAL",
|
|
28
28
|
)
|
|
29
29
|
# Genesis has no pull-request merge boundary. This is presentation of the
|
|
30
30
|
# existing mode contract, not a new execution sequence.
|
|
@@ -52,6 +52,7 @@ _STEP_PHASES = {
|
|
|
52
52
|
"WAIT_FOR_OPERATOR_MERGE": frozenset({"PR_OR_MERGE", "EXTERNAL_CI_WAIT"}),
|
|
53
53
|
"FINALIZE_AGENT": frozenset({"REPOSITORY_FINALIZATION", "FINALIZATION"}),
|
|
54
54
|
"RECONCILE_AGENT": frozenset({"RECONCILIATION", "REPORT_GENERATION", "EVIDENCE_PERSISTENCE"}),
|
|
55
|
+
"WAIT_FOR_RECONCILIATION_MERGE": frozenset({"PR_OR_MERGE", "EXTERNAL_CI_WAIT"}),
|
|
55
56
|
"REPOSITORY_CLEANUP": frozenset({"REPOSITORY_CLEANUP"}),
|
|
56
57
|
"TERMINAL": frozenset({"TOTAL_EXECUTION"}),
|
|
57
58
|
}
|
|
@@ -397,6 +397,7 @@ def _commit_strategy(state: TransactionState, bundle: TerminalEvidenceBundle) ->
|
|
|
397
397
|
f"- Implementation merge: `{state.implementation_merge_commit or 'not recorded'}`",
|
|
398
398
|
f"- Finalization PR: `{state.finalization_pull_request or 'not recorded'}`",
|
|
399
399
|
f"- Finalization merge: `{state.finalization_merge_commit or 'not recorded'}`",
|
|
400
|
+
f"- Reconciliation PR: `{state.reconciliation_pull_request or 'not recorded'}`",
|
|
400
401
|
)
|
|
401
402
|
|
|
402
403
|
|
|
@@ -1080,6 +1081,7 @@ def _managed_autonomy_projection(root: Path, state: TransactionState, bundle: Te
|
|
|
1080
1081
|
snapshot = managed_autonomy_snapshot(
|
|
1081
1082
|
root, run_id=state.run_id, execution_outcome=state.phase,
|
|
1082
1083
|
implementation_pr=state.implementation_pull_request, finalization_pr=state.finalization_pull_request,
|
|
1084
|
+
reconciliation_pr=state.reconciliation_pull_request,
|
|
1083
1085
|
repository_state="MERGED_RECONCILED" if state.phase == "COMPLETE" and (state.finalization_merge_commit or state.action_intent == "VALIDATION_ONLY") else "UNAVAILABLE",
|
|
1084
1086
|
workspace_state="WORKSPACE_READY" if state.phase == "COMPLETE" and bundle.worktree_state == "clean" else "UNAVAILABLE",
|
|
1085
1087
|
main_origin_sync="YES" if bundle.target_branch == "main" else "UNAVAILABLE",
|
|
@@ -1503,6 +1505,7 @@ def generate_terminal_report(
|
|
|
1503
1505
|
"## Pull Requests",
|
|
1504
1506
|
f"- Implementation: branch `{state.implementation_branch}`, PR `{state.implementation_pull_request}`, merge `{state.implementation_merge_commit}`",
|
|
1505
1507
|
f"- Finalization: branch `{state.finalization_branch}`, PR `{state.finalization_pull_request}`, merge `{state.finalization_merge_commit}`",
|
|
1508
|
+
f"- Reconciliation: branch `{state.branch if state.transaction_kind == 'RECONCILIATION' else None}`, PR `{state.reconciliation_pull_request}`",
|
|
1506
1509
|
"",
|
|
1507
1510
|
*_retry_relationship(state),
|
|
1508
1511
|
"## Initial Repository Assessment",
|
|
@@ -1600,7 +1603,7 @@ def generate_terminal_report(
|
|
|
1600
1603
|
"## Metrics",
|
|
1601
1604
|
f"- Codex CLI execution time: {state.agent_execution_seconds if state.agent_execution_seconds is not None else 'not measured'} seconds",
|
|
1602
1605
|
f"- Repair iterations: {state.repair_iterations}",
|
|
1603
|
-
f"- PRs created: {sum(value is not None for value in (state.implementation_pull_request, state.finalization_pull_request))}",
|
|
1606
|
+
f"- PRs created: {sum(value is not None for value in (state.implementation_pull_request, state.finalization_pull_request, state.reconciliation_pull_request))}",
|
|
1604
1607
|
f"- Merges performed: {sum(value is not None for value in (state.implementation_merge_commit, state.finalization_merge_commit))}",
|
|
1605
1608
|
"",
|
|
1606
1609
|
)
|
|
@@ -44,7 +44,9 @@ class GitHubClient(Protocol):
|
|
|
44
44
|
def ready(self, number: int) -> None: ...
|
|
45
45
|
def normalize_markdown_body(self, number: int) -> bool: ...
|
|
46
46
|
def merge(self, number: int) -> None: ...
|
|
47
|
-
def create_or_recover_pull_request(
|
|
47
|
+
def create_or_recover_pull_request(
|
|
48
|
+
self, branch: str, base: str, title: str, body: str, *, draft: bool = False,
|
|
49
|
+
) -> PullRequestEvidence: ...
|
|
48
50
|
def qualification_for_exact_head(self, number: int, head_sha: str) -> dict[str, object]: ...
|
|
49
51
|
def version_preparation_writer(self) -> dict[str, object]: ...
|
|
50
52
|
|
|
@@ -250,7 +252,9 @@ class GhCliClient:
|
|
|
250
252
|
raise RunnerError("Finalization recovery found more than one pull request for its checkpointed branch.")
|
|
251
253
|
return self.pull_request(numbers[0])
|
|
252
254
|
|
|
253
|
-
def create_or_recover_pull_request(
|
|
255
|
+
def create_or_recover_pull_request(
|
|
256
|
+
self, branch: str, base: str, title: str, body: str, *, draft: bool = False,
|
|
257
|
+
) -> PullRequestEvidence:
|
|
254
258
|
"""Create one bounded PR or recover the sole existing branch identity."""
|
|
255
259
|
existing = self.pull_request_for_head_branch(branch)
|
|
256
260
|
if existing is not None:
|
|
@@ -258,7 +262,13 @@ class GhCliClient:
|
|
|
258
262
|
raise RunnerError("Version preparation branch already has a pull request for another base.")
|
|
259
263
|
return existing
|
|
260
264
|
try:
|
|
261
|
-
|
|
265
|
+
arguments = [
|
|
266
|
+
"pr", "create", "--head", branch, "--base", base,
|
|
267
|
+
"--title", title, "--body", body,
|
|
268
|
+
]
|
|
269
|
+
if draft:
|
|
270
|
+
arguments.append("--draft")
|
|
271
|
+
raw = self._github(*arguments)
|
|
262
272
|
except RuntimeError as error:
|
|
263
273
|
# A successful create may lose its acknowledgement. Only recover
|
|
264
274
|
# the deterministic branch identity; never create a second PR.
|