@ohos-ports/nx 23.3.0-beta.2 → 24.0.2-beta.0
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.
- package/README.md +3 -6
- package/dist/bin/nx.js +2 -2
- package/dist/bin/post-install.js +5 -3
- package/dist/nx-cloud-internals.d.ts +1 -0
- package/dist/nx-cloud-internals.js +7 -1
- package/dist/src/adapter/ngcli-adapter.js +2 -2
- package/dist/src/ai/clone-ai-config-repo.js +49 -12
- package/dist/src/command-line/add/add.d.ts +2 -0
- package/dist/src/command-line/add/add.js +23 -5
- package/dist/src/command-line/affected/affected.js +2 -2
- package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +0 -12
- package/dist/src/command-line/graph/graph.js +38 -10
- package/dist/src/command-line/init/command-object.js +0 -12
- package/dist/src/command-line/init/implementation/add-nx-to-nest.js +21 -62
- package/dist/src/command-line/init/init-v2.js +0 -2
- package/dist/src/command-line/migrate/agentic/close-agent-session.d.ts +1 -1
- package/dist/src/command-line/migrate/agentic/close-agent-session.js +4 -3
- package/dist/src/command-line/migrate/agentic/definitions.d.ts +1 -3
- package/dist/src/command-line/migrate/agentic/definitions.js +5 -97
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +7 -45
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +9 -51
- package/dist/src/command-line/migrate/agentic/handoff.d.ts +1 -43
- package/dist/src/command-line/migrate/agentic/handoff.js +3 -119
- package/dist/src/command-line/migrate/agentic/instruction-files.d.ts +0 -21
- package/dist/src/command-line/migrate/agentic/instruction-files.js +6 -27
- package/dist/src/command-line/migrate/agentic/master/run-master-session.d.ts +19 -1
- package/dist/src/command-line/migrate/agentic/master/run-master-session.js +88 -49
- package/dist/src/command-line/migrate/agentic/master/spawn-master.d.ts +1 -0
- package/dist/src/command-line/migrate/agentic/master/spawn-master.js +86 -24
- package/dist/src/command-line/migrate/agentic/print-dropped-agent-context.d.ts +2 -2
- package/dist/src/command-line/migrate/agentic/print-dropped-agent-context.js +2 -2
- package/dist/src/command-line/migrate/agentic/prompts/fragments.d.ts +1 -12
- package/dist/src/command-line/migrate/agentic/prompts/fragments.js +8 -17
- package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.d.ts +0 -15
- package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.js +3 -100
- package/dist/src/command-line/migrate/agentic/select.d.ts +12 -6
- package/dist/src/command-line/migrate/agentic/select.js +38 -8
- package/dist/src/command-line/migrate/agentic/types.d.ts +9 -68
- package/dist/src/command-line/migrate/agentic/types.js +6 -8
- package/dist/src/command-line/migrate/command-object.js +6 -10
- package/dist/src/command-line/migrate/execute-migration.d.ts +6 -6
- package/dist/src/command-line/migrate/execute-migration.js +25 -15
- package/dist/src/command-line/migrate/migrate-analytics.d.ts +18 -1
- package/dist/src/command-line/migrate/migrate-analytics.js +38 -0
- package/dist/src/command-line/migrate/migrate-commits.d.ts +29 -17
- package/dist/src/command-line/migrate/migrate-commits.js +86 -32
- package/dist/src/command-line/migrate/migrate-config.d.ts +6 -6
- package/dist/src/command-line/migrate/migrate-config.js +12 -6
- package/dist/src/command-line/migrate/migrate-output.d.ts +1 -6
- package/dist/src/command-line/migrate/migrate-output.js +1 -10
- package/dist/src/command-line/migrate/migrate.d.ts +6 -30
- package/dist/src/command-line/migrate/migrate.js +257 -255
- package/dist/src/command-line/migrate/resolve-package-version.d.ts +2 -4
- package/dist/src/command-line/migrate/resolve-package-version.js +2 -35
- package/dist/src/command-line/migrate/run/agent-output.d.ts +2 -0
- package/dist/src/command-line/migrate/run/agent-output.js +9 -0
- package/dist/src/command-line/migrate/run/broker.d.ts +14 -5
- package/dist/src/command-line/migrate/run/broker.js +69 -21
- package/dist/src/command-line/migrate/run/clean-retry.js +4 -2
- package/dist/src/command-line/migrate/run/existing-run-report.d.ts +1 -1
- package/dist/src/command-line/migrate/run/existing-run-report.js +7 -5
- package/dist/src/command-line/migrate/run/give-up.d.ts +4 -3
- package/dist/src/command-line/migrate/run/give-up.js +8 -5
- package/dist/src/command-line/migrate/run/index.d.ts +7 -5
- package/dist/src/command-line/migrate/run/index.js +16 -1
- package/dist/src/command-line/migrate/run/issues.js +7 -2
- package/dist/src/command-line/migrate/run/orchestrator.d.ts +61 -4
- package/dist/src/command-line/migrate/run/orchestrator.js +397 -133
- package/dist/src/command-line/migrate/run/run-state.d.ts +17 -1
- package/dist/src/command-line/migrate/run/run-state.js +41 -2
- package/dist/src/command-line/migrate/run/runbook.d.ts +1 -0
- package/dist/src/command-line/migrate/run/runbook.js +115 -18
- package/dist/src/command-line/migrate/run/state-lock.d.ts +29 -4
- package/dist/src/command-line/migrate/run/state-lock.js +102 -10
- package/dist/src/command-line/migrate/run/state-machine.js +4 -1
- package/dist/src/command-line/migrate/run/util.d.ts +7 -2
- package/dist/src/command-line/migrate/run/util.js +30 -3
- package/dist/src/command-line/migrate/run/worker.d.ts +1 -1
- package/dist/src/command-line/migrate/run/worker.js +252 -250
- package/dist/src/command-line/migrate/sort-migrations.d.ts +1 -7
- package/dist/src/command-line/migrate/sort-migrations.js +1 -12
- package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +17 -12
- package/dist/src/command-line/release/changelog.js +9 -6
- package/dist/src/command-line/release/config/config.d.ts +1 -0
- package/dist/src/command-line/release/config/config.js +48 -9
- package/dist/src/command-line/release/release.js +2 -2
- package/dist/src/command-line/release/utils/git.js +8 -7
- package/dist/src/command-line/release/utils/interpolate-release-config.d.ts +6 -0
- package/dist/src/command-line/release/utils/interpolate-release-config.js +39 -0
- package/dist/src/command-line/release/utils/release-graph.d.ts +5 -0
- package/dist/src/command-line/release/utils/release-graph.js +1 -0
- package/dist/src/command-line/release/utils/remote-release-clients/github.js +5 -6
- package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +5 -6
- package/dist/src/command-line/release/utils/resolve-changelog-renderer.js +2 -2
- package/dist/src/command-line/release/utils/shared.js +10 -10
- package/dist/src/command-line/release/utils/version-plan-utils.d.ts +3 -12
- package/dist/src/command-line/release/utils/version-plan-utils.js +33 -55
- package/dist/src/command-line/release/version/version-actions.js +2 -1
- package/dist/src/command-line/release/version.js +5 -2
- package/dist/src/command-line/reset/reset.js +58 -62
- package/dist/src/command-line/run/executor-utils.js +1 -0
- package/dist/src/command-line/run/run-one.d.ts +20 -1
- package/dist/src/command-line/run/run-one.js +153 -5
- package/dist/src/command-line/run/run.js +2 -1
- package/dist/src/command-line/show/projects.js +5 -3
- package/dist/src/command-line/show/show-target/info.d.ts +10 -1
- package/dist/src/command-line/show/show-target/info.js +100 -15
- package/dist/src/command-line/show/show-target/inputs.js +10 -2
- package/dist/src/command-line/show/show-target/utils.js +1 -0
- package/dist/src/command-line/watch/command-object.js +3 -22
- package/dist/src/command-line/watch/watch.d.ts +0 -7
- package/dist/src/command-line/watch/watch.js +1 -1
- package/dist/src/command-line/yargs-utils/shared-options.js +1 -1
- package/dist/src/config/misc-interfaces.d.ts +18 -0
- package/dist/src/config/nx-json.d.ts +22 -11
- package/dist/src/config/task-graph.d.ts +1 -1
- package/dist/src/config/workspace-json-project-json.d.ts +88 -0
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/core/graph/runtime.js +1 -1
- package/dist/src/core/graph/styles.css +1 -1
- package/dist/src/core/graph/styles.js +1 -1
- package/dist/src/daemon/client/client.d.ts +15 -0
- package/dist/src/daemon/client/client.js +181 -71
- package/dist/src/daemon/client/daemon-socket-messenger.d.ts +1 -1
- package/dist/src/daemon/client/daemon-socket-messenger.js +2 -2
- package/dist/src/daemon/server/client-socket-context.js +1 -1
- package/dist/src/daemon/server/handle-process-in-background.js +21 -9
- package/dist/src/daemon/server/handle-select-affected-tasks.d.ts +2 -1
- package/dist/src/daemon/server/handle-select-affected-tasks.js +21 -13
- package/dist/src/daemon/server/native-js-loader.d.ts +1 -0
- package/dist/src/daemon/server/native-js-loader.js +21 -0
- package/dist/src/daemon/server/project-graph-incremental-recomputation.js +4 -8
- package/dist/src/daemon/server/server.d.ts +2 -1
- package/dist/src/daemon/server/server.js +17 -4
- package/dist/src/daemon/server/shutdown-utils.js +0 -3
- package/dist/src/daemon/server/start.d.ts +1 -0
- package/dist/src/daemon/server/start.js +2 -0
- package/dist/src/daemon/socket-utils.d.ts +11 -9
- package/dist/src/daemon/socket-utils.js +31 -61
- package/dist/src/daemon/tmp-dir.d.ts +19 -14
- package/dist/src/daemon/tmp-dir.js +142 -354
- package/dist/src/devkit-exports.d.ts +9 -1
- package/dist/src/devkit-exports.js +12 -1
- package/dist/src/devkit-internals.d.ts +6 -5
- package/dist/src/devkit-internals.js +13 -4
- package/dist/src/executors/run-commands/run-commands.impl.d.ts +4 -2
- package/dist/src/executors/run-commands/run-commands.impl.js +53 -27
- package/dist/src/executors/run-commands/running-tasks.d.ts +13 -4
- package/dist/src/executors/run-commands/running-tasks.js +148 -95
- package/dist/src/executors/run-commands/schema.json +1 -1
- package/dist/src/hasher/check-task-files.js +5 -8
- package/dist/src/hasher/file-hasher.js +9 -7
- package/dist/src/hasher/hash-task.js +4 -0
- package/dist/src/hasher/native-task-hasher-impl.js +3 -1
- package/dist/src/hasher/task-hasher.d.ts +2 -0
- package/dist/src/hasher/task-hasher.js +4 -1
- package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.d.ts +1 -2
- package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.js +6 -11
- package/dist/src/migrations/update-23-2-0/set-cache-on-executor-target-defaults.md +2 -2
- package/dist/src/migrations/update-24-0-0/enable-legacy-affected.d.ts +4 -0
- package/dist/src/migrations/update-24-0-0/enable-legacy-affected.js +15 -0
- package/dist/src/migrations/update-24-0-0/enable-legacy-affected.md +30 -0
- package/dist/src/migrations/update-24-0-0/find-custom-hashers.d.ts +3 -0
- package/dist/src/migrations/update-24-0-0/find-custom-hashers.js +100 -0
- package/dist/src/migrations/update-24-0-0/find-custom-hashers.md +63 -0
- package/dist/src/migrations/update-24-0-0/move-custom-hashers-to-inputs.md +146 -0
- package/dist/src/migrations/update-24-0-0/rename-ultracache-ignore-options.d.ts +3 -0
- package/dist/src/migrations/update-24-0-0/rename-ultracache-ignore-options.js +130 -0
- package/dist/src/migrations/update-24-0-0/rename-ultracache-ignore-options.md +62 -0
- package/dist/src/native/assert-supported-platform.js +1 -1
- package/dist/src/native/index.d.ts +238 -18
- package/dist/src/native/index.js +6 -1
- package/dist/src/native/native-bindings.js +15 -6
- package/dist/src/native/nx.openharmony-arm64.node +0 -0
- package/dist/src/native/nx.wasi-browser.js +1 -0
- package/dist/src/native/nx.wasi.cjs +1 -0
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/nx-cloud/update-manager.d.ts +14 -0
- package/dist/src/nx-cloud/update-manager.js +237 -50
- package/dist/src/plugins/js/lock-file/pnpm-parser.js +3 -4
- package/dist/src/plugins/js/lock-file/pruned-output.d.ts +2 -2
- package/dist/src/plugins/js/lock-file/pruned-output.js +4 -4
- package/dist/src/plugins/js/utils/register.js +9 -4
- package/dist/src/project-graph/affected/affected-tasks.d.ts +9 -6
- package/dist/src/project-graph/affected/affected-tasks.js +31 -7
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +7 -0
- package/dist/src/project-graph/plugins/loaded-nx-plugin.js +49 -6
- package/dist/src/project-graph/utils/project-configuration/target-merging.js +1 -1
- package/dist/src/project-graph/utils/project-configuration/target-normalization.js +12 -133
- package/dist/src/project-graph/utils/project-configuration-utils.js +2 -2
- package/dist/src/project-graph/utils/retrieve-workspace-files.js +27 -6
- package/dist/src/tasks-runner/create-task-graph.d.ts +8 -7
- package/dist/src/tasks-runner/create-task-graph.js +50 -27
- package/dist/src/tasks-runner/default-tasks-runner.js +1 -1
- package/dist/src/tasks-runner/init-tasks-runner.js +116 -59
- package/dist/src/tasks-runner/life-cycle.d.ts +4 -1
- package/dist/src/tasks-runner/life-cycle.js +7 -0
- package/dist/src/tasks-runner/life-cycles/performance-analysis.d.ts +5 -2
- package/dist/src/tasks-runner/life-cycles/performance-analysis.js +13 -7
- package/dist/src/tasks-runner/life-cycles/performance-life-cycle.d.ts +4 -3
- package/dist/src/tasks-runner/life-cycles/performance-life-cycle.js +9 -2
- package/dist/src/tasks-runner/life-cycles/static-run-many-terminal-output-life-cycle.js +5 -2
- package/dist/src/tasks-runner/life-cycles/static-run-one-terminal-output-life-cycle.js +4 -1
- package/dist/src/tasks-runner/life-cycles/summary-terminal-output-life-cycle.js +5 -1
- package/dist/src/tasks-runner/pseudo-ipc.d.ts +1 -0
- package/dist/src/tasks-runner/pseudo-ipc.js +4 -0
- package/dist/src/tasks-runner/pseudo-terminal.d.ts +1 -1
- package/dist/src/tasks-runner/pseudo-terminal.js +2 -2
- package/dist/src/tasks-runner/readiness/executor-ready-signal.d.ts +7 -0
- package/dist/src/tasks-runner/readiness/executor-ready-signal.js +43 -0
- package/dist/src/tasks-runner/readiness/probes.d.ts +14 -0
- package/dist/src/tasks-runner/readiness/probes.js +101 -0
- package/dist/src/tasks-runner/readiness/ready-signal-server.d.ts +18 -0
- package/dist/src/tasks-runner/readiness/ready-signal-server.js +72 -0
- package/dist/src/tasks-runner/readiness/ready-signal.d.ts +33 -0
- package/dist/src/tasks-runner/readiness/ready-signal.js +92 -0
- package/dist/src/tasks-runner/readiness/ready-when.d.ts +84 -0
- package/dist/src/tasks-runner/readiness/ready-when.js +371 -0
- package/dist/src/tasks-runner/readiness/startup-host.d.ts +1 -0
- package/dist/src/tasks-runner/readiness/startup-host.js +32 -0
- package/dist/src/tasks-runner/run-command.d.ts +6 -2
- package/dist/src/tasks-runner/run-command.js +21 -31
- package/dist/src/tasks-runner/run-outcome.d.ts +16 -0
- package/dist/src/tasks-runner/run-outcome.js +39 -0
- package/dist/src/tasks-runner/running-tasks/batch-process.js +6 -0
- package/dist/src/tasks-runner/running-tasks/node-child-process.js +5 -4
- package/dist/src/tasks-runner/running-tasks/running-task.d.ts +1 -1
- package/dist/src/tasks-runner/task-env.d.ts +10 -0
- package/dist/src/tasks-runner/task-env.js +37 -5
- package/dist/src/tasks-runner/task-graph-utils.d.ts +9 -6
- package/dist/src/tasks-runner/task-graph-utils.js +17 -12
- package/dist/src/tasks-runner/task-orchestrator.d.ts +62 -18
- package/dist/src/tasks-runner/task-orchestrator.js +750 -150
- package/dist/src/tasks-runner/tasks-runner.d.ts +1 -1
- package/dist/src/tasks-runner/tasks-schedule.d.ts +18 -1
- package/dist/src/tasks-runner/tasks-schedule.js +63 -6
- package/dist/src/tasks-runner/tokens.d.ts +54 -0
- package/dist/src/tasks-runner/tokens.js +161 -0
- package/dist/src/tasks-runner/utils.d.ts +35 -6
- package/dist/src/tasks-runner/utils.js +149 -68
- package/dist/src/ultracache/ci-status.d.ts +24 -0
- package/dist/src/ultracache/ci-status.js +87 -0
- package/dist/src/ultracache/config.d.ts +3 -3
- package/dist/src/ultracache/config.js +4 -4
- package/dist/src/ultracache/fetch.d.ts +2 -0
- package/dist/src/ultracache/fetch.js +1 -0
- package/dist/src/ultracache/overrides.js +4 -0
- package/dist/src/utils/acknowledge-build-scripts.d.ts +34 -1
- package/dist/src/utils/acknowledge-build-scripts.js +171 -32
- package/dist/src/utils/child-process.js +2 -1
- package/dist/src/utils/find-matching-projects.js +9 -6
- package/dist/src/utils/git-utils.d.ts +12 -5
- package/dist/src/utils/git-utils.js +69 -11
- package/dist/src/utils/long-running-target.d.ts +1 -3
- package/dist/src/utils/long-running-target.js +1 -3
- package/dist/src/utils/output.d.ts +4 -3
- package/dist/src/utils/output.js +4 -3
- package/dist/src/utils/owned-private-dir.d.ts +1 -17
- package/dist/src/utils/owned-private-dir.js +10 -62
- package/dist/src/utils/package-manager.d.ts +8 -0
- package/dist/src/utils/package-manager.js +65 -8
- package/dist/src/utils/perf-hooks.d.ts +1 -0
- package/dist/src/utils/plugins/core-plugins.js +5 -1
- package/dist/src/utils/progress-topics.d.ts +1 -0
- package/dist/src/utils/progress-topics.js +1 -0
- package/dist/src/utils/safe-spawn.js +1 -5
- package/dist/src/utils/shell-quoting.d.ts +8 -1
- package/dist/src/utils/shell-quoting.js +12 -1
- package/dist/src/utils/spawn-without-terminal.d.ts +27 -0
- package/dist/src/utils/spawn-without-terminal.js +110 -0
- package/dist/src/utils/string-similarity.d.ts +38 -0
- package/dist/src/utils/string-similarity.js +108 -0
- package/migrations.json +20 -6
- package/package.json +15 -24
- package/schemas/migrations-schema.json +11 -0
- package/schemas/nx-schema.json +133 -11
- package/schemas/project-schema.json +114 -4
- package/dist/src/command-line/migrate/agentic/prompts/generic-validation.d.ts +0 -56
- package/dist/src/command-line/migrate/agentic/prompts/generic-validation.js +0 -67
- package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.d.ts +0 -49
- package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.js +0 -53
- package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.d.ts +0 -26
- package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.js +0 -27
- package/dist/src/command-line/migrate/agentic/prompts/system-prompt.d.ts +0 -70
- package/dist/src/command-line/migrate/agentic/prompts/system-prompt.js +0 -113
- package/dist/src/command-line/migrate/agentic/run-step.d.ts +0 -68
- package/dist/src/command-line/migrate/agentic/run-step.js +0 -148
- package/dist/src/command-line/migrate/agentic/runner.d.ts +0 -21
- package/dist/src/command-line/migrate/agentic/runner.js +0 -262
- package/dist/src/migrations/update-23-0-0/consolidate-release-tag-config.d.ts +0 -9
- package/dist/src/migrations/update-23-0-0/consolidate-release-tag-config.js +0 -18
- package/dist/src/tasks-runner/legacy-depends-on-warning.d.ts +0 -18
- package/dist/src/tasks-runner/legacy-depends-on-warning.js +0 -109
|
@@ -5,7 +5,16 @@ exports.runOrchestratorResume = runOrchestratorResume;
|
|
|
5
5
|
exports.holdRunToContinue = holdRunToContinue;
|
|
6
6
|
exports.activeRunToReplace = activeRunToReplace;
|
|
7
7
|
exports.releaseRunToHandOff = releaseRunToHandOff;
|
|
8
|
+
exports.activeRunForClassic = activeRunForClassic;
|
|
9
|
+
exports.checkRunForStartFresh = checkRunForStartFresh;
|
|
10
|
+
exports.deleteRunForStartFresh = deleteRunForStartFresh;
|
|
11
|
+
exports.refuseNewRunAlongsideLiveRun = refuseNewRunAlongsideLiveRun;
|
|
12
|
+
exports.checkRunsForSingleMigration = checkRunsForSingleMigration;
|
|
13
|
+
exports.prepareSingleMigrationRun = prepareSingleMigrationRun;
|
|
14
|
+
exports.removeSingleMigrationRun = removeSingleMigrationRun;
|
|
8
15
|
exports.runOrchestratorReconcile = runOrchestratorReconcile;
|
|
16
|
+
exports.dispenseSingleMigrationStep = dispenseSingleMigrationStep;
|
|
17
|
+
exports.lastCoveringLandedEntry = lastCoveringLandedEntry;
|
|
9
18
|
exports.completionWarnings = completionWarnings;
|
|
10
19
|
const child_process_1 = require("child_process");
|
|
11
20
|
const crypto_1 = require("crypto");
|
|
@@ -42,7 +51,7 @@ const issues_1 = require("./issues");
|
|
|
42
51
|
const runbook_1 = require("./runbook");
|
|
43
52
|
const existing_run_report_1 = require("./existing-run-report");
|
|
44
53
|
const package_manager_1 = require("../../../utils/package-manager");
|
|
45
|
-
// The
|
|
54
|
+
// The migrate orchestrator: drives a durable run one dispense at a time.
|
|
46
55
|
// An outer AI agent runs each dispensed command and re-invokes `nx migrate
|
|
47
56
|
// --run-id=<id>` to reconcile; there is no long-lived process.
|
|
48
57
|
const PLAN_SNAPSHOT_0 = 'plan-0.json';
|
|
@@ -55,7 +64,11 @@ const HANG_THRESHOLD_MS = 15 * 60 * 1000;
|
|
|
55
64
|
const NO_PROGRESS_THRESHOLD = 3;
|
|
56
65
|
// Two rearms per step: one for the diagnosed fix and one for its correction.
|
|
57
66
|
const REARM_ESCALATION_CAP = 2;
|
|
58
|
-
const INIT_CONTINUE_HINT = 're-run the command, or
|
|
67
|
+
const INIT_CONTINUE_HINT = 're-run the command, or re-run it with --agentic=false to use the standard migrate flow.';
|
|
68
|
+
const ID_PROBLEM_REFUSALS = {
|
|
69
|
+
'not-shell-safe': 'contains characters that are not shell-safe. Orchestrated runs require shell-safe migration ids.',
|
|
70
|
+
duplicate: 'is listed more than once in the plan. Orchestrated runs require each migration id once.',
|
|
71
|
+
};
|
|
59
72
|
function continueRunHint(runId) {
|
|
60
73
|
return `re-run the command to continue run '${runId}'.`;
|
|
61
74
|
}
|
|
@@ -90,20 +103,20 @@ function refuseUnsafeScratchExposure(exposure, thenWhat) {
|
|
|
90
103
|
}
|
|
91
104
|
}
|
|
92
105
|
async function runOrchestratorInit(input) {
|
|
93
|
-
const { root, migrationsJson, migrationsPath, createCommits, commitPrefix, skipInstall, installedNxVersion, validate, finalValidation, emitAgentInstructions = true, onExistingRun = 'report', replaceRunId, confirmStart, } = input;
|
|
106
|
+
const { root, migrationsJson, migrationsPath, createCommits, commitPrefix, skipInstall, installedNxVersion, validate, finalValidation, emitAgentInstructions = true, onExistingRun = 'report', replaceRunId, confirmStart, singleMigration = false, } = input;
|
|
94
107
|
const migrations = (migrationsJson.migrations ?? []);
|
|
95
|
-
const sorted = (0, sort_migrations_1.sortMigrations)(migrations.slice()
|
|
96
|
-
hoistHandoffGitignore: true,
|
|
97
|
-
});
|
|
108
|
+
const sorted = (0, sort_migrations_1.sortMigrations)(migrations.slice());
|
|
98
109
|
const plannedIds = sorted.map((m) => `${m.package}:${m.name}`);
|
|
99
110
|
// An active run means a prior init already happened (e.g. it crashed before
|
|
100
111
|
// the agent's first reconcile); starting a second run would compete with it,
|
|
101
112
|
// and whether to continue it or replace it is the user's call, not a guess
|
|
102
113
|
// from the plans. Reported before the migration id check below: this plan
|
|
103
114
|
// is not being started. NewerRunStateFormatError propagates.
|
|
104
|
-
const active =
|
|
105
|
-
?
|
|
106
|
-
:
|
|
115
|
+
const active = singleMigration
|
|
116
|
+
? null
|
|
117
|
+
: onExistingRun === 'start-fresh'
|
|
118
|
+
? activeRunToReplace(root, replaceRunId)
|
|
119
|
+
: findActiveRunForInit(root);
|
|
107
120
|
if (active &&
|
|
108
121
|
(onExistingRun === 'report' ||
|
|
109
122
|
(replaceRunId !== undefined && active.runId !== replaceRunId))) {
|
|
@@ -118,19 +131,19 @@ async function runOrchestratorInit(input) {
|
|
|
118
131
|
}
|
|
119
132
|
if ((0, fs_1.existsSync)((0, run_state_1.migrateRunsDir)(root))) {
|
|
120
133
|
(0, state_lock_1.withRunCreationLock)(root, () => {
|
|
121
|
-
|
|
134
|
+
refuseNewRunAlongsideLiveRun(root, singleMigration);
|
|
122
135
|
if (active) {
|
|
123
136
|
refuseUndeletableRun(root, active.runId, active.state);
|
|
124
137
|
}
|
|
125
138
|
});
|
|
126
139
|
}
|
|
127
|
-
// Dispensed commands interpolate migration ids verbatim
|
|
128
|
-
// validates the incoming plan's ids. Before
|
|
129
|
-
// not be thrown away for a plan that cannot
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
140
|
+
// Dispensed commands interpolate migration ids verbatim and a worker finds
|
|
141
|
+
// its step by id, so every init validates the incoming plan's ids. Before
|
|
142
|
+
// the delete below: a run must not be thrown away for a plan that cannot
|
|
143
|
+
// start.
|
|
144
|
+
const [idProblem] = (0, run_state_1.migrationIdProblems)(sorted);
|
|
145
|
+
if (idProblem !== undefined) {
|
|
146
|
+
throw new Error(`The migration id '${idProblem.id}' ${ID_PROBLEM_REFUSALS[idProblem.problem]} To run the plan without orchestration, re-run with --agentic=false.`);
|
|
134
147
|
}
|
|
135
148
|
if (confirmStart && !(await confirmStart())) {
|
|
136
149
|
return { kind: 'refused' };
|
|
@@ -150,19 +163,11 @@ async function runOrchestratorInit(input) {
|
|
|
150
163
|
refuseUnsafeScratchExposure(scratchExposure, INIT_CONTINUE_HINT);
|
|
151
164
|
}
|
|
152
165
|
// Applied before the checkpoint so the entry (when it can be added) already
|
|
153
|
-
// covers older scratch by the time the checkpoint's `git add -A` runs
|
|
154
|
-
// fallback's standalone commit is suppressed because that checkpoint
|
|
155
|
-
// carries the edit. Unlike the classic loop, a planned ignore migration
|
|
156
|
-
// can't be deferred to: the run dir is created below, before that
|
|
157
|
-
// migration runs.
|
|
166
|
+
// covers older scratch by the time the checkpoint's `git add -A` runs.
|
|
158
167
|
await (0, handoff_gitignore_1.applyAgenticHandoffGitignoreFallback)({
|
|
159
168
|
migrations: sorted,
|
|
160
169
|
installedNxVersion,
|
|
161
|
-
effectiveCreateCommits: createCommits,
|
|
162
|
-
commitPrefix,
|
|
163
170
|
root,
|
|
164
|
-
applyWhenPlanned: true,
|
|
165
|
-
commitStandalone: false,
|
|
166
171
|
});
|
|
167
172
|
if (scratchExposure === 'unignored') {
|
|
168
173
|
// The fallback was the workspace's last chance at ignore coverage;
|
|
@@ -176,17 +181,21 @@ async function runOrchestratorInit(input) {
|
|
|
176
181
|
// migrate would wait on this process. The .gitignore fallback is idempotent.
|
|
177
182
|
let deleted = false;
|
|
178
183
|
const reserved = (0, state_lock_1.withRunCreationLock)(root, () => {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (nowActive && nowActive.runId !== active?.runId) {
|
|
182
|
-
return nowActive;
|
|
184
|
+
if (singleMigration) {
|
|
185
|
+
refuseNewRunAlongsideLiveRun(root, true);
|
|
183
186
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
if (
|
|
187
|
-
|
|
187
|
+
else {
|
|
188
|
+
const nowActive = findActiveRunForInit(root, active !== null);
|
|
189
|
+
if (nowActive && nowActive.runId !== active?.runId) {
|
|
190
|
+
return nowActive;
|
|
191
|
+
}
|
|
192
|
+
if (active) {
|
|
193
|
+
// Completed meanwhile (a reconcile finished it): nothing to replace.
|
|
194
|
+
if (!nowActive) {
|
|
195
|
+
throw new Error(noActiveRunToReplace(active.runId));
|
|
196
|
+
}
|
|
197
|
+
deleted = deleteRunRecord(root, active.runId);
|
|
188
198
|
}
|
|
189
|
-
deleted = deleteRunRecord(root, active.runId);
|
|
190
199
|
}
|
|
191
200
|
// The snapshot must exist before run.json makes the run discoverable: a
|
|
192
201
|
// crash in between must not leave an active run without its plan.
|
|
@@ -224,7 +233,7 @@ async function runOrchestratorInit(input) {
|
|
|
224
233
|
// step's gitRefBefore and rules out clean retries for the whole run. A
|
|
225
234
|
// failed probe counts as dirty: mistaking it for clean would let a later
|
|
226
235
|
// retry-clean reset destroy the very work this flag exists to protect.
|
|
227
|
-
const checkpointFailed = createCommits && (0,
|
|
236
|
+
const checkpointFailed = createCommits && (0, migrate_commits_1.migrateTreeStatus)(root) !== 'clean';
|
|
228
237
|
const branch = (0, git_utils_1.getGitCurrentBranch)(root);
|
|
229
238
|
const state = {
|
|
230
239
|
formatVersion: run_state_1.CURRENT_RUN_STATE_FORMAT_VERSION,
|
|
@@ -248,6 +257,7 @@ async function runOrchestratorInit(input) {
|
|
|
248
257
|
steps: buildSteps(sorted, finalValidation !== false),
|
|
249
258
|
commits: checkpoint ? [checkpoint] : [],
|
|
250
259
|
...(checkpointFailed ? { checkpointFailed: true } : {}),
|
|
260
|
+
...(singleMigration ? { singleMigration: true } : {}),
|
|
251
261
|
analytics: { startEmitted: false, completeEmitted: false },
|
|
252
262
|
};
|
|
253
263
|
(0, state_lock_1.withRunCreationLock)(root, () => {
|
|
@@ -273,9 +283,9 @@ function runOrchestratorResume(input) {
|
|
|
273
283
|
* Holds the active run `runId` names and returns its state. A continue calls
|
|
274
284
|
* this before the install and the agent selection: a concurrent start-fresh
|
|
275
285
|
* must not delete the run meanwhile. Throws when the id names no run or a
|
|
276
|
-
* finished one, and while another process holds the run
|
|
277
|
-
* open a second session over a live one.
|
|
278
|
-
* holder refuses.
|
|
286
|
+
* finished one, and while another process holds the run or works on a
|
|
287
|
+
* different run: continuing would open a second session over a live one.
|
|
288
|
+
* Under WASM nothing is held and no holder refuses.
|
|
279
289
|
*/
|
|
280
290
|
function holdRunToContinue(root, runId) {
|
|
281
291
|
if (!run_id_1.RUN_ID_SAFE.test(runId)) {
|
|
@@ -314,52 +324,194 @@ function activeRunToReplace(root, runId) {
|
|
|
314
324
|
function releaseRunToHandOff(root, runId) {
|
|
315
325
|
(0, state_lock_1.releaseRunActivity)((0, run_state_1.runDir)(root, runId));
|
|
316
326
|
}
|
|
327
|
+
/**
|
|
328
|
+
* The active run a classic `--run-migrations` must not run over, held while
|
|
329
|
+
* the caller reports it or asks what to do; null when no run is active. Run
|
|
330
|
+
* directories that cannot be read refuse as they do for init.
|
|
331
|
+
*/
|
|
332
|
+
function activeRunForClassic(root, plannedIds) {
|
|
333
|
+
const active = findActiveRunForInit(root);
|
|
334
|
+
if (!active)
|
|
335
|
+
return null;
|
|
336
|
+
// Held before the facts are read, as init's report does.
|
|
337
|
+
(0, state_lock_1.holdRunActivity)(root, active.runId);
|
|
338
|
+
return {
|
|
339
|
+
runId: active.runId,
|
|
340
|
+
facts: (0, existing_run_report_1.collectExistingRunFacts)(root, active.runId, active.state, plannedIds),
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Throws when a classic start fresh could not delete the active run `runId`.
|
|
345
|
+
* Run before the commit prompt, as init does, so the user is not asked for a
|
|
346
|
+
* run that is then refused; deleteRunForStartFresh repeats the checks.
|
|
347
|
+
*/
|
|
348
|
+
function checkRunForStartFresh(root, runId) {
|
|
349
|
+
(0, state_lock_1.withRunCreationLock)(root, () => {
|
|
350
|
+
const active = activeRunToDelete(root, runId);
|
|
351
|
+
refuseUndeletableRun(root, runId, active.state);
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Deletes the record of the active run `runId` for a classic start fresh,
|
|
356
|
+
* under the refusals init applies before it replaces a run. The classic loop
|
|
357
|
+
* records no run of its own afterwards.
|
|
358
|
+
*/
|
|
359
|
+
function deleteRunForStartFresh(root, runId) {
|
|
360
|
+
const deleted = (0, state_lock_1.withRunCreationLock)(root, () => {
|
|
361
|
+
activeRunToDelete(root, runId);
|
|
362
|
+
return deleteRunRecord(root, runId);
|
|
363
|
+
});
|
|
364
|
+
if (deleted) {
|
|
365
|
+
removeDeletedRunDir(root, runId);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
// Call under the creation lock. A run that became active since the user chose
|
|
369
|
+
// is refused, never deleted in place of the one they saw.
|
|
370
|
+
function activeRunToDelete(root, runId) {
|
|
371
|
+
const active = findActiveRunForInit(root, true);
|
|
372
|
+
if (!active) {
|
|
373
|
+
throw new Error(noActiveRunToReplace(runId));
|
|
374
|
+
}
|
|
375
|
+
if (active.runId !== runId) {
|
|
376
|
+
throw new Error(`Not starting fresh: migrate run '${active.runId}' became active while this command was starting. Re-run the command to see it.`);
|
|
377
|
+
}
|
|
378
|
+
return active;
|
|
379
|
+
}
|
|
317
380
|
// Reads the newest active run; null when no run is active. Uninterpretable
|
|
318
381
|
// run dirs refuse a fresh start (one of them could be an active run this
|
|
319
|
-
//
|
|
320
|
-
// unless `refuseUninterpretable`: a start-fresh would delete that run
|
|
321
|
-
// then find only the unreadable ones. NewerRunStateFormatError propagates
|
|
322
|
-
// from the read.
|
|
382
|
+
// invocation would compete with) but only warn when a healthy active run is
|
|
383
|
+
// found, unless `refuseUninterpretable`: a start-fresh would delete that run
|
|
384
|
+
// and then find only the unreadable ones. NewerRunStateFormatError propagates
|
|
385
|
+
// from the read. Refuses while another process works on a run it wouldn't
|
|
386
|
+
// report.
|
|
323
387
|
function findActiveRunForInit(root, refuseUninterpretable = false) {
|
|
388
|
+
refuseNewRunAlongsideLiveRun(root);
|
|
324
389
|
const { active, uninterpretable } = (0, run_state_1.findActiveRun)(root);
|
|
325
390
|
if (uninterpretable.length > 0) {
|
|
326
|
-
const noun = uninterpretable.length === 1 ? 'directory' : 'directories';
|
|
327
|
-
// A directory name is whatever is on disk and a reason quotes what it
|
|
328
|
-
// found, so neither can be trusted to stay on the line it is put on.
|
|
329
|
-
// Sanitized here rather than left to the gateway: these same lines are
|
|
330
|
-
// joined into the throw below, which leaves through handleErrors.
|
|
331
|
-
const details = uninterpretable.map((u) => `${types_1.MIGRATE_RUNS_RELATIVE_DIR}/${(0, text_1.singleLine)(u.dirName)}: ${(0, text_1.singleLine)(u.reason)}`);
|
|
332
391
|
if (!active || refuseUninterpretable) {
|
|
392
|
+
const noun = uninterpretable.length === 1 ? 'directory' : 'directories';
|
|
333
393
|
throw new Error([
|
|
334
|
-
`Whether a migrate run is still active could not be determined;
|
|
335
|
-
...
|
|
394
|
+
`Whether a migrate run is still active could not be determined; running the plan could re-apply migrations an unfinished run already applied.`,
|
|
395
|
+
...uninterpretableDetails(uninterpretable),
|
|
336
396
|
`Fix or remove the listed ${noun}, then re-run the command.`,
|
|
337
397
|
].join('\n'));
|
|
338
398
|
}
|
|
399
|
+
warnIgnoredRunDirs(uninterpretable);
|
|
400
|
+
}
|
|
401
|
+
return active;
|
|
402
|
+
}
|
|
403
|
+
// A directory name is whatever is on disk and a reason quotes what it found,
|
|
404
|
+
// so neither can be trusted to stay on the line it is put on. Sanitized here
|
|
405
|
+
// rather than left to the gateway: these lines are also joined into a throw,
|
|
406
|
+
// which leaves through handleErrors.
|
|
407
|
+
function uninterpretableDetails(uninterpretable) {
|
|
408
|
+
return uninterpretable.map((u) => `${types_1.MIGRATE_RUNS_RELATIVE_DIR}/${(0, text_1.singleLine)(u.dirName)}: ${(0, text_1.singleLine)(u.reason)}`);
|
|
409
|
+
}
|
|
410
|
+
function warnIgnoredRunDirs(uninterpretable) {
|
|
411
|
+
const noun = uninterpretable.length === 1 ? 'directory' : 'directories';
|
|
412
|
+
(0, agent_output_1.warnToAgent)({
|
|
413
|
+
title: `Ignoring ${uninterpretable.length} migrate run ${noun} that could not be read.`,
|
|
414
|
+
bodyLines: uninterpretableDetails(uninterpretable),
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Throws while another process works on a run that init wouldn't report: a
|
|
419
|
+
* `--run-migration` run, a run being started, or a run nx can't read. A new
|
|
420
|
+
* run would install and commit over its in-progress edits. An active
|
|
421
|
+
* `--run-migrations` run is reported instead, except to a `--run-migration`
|
|
422
|
+
* run, and records no process holds block nothing.
|
|
423
|
+
*/
|
|
424
|
+
function refuseNewRunAlongsideLiveRun(root, singleMigration = false) {
|
|
425
|
+
const live = (0, state_lock_1.findOtherLiveRuns)(root).find((run) => singleMigration || !run.state || (0, run_state_1.runKind)(run.state) !== 'active');
|
|
426
|
+
if (live) {
|
|
427
|
+
throw (0, state_lock_1.liveRunRefusal)(singleMigration
|
|
428
|
+
? 'Not running the migration'
|
|
429
|
+
: 'Not running the migrations', live);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* The refusals of prepareSingleMigrationRun alone, for the CLI to make before
|
|
434
|
+
* its install. Removes and warns about nothing.
|
|
435
|
+
*/
|
|
436
|
+
function checkRunsForSingleMigration(root) {
|
|
437
|
+
if (!(0, fs_1.existsSync)((0, run_state_1.migrateRunsDir)(root)))
|
|
438
|
+
return;
|
|
439
|
+
(0, state_lock_1.withRunCreationLock)(root, () => refuseNewRunAlongsideLiveRun(root, true));
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* The check every `--run-migration` run without a run id makes before it runs
|
|
443
|
+
* anything. Throws while another nx migrate process works on a run, removes
|
|
444
|
+
* `--run-migration` records no process holds, and warns about idle active
|
|
445
|
+
* runs, which do not record this migration.
|
|
446
|
+
*/
|
|
447
|
+
function prepareSingleMigrationRun(root) {
|
|
448
|
+
if (!(0, fs_1.existsSync)((0, run_state_1.migrateRunsDir)(root)))
|
|
449
|
+
return;
|
|
450
|
+
const idleRunIds = [];
|
|
451
|
+
const uninterpretable = [];
|
|
452
|
+
(0, state_lock_1.withRunCreationLock)(root, () => {
|
|
453
|
+
refuseNewRunAlongsideLiveRun(root, true);
|
|
454
|
+
// No other process holds what is left.
|
|
455
|
+
for (const run of (0, state_lock_1.listOtherRuns)(root)) {
|
|
456
|
+
if (run.starting)
|
|
457
|
+
continue;
|
|
458
|
+
if (run.state?.singleMigration === true) {
|
|
459
|
+
removeSingleMigrationRun(root, run.dirName);
|
|
460
|
+
}
|
|
461
|
+
else if (run.state) {
|
|
462
|
+
idleRunIds.push(run.dirName);
|
|
463
|
+
}
|
|
464
|
+
else if (!(run.readError instanceof run_state_1.NewerRunStateFormatError)) {
|
|
465
|
+
// A newer nx's run is that nx's to report.
|
|
466
|
+
uninterpretable.push({
|
|
467
|
+
dirName: run.dirName,
|
|
468
|
+
reason: (0, util_1.summarizeError)(run.readError),
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
if (uninterpretable.length > 0) {
|
|
474
|
+
warnIgnoredRunDirs(uninterpretable);
|
|
475
|
+
}
|
|
476
|
+
if (idleRunIds.length > 0) {
|
|
477
|
+
const runs = idleRunIds.map((id) => `'${(0, text_1.singleLine)(id)}'`).join(', ');
|
|
339
478
|
(0, agent_output_1.warnToAgent)({
|
|
340
|
-
title: `
|
|
341
|
-
bodyLines:
|
|
479
|
+
title: `This migration won't be recorded into the active migrate ${idleRunIds.length === 1 ? 'run' : 'runs'} ${runs}.`,
|
|
480
|
+
bodyLines: [
|
|
481
|
+
idleRunIds.length === 1
|
|
482
|
+
? 'Continuing that run may apply the migration again.'
|
|
483
|
+
: 'Continuing one of those runs may apply the migration again.',
|
|
484
|
+
],
|
|
342
485
|
});
|
|
343
486
|
}
|
|
344
|
-
return active;
|
|
345
487
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
function
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
488
|
+
/**
|
|
489
|
+
* Removes a `--run-migration` run's record. Call under the creation lock. Kept
|
|
490
|
+
* while another process still works on the run, such as a worker the agent
|
|
491
|
+
* left running: other runs refuse alongside it, and the next
|
|
492
|
+
* `--run-migration` removes it once none does. Otherwise best effort: what is
|
|
493
|
+
* left blocks nothing.
|
|
494
|
+
*/
|
|
495
|
+
function removeSingleMigrationRun(root, runId) {
|
|
496
|
+
const dir = (0, run_state_1.runDir)(root, runId);
|
|
497
|
+
const path = `${types_1.MIGRATE_RUNS_RELATIVE_DIR}/${runId}`;
|
|
498
|
+
try {
|
|
499
|
+
const deletion = deleteUnheldRunRecord(root, runId);
|
|
500
|
+
if (typeof deletion === 'object') {
|
|
501
|
+
(0, state_lock_1.releaseRunActivity)(dir);
|
|
502
|
+
(0, agent_output_1.warnToAgent)({
|
|
503
|
+
title: deletion.holders === 'unknown'
|
|
504
|
+
? `Kept ${path}: nx cannot tell whether another nx migrate process is still working on it. Make sure none is, then remove it.`
|
|
505
|
+
: `Kept ${path}: ${(0, state_lock_1.describeHolders)(deletion.holders)}. Other nx migrate runs refuse until it ends, and the next --run-migration removes it.`,
|
|
506
|
+
});
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
(0, fs_1.rmSync)(dir, { recursive: true, force: true });
|
|
510
|
+
}
|
|
511
|
+
catch (e) {
|
|
512
|
+
(0, agent_output_1.warnToAgent)({
|
|
513
|
+
title: `Could not remove ${path}: ${(0, util_1.summarizeError)(e)}. It blocks nothing; remove it when convenient.`,
|
|
514
|
+
});
|
|
363
515
|
}
|
|
364
516
|
}
|
|
365
517
|
// The report for an init that found a run already active. On the agent path
|
|
@@ -405,7 +557,7 @@ function refuseUndeletableRun(root, runId, state) {
|
|
|
405
557
|
throw new Error(`Not deleting migrate run '${runId}': without a native file lock nx cannot tell whether another nx migrate process is acting on it. Make sure none is, remove ${types_1.MIGRATE_RUNS_RELATIVE_DIR}/${runId}, then re-run the command.`);
|
|
406
558
|
}
|
|
407
559
|
const holders = (0, state_lock_1.liveRunActivityPids)((0, run_state_1.runDir)(root, runId));
|
|
408
|
-
if (
|
|
560
|
+
if ((0, state_lock_1.isHeld)(holders)) {
|
|
409
561
|
throw (0, state_lock_1.heldRunError)('deleting', runId, holders);
|
|
410
562
|
}
|
|
411
563
|
const workers = (0, existing_run_report_1.liveWorkers)(state);
|
|
@@ -421,19 +573,36 @@ function refuseUndeletableRun(root, runId, state) {
|
|
|
421
573
|
function noActiveRunToReplace(runId) {
|
|
422
574
|
return `Not starting fresh: ${runId === undefined ? 'no migrate run' : `no migrate run '${runId}'`} is active, so there is nothing to replace. To start a run, re-run the command without --start-fresh and --run-id.`;
|
|
423
575
|
}
|
|
424
|
-
//
|
|
425
|
-
//
|
|
426
|
-
// state: the early pass predates the prompt. False when already gone.
|
|
576
|
+
// The refusals run again on fresh state: the early pass predates the prompt.
|
|
577
|
+
// False when already gone.
|
|
427
578
|
function deleteRunRecord(root, runId) {
|
|
579
|
+
const deletion = deleteUnheldRunRecord(root, runId, (state) => refuseUndeletableRun(root, runId, state));
|
|
580
|
+
if (deletion === 'deleted')
|
|
581
|
+
return true;
|
|
582
|
+
if (deletion === 'gone')
|
|
583
|
+
return false;
|
|
584
|
+
throw (0, state_lock_1.heldRunError)('deleting', runId, deletion.holders);
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Removes run.json unless another process holds the run, and returns its
|
|
588
|
+
* holders instead. Call under the creation lock, which every claim takes, so
|
|
589
|
+
* no process starts holding the run between the check and the removal. The
|
|
590
|
+
* state lock keeps a reconcile or worker from starting against a directory
|
|
591
|
+
* about to disappear. `refuse` sees the state read under it.
|
|
592
|
+
*/
|
|
593
|
+
function deleteUnheldRunRecord(root, runId, refuse) {
|
|
428
594
|
const dir = (0, run_state_1.runDir)(root, runId);
|
|
429
595
|
return (0, state_lock_1.withRunStateLock)(dir, () => {
|
|
430
596
|
if (!(0, run_state_1.hasRunState)(dir))
|
|
431
|
-
return
|
|
432
|
-
|
|
433
|
-
|
|
597
|
+
return 'gone';
|
|
598
|
+
refuse?.((0, run_state_1.readRunState)(dir));
|
|
599
|
+
const holders = (0, state_lock_1.liveRunActivityPids)(dir);
|
|
600
|
+
if ((0, state_lock_1.isHeld)(holders))
|
|
601
|
+
return { holders };
|
|
602
|
+
// The hold's lock file is inside the directory removed afterwards.
|
|
434
603
|
(0, state_lock_1.releaseRunActivity)(dir);
|
|
435
604
|
(0, fs_1.rmSync)((0, path_1.join)(dir, run_state_1.RUN_STATE_FILE_NAME), { force: true });
|
|
436
|
-
return
|
|
605
|
+
return 'deleted';
|
|
437
606
|
});
|
|
438
607
|
}
|
|
439
608
|
// The rest of a deleted run's directory, removed outside the locks and best
|
|
@@ -518,7 +687,7 @@ function ensureCheckpoint(root, dir, state) {
|
|
|
518
687
|
// The retried checkpoint captured everything, so clean retries are safe
|
|
519
688
|
// again. Only a verified-clean tree clears the flag: a failed probe proves
|
|
520
689
|
// nothing was captured.
|
|
521
|
-
const cleared = (0,
|
|
690
|
+
const cleared = (0, migrate_commits_1.migrateTreeStatus)(root) === 'clean';
|
|
522
691
|
if (!checkpoint && !cleared)
|
|
523
692
|
return state;
|
|
524
693
|
return (0, state_lock_1.updateRunState)(dir, (fresh) => {
|
|
@@ -548,7 +717,7 @@ function ensureCheckpoint(root, dir, state) {
|
|
|
548
717
|
function checkpointEntry(root, commitPrefix, before) {
|
|
549
718
|
// Skip only on a verified-clean tree; on a failed probe the commit attempt
|
|
550
719
|
// below re-probes and may succeed once the transient failure passes.
|
|
551
|
-
if ((0,
|
|
720
|
+
if ((0, migrate_commits_1.migrateTreeStatus)(root) === 'clean') {
|
|
552
721
|
return null;
|
|
553
722
|
}
|
|
554
723
|
(0, migrate_commits_1.commitCheckpointBeforeMigrations)(root, commitPrefix);
|
|
@@ -566,7 +735,8 @@ function finishInit(root, dir, runId, state, origin, emitAgentInstructions,
|
|
|
566
735
|
// which must fail before its git and state side effects).
|
|
567
736
|
runbook) {
|
|
568
737
|
let current = state;
|
|
569
|
-
if (
|
|
738
|
+
if (reportsOrchestratorAnalytics(current) &&
|
|
739
|
+
!current.analytics.startEmitted) {
|
|
570
740
|
// Claim the watermark on the fresh state first: of two concurrent inits
|
|
571
741
|
// exactly one flips it, and only that one reports.
|
|
572
742
|
let claimed = false;
|
|
@@ -588,7 +758,7 @@ runbook) {
|
|
|
588
758
|
});
|
|
589
759
|
}
|
|
590
760
|
}
|
|
591
|
-
if (origin === 'resumed') {
|
|
761
|
+
if (origin === 'resumed' && reportsOrchestratorAnalytics(current)) {
|
|
592
762
|
(0, migrate_analytics_1.reportMigrateOrchestratorResume)((0, state_machine_1.runTallies)(current));
|
|
593
763
|
}
|
|
594
764
|
const content = runbook ?? ensureRunbook(root, dir, runId, current);
|
|
@@ -694,6 +864,7 @@ function runbookContext(root, runId, state) {
|
|
|
694
864
|
// The pass guidance follows the plan: an opted-out run, or one recorded
|
|
695
865
|
// before the pass existed, has no pass step to dispense.
|
|
696
866
|
finalValidation: state.steps.some((s) => s.kind === 'final-validation'),
|
|
867
|
+
singleMigration: state.singleMigration === true,
|
|
697
868
|
};
|
|
698
869
|
}
|
|
699
870
|
// Only the newest recorded commit is checked, so the warning stops on its own
|
|
@@ -760,7 +931,7 @@ async function runOrchestratorReconcile(input) {
|
|
|
760
931
|
try {
|
|
761
932
|
const result = await applyReconcileStepAction(root, dir, state, stepAction, scope);
|
|
762
933
|
if (result.kind === 'error') {
|
|
763
|
-
emitError(root, runId, result.reason);
|
|
934
|
+
emitError(root, runId, state, result.reason);
|
|
764
935
|
return; // no transition was written
|
|
765
936
|
}
|
|
766
937
|
const target = result.targetStep;
|
|
@@ -772,10 +943,12 @@ async function runOrchestratorReconcile(input) {
|
|
|
772
943
|
!result.resetTree) {
|
|
773
944
|
const outcome = await (0, broker_1.giveUpStepTree)(root, dir, target, state.skipInstall === true, reconcileCommand(root, runId), scope);
|
|
774
945
|
if (outcome.kind === 'refused') {
|
|
775
|
-
emitError(root, runId, outcome.reason);
|
|
946
|
+
emitError(root, runId, state, outcome.reason);
|
|
776
947
|
return;
|
|
777
948
|
}
|
|
778
|
-
(0, give_up_1.warnAboutGiveUp)(target, outcome);
|
|
949
|
+
(0, give_up_1.warnAboutGiveUp)(target, outcome, (0, util_1.commitFailureRecovery)(state));
|
|
950
|
+
if (target.installFailed === true)
|
|
951
|
+
warnInstallLeftToUser(root, target);
|
|
779
952
|
// Settled already; the dispense refuses a tree still held.
|
|
780
953
|
scope.lease?.release();
|
|
781
954
|
scope.lease = undefined;
|
|
@@ -786,8 +959,9 @@ async function runOrchestratorReconcile(input) {
|
|
|
786
959
|
// sections stay synchronous); the transition and any unrecorded entry then
|
|
787
960
|
// land in one write. Adopt and skip keep the tree, so the install they owe
|
|
788
961
|
// runs here: the next dispense would take the changed deps as its baseline.
|
|
789
|
-
// Giving up keeps it the same way when the run does not commit.
|
|
790
|
-
//
|
|
962
|
+
// Giving up keeps it the same way when the run does not commit. An install
|
|
963
|
+
// that already failed is left to the step's mark instead. Retries owe
|
|
964
|
+
// nothing: the rearmed attempt reconciles itself.
|
|
791
965
|
const { entry, installFailed, recorded } = await stepActionSideEffects(root, dir, state, target, stepAction, result.resetTree, scope);
|
|
792
966
|
// A rearm starts a fresh attempt; drop the stale handoff before the rearm
|
|
793
967
|
// is persisted so a crash in between can't refold the old outcome into the
|
|
@@ -855,7 +1029,7 @@ async function runOrchestratorReconcile(input) {
|
|
|
855
1029
|
: next;
|
|
856
1030
|
});
|
|
857
1031
|
if (freshRejection) {
|
|
858
|
-
emitError(root, runId, entry?.kind === 'landed' && entry.sha
|
|
1032
|
+
emitError(root, runId, state, entry?.kind === 'landed' && entry.sha
|
|
859
1033
|
? `${freshRejection} Note: this action's commit ${entry.sha} had already landed and stays in history; resolve the step against the tree as it stands now.`
|
|
860
1034
|
: freshRejection);
|
|
861
1035
|
return;
|
|
@@ -863,6 +1037,11 @@ async function runOrchestratorReconcile(input) {
|
|
|
863
1037
|
if (unresolvedArchiveError !== undefined) {
|
|
864
1038
|
(0, give_up_1.warnUnresolvedNotArchived)((0, state_machine_1.stepLabel)(target), unresolvedArchiveError);
|
|
865
1039
|
}
|
|
1040
|
+
// A retry's rearm drops the mark: its next attempt installs.
|
|
1041
|
+
if (target.installFailed === true &&
|
|
1042
|
+
written.steps.find((s) => s.id === target.id)?.installFailed === true) {
|
|
1043
|
+
warnInstallLeftToUser(root, target);
|
|
1044
|
+
}
|
|
866
1045
|
state = written;
|
|
867
1046
|
}
|
|
868
1047
|
finally {
|
|
@@ -1103,7 +1282,7 @@ async function foldLedgerEntry(root, dir, state, step, promptOutcome, scope) {
|
|
|
1103
1282
|
// is not verifiably clean (see foldLedgerEntry for why).
|
|
1104
1283
|
async function retainedTreeSideEffects(root, dir, state, step, seam, scope) {
|
|
1105
1284
|
const installFailed = await installFailedForStep(root, dir, state, step, seam, scope);
|
|
1106
|
-
const entry = state.createCommits && (0,
|
|
1285
|
+
const entry = state.createCommits && (0, migrate_commits_1.migrateTreeStatus)(root) !== 'clean'
|
|
1107
1286
|
? { kind: 'failed', stepIds: [step.id] }
|
|
1108
1287
|
: null;
|
|
1109
1288
|
return { entry, installFailed };
|
|
@@ -1131,6 +1310,14 @@ async function installFailedForStep(root, dir, state, step, seam, scope) {
|
|
|
1131
1310
|
return true;
|
|
1132
1311
|
}
|
|
1133
1312
|
}
|
|
1313
|
+
function warnInstallLeftToUser(root, step) {
|
|
1314
|
+
(0, agent_output_1.warnToAgent)({
|
|
1315
|
+
title: `The dependency changes made by ${(0, state_machine_1.stepLabel)(step)} are not installed.`,
|
|
1316
|
+
bodyLines: [
|
|
1317
|
+
`Run \`${(0, util_1.pmInstallCommand)(root)}\` once the cause of the failed install is fixed. The run's final report repeats this unless a later install covers them.`,
|
|
1318
|
+
],
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1134
1321
|
// A failed handoff fails the prompt; a success handoff completes it, unless it
|
|
1135
1322
|
// marks the prompt not applicable via `extras.outcome === 'skipped'`.
|
|
1136
1323
|
function handoffToPromptOutcome(handoff) {
|
|
@@ -1232,7 +1419,7 @@ async function applyReconcileStepAction(root, dir, state, action, scope) {
|
|
|
1232
1419
|
// Only the tree can say whether the reset left it clean. Anything else is
|
|
1233
1420
|
// refused: accepting would rearm a step whose next attempt reruns the
|
|
1234
1421
|
// generator over the previous attempt's output.
|
|
1235
|
-
if ((0,
|
|
1422
|
+
if ((0, migrate_commits_1.migrateTreeStatus)(root) !== 'clean') {
|
|
1236
1423
|
return {
|
|
1237
1424
|
kind: 'error',
|
|
1238
1425
|
reason: `Cannot apply action 'retry-clean' to step '${step.id}': the working tree is not verifiably clean after the reset to ${step.gitRefBefore}. Inspect it with \`git status\`, then re-run it. ${fallback}`,
|
|
@@ -1261,7 +1448,7 @@ async function applyReconcileStepAction(root, dir, state, action, scope) {
|
|
|
1261
1448
|
reason: `Cannot apply action 'unresolved' to step '${step.id}': the reset to ${step.gitRefBefore} failed: ${e instanceof Error ? e.message : String(e)}`,
|
|
1262
1449
|
};
|
|
1263
1450
|
}
|
|
1264
|
-
if ((0,
|
|
1451
|
+
if ((0, migrate_commits_1.migrateTreeStatus)(root) !== 'clean') {
|
|
1265
1452
|
return {
|
|
1266
1453
|
kind: 'error',
|
|
1267
1454
|
reason: `Cannot apply action 'unresolved' to step '${step.id}': the working tree is not verifiably clean after the reset to ${step.gitRefBefore}. Inspect it with \`git status\`, then re-run it.`,
|
|
@@ -1316,11 +1503,13 @@ function unresolvedResetsTree(state, step, cleanRetry) {
|
|
|
1316
1503
|
async function stepActionSideEffects(root, dir, state, step, action, resetTree, scope) {
|
|
1317
1504
|
switch (action) {
|
|
1318
1505
|
case 'adopt':
|
|
1319
|
-
// A died worker's
|
|
1320
|
-
//
|
|
1321
|
-
// would be replayed.
|
|
1506
|
+
// A died worker's commit request is reused while nothing records its
|
|
1507
|
+
// outcome, since the session may still answer it. Otherwise the adopt
|
|
1508
|
+
// asks under its own request, or the old answer would be replayed.
|
|
1322
1509
|
return state.createCommits
|
|
1323
1510
|
? commitForStep(root, dir, state, step, scope, step.status === 'failed' ||
|
|
1511
|
+
step.commitLedgerIndex !== undefined ||
|
|
1512
|
+
step.installFailed === true ||
|
|
1324
1513
|
(0, state_machine_1.coveringLandedEntries)(state, step.id).length > 0
|
|
1325
1514
|
? 'adopt'
|
|
1326
1515
|
: undefined)
|
|
@@ -1356,9 +1545,10 @@ async function commitForStep(root, dir, state, step, scope, commitAs) {
|
|
|
1356
1545
|
const name = (0, state_machine_1.commitNameForStep)(step);
|
|
1357
1546
|
const absorbedStepIds = (0, state_machine_1.uncoveredFailedStepIds)(state).filter((id) => id !== step.id);
|
|
1358
1547
|
const skipInstall = state.skipInstall === true;
|
|
1548
|
+
const recovery = (0, util_1.commitFailureRecovery)(state);
|
|
1359
1549
|
let commit;
|
|
1360
1550
|
try {
|
|
1361
|
-
commit = await (0, broker_1.commitStepTree)(dir, step, absorbedStepIds, () => (0, migrate_commits_1.commitMigrationIfRequested)(root, { name }, true, state.commitPrefix, () => (0, util_1.installDepsChangedSinceDispense)(root, dir, step, skipInstall, reconcileCommand(root, state.runId)), (0, state_machine_1.stepsToPendingMigrations)(state, absorbedStepIds)), scope, commitAs);
|
|
1551
|
+
commit = await (0, broker_1.commitStepTree)(dir, step, absorbedStepIds, () => (0, migrate_commits_1.commitMigrationIfRequested)(root, { name }, true, state.commitPrefix, () => (0, util_1.installDepsChangedSinceDispense)(root, dir, step, skipInstall, reconcileCommand(root, state.runId)), (0, state_machine_1.stepsToPendingMigrations)(state, absorbedStepIds), recovery), scope, commitAs);
|
|
1362
1552
|
}
|
|
1363
1553
|
catch (e) {
|
|
1364
1554
|
// Nothing to record: the step moved on, another process holds the tree, or
|
|
@@ -1372,14 +1562,14 @@ async function commitForStep(root, dir, state, step, scope, commitAs) {
|
|
|
1372
1562
|
// reports through result.status. The debt cannot stand in for the install
|
|
1373
1563
|
// failure, since a later commit absorbing this diff clears the debt while
|
|
1374
1564
|
// the dependencies are still missing.
|
|
1375
|
-
(0, util_1.warnCommitFailed)(name, e);
|
|
1565
|
+
(0, util_1.warnCommitFailed)(name, recovery, e);
|
|
1376
1566
|
return {
|
|
1377
1567
|
entry: { kind: 'failed', stepIds: [step.id] },
|
|
1378
1568
|
installFailed: true,
|
|
1379
1569
|
};
|
|
1380
1570
|
}
|
|
1381
1571
|
if (commit.result.status === 'failed') {
|
|
1382
|
-
(0, util_1.warnCommitFailed)(name);
|
|
1572
|
+
(0, util_1.warnCommitFailed)(name, recovery);
|
|
1383
1573
|
}
|
|
1384
1574
|
return {
|
|
1385
1575
|
entry: (0, state_machine_1.commitResultToLedgerEntry)(commit.result, step.id, commit.absorbedStepIds),
|
|
@@ -1501,11 +1691,7 @@ function dispenseNextStep(root, dir, runId, state, step, noProgress) {
|
|
|
1501
1691
|
// Read the pre-step baselines (git and package.json reads) before the
|
|
1502
1692
|
// lock; the dispense transition and the baselines then apply to the fresh
|
|
1503
1693
|
// state in one write.
|
|
1504
|
-
const baselines =
|
|
1505
|
-
gitRefBefore: (0, git_utils_1.getLatestCommitSha)(root) ?? undefined,
|
|
1506
|
-
treeCleanAtDispense: (0, git_utils_1.getWorkingTreeStatus)(root) === 'clean',
|
|
1507
|
-
depsHashAtDispense: (0, util_1.depsHash)(root),
|
|
1508
|
-
};
|
|
1694
|
+
const baselines = readDispenseBaselines(root);
|
|
1509
1695
|
let advancedElsewhere = false;
|
|
1510
1696
|
let held;
|
|
1511
1697
|
const current = (0, state_lock_1.updateRunState)(dir, (fresh) => {
|
|
@@ -1536,10 +1722,12 @@ function dispenseNextStep(root, dir, runId, state, step, noProgress) {
|
|
|
1536
1722
|
return;
|
|
1537
1723
|
}
|
|
1538
1724
|
const dispensed = current.steps.find((s) => s.id === step.id);
|
|
1539
|
-
(
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1725
|
+
if (reportsOrchestratorAnalytics(current)) {
|
|
1726
|
+
(0, migrate_analytics_1.reportMigrateOrchestratorStepDispensed)({
|
|
1727
|
+
attempt: dispensed.attempt,
|
|
1728
|
+
ordinal: (0, state_machine_1.runTallies)(current).dispenseCount,
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1543
1731
|
switch (dispensed.kind) {
|
|
1544
1732
|
case 'migration':
|
|
1545
1733
|
emitNextStep(root, runId, current, dispensed, noProgress);
|
|
@@ -1553,6 +1741,25 @@ function dispenseNextStep(root, dir, runId, state, step, noProgress) {
|
|
|
1553
1741
|
}
|
|
1554
1742
|
}
|
|
1555
1743
|
}
|
|
1744
|
+
function readDispenseBaselines(root) {
|
|
1745
|
+
return {
|
|
1746
|
+
gitRefBefore: (0, git_utils_1.getLatestCommitSha)(root) ?? undefined,
|
|
1747
|
+
treeCleanAtDispense: (0, migrate_commits_1.migrateTreeStatus)(root) === 'clean',
|
|
1748
|
+
depsHashAtDispense: (0, util_1.depsHash)(root),
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1751
|
+
/**
|
|
1752
|
+
* Dispenses a `--run-migration` run's step without a response: the parent
|
|
1753
|
+
* runs the step's first attempt itself, and an agent it starts afterwards
|
|
1754
|
+
* gets the step from the "next" command.
|
|
1755
|
+
*/
|
|
1756
|
+
function dispenseSingleMigrationStep(root, runId) {
|
|
1757
|
+
const baselines = readDispenseBaselines(root);
|
|
1758
|
+
(0, state_lock_1.updateRunState)((0, run_state_1.runDir)(root, runId), (fresh) => {
|
|
1759
|
+
const [step] = fresh.steps;
|
|
1760
|
+
return setDispenseBaselines(applyEventOrThrow(fresh, dispenseEvent(step)), step.id, baselines);
|
|
1761
|
+
});
|
|
1762
|
+
}
|
|
1556
1763
|
function dispenseEvent(step) {
|
|
1557
1764
|
switch (step.kind) {
|
|
1558
1765
|
case 'migration':
|
|
@@ -1631,6 +1838,7 @@ function emitRetryFailed(root, runId, state, step, noProgress) {
|
|
|
1631
1838
|
` current HEAD: ${head ?? '(unknown)'}`,
|
|
1632
1839
|
` working tree: ${tree === null ? '(unknown)' : tree ? `\n${tree}` : '(clean)'}`,
|
|
1633
1840
|
``,
|
|
1841
|
+
...unfinishedOperationLines(root, state, step),
|
|
1634
1842
|
retryBudgetLine(step, committed),
|
|
1635
1843
|
``,
|
|
1636
1844
|
`Decide how to proceed and re-run reconcile with one of:`,
|
|
@@ -1639,13 +1847,17 @@ function emitRetryFailed(root, runId, state, step, noProgress) {
|
|
|
1639
1847
|
: [retryOptionLine(retrySafety, reconcileCommand(root, runId, 'retry'))]),
|
|
1640
1848
|
];
|
|
1641
1849
|
if (cleanRetry && !capReached) {
|
|
1642
|
-
lines.push(` retry-clean: reset the tree to ${step.gitRefBefore ?? 'the pre-migration ref'} (discarding its uncommitted tracked changes and the untracked files git does not ignore, ${
|
|
1643
|
-
}
|
|
1850
|
+
lines.push(` retry-clean: reset the tree to ${step.gitRefBefore ?? 'the pre-migration ref'} (discarding its uncommitted tracked changes and the untracked files git does not ignore, keeping ${KEPT_OWNED_PATHS}) and retry from that clean state by running: ${reconcileCommand(root, runId, 'retry-clean')}`);
|
|
1851
|
+
}
|
|
1852
|
+
// A failed commit is already named above the choices.
|
|
1853
|
+
const adoptWhen = recordedCommitFailed(state, step)
|
|
1854
|
+
? ''
|
|
1855
|
+
: committed
|
|
1856
|
+
? `a commit of this ${(0, state_machine_1.stepNoun)(step)}'s changes was started and never recorded; `
|
|
1857
|
+
: `the ${(0, state_machine_1.stepNoun)(step)} was ${step.kind === 'migration' ? 'applied' : 'done'} by hand; `;
|
|
1644
1858
|
lines.push(landed
|
|
1645
1859
|
? ` adopt: keep the landed commit${landed.sha ? ` ${landed.sha}` : ''} and the current working-tree state as the ${(0, state_machine_1.stepNoun)(step)}'s result, then run: ${reconcileCommand(root, runId, 'adopt')}`
|
|
1646
|
-
: ` adopt: ${
|
|
1647
|
-
? `a commit of this ${(0, state_machine_1.stepNoun)(step)}'s changes was started and never recorded`
|
|
1648
|
-
: `the ${(0, state_machine_1.stepNoun)(step)} was ${step.kind === 'migration' ? 'applied' : 'done'} by hand`}; keep the current working-tree state as its result, then run: ${reconcileCommand(root, runId, 'adopt')}`);
|
|
1860
|
+
: ` adopt: ${adoptWhen}keep the current working-tree state as its result, then run: ${reconcileCommand(root, runId, 'adopt')}`);
|
|
1649
1861
|
// Refused by the state machine: the migration is, or may be, committed.
|
|
1650
1862
|
if (!committed) {
|
|
1651
1863
|
lines.push(` skip: ${reconcileCommand(root, runId, 'skip')}`, unresolvedOptionLine(root, runId, state, step, unresolvedResetsTree(state, step, cleanRetry)));
|
|
@@ -1677,6 +1889,26 @@ function retryBudgetLine(step, committed) {
|
|
|
1677
1889
|
? `adopt only once you have inspected the tree and finished the ${(0, state_machine_1.stepNoun)(step)}; otherwise stop and report`
|
|
1678
1890
|
: 'give the step up with unresolved and continue'}.`;
|
|
1679
1891
|
}
|
|
1892
|
+
// A recorded failed commit with the mark standing withdraws skip and
|
|
1893
|
+
// unresolved, so every choice left commits.
|
|
1894
|
+
function unfinishedOperationLines(root, state, step) {
|
|
1895
|
+
const lines = [];
|
|
1896
|
+
if (step.installFailed === true) {
|
|
1897
|
+
lines.push(`Its dependency install did not complete. A retry runs it again, so fix the cause first if it can be fixed. Any other choice settles the step without installing and leaves the install to you; the run's final report asks for \`${(0, util_1.pmInstallCommand)(root)}\`.`, ``);
|
|
1898
|
+
}
|
|
1899
|
+
if (recordedCommitFailed(state, step)) {
|
|
1900
|
+
lines.push(`Its commit did not complete. Every choice below commits again.`, ``);
|
|
1901
|
+
}
|
|
1902
|
+
return lines;
|
|
1903
|
+
}
|
|
1904
|
+
function recordedCommitFailed(state, step) {
|
|
1905
|
+
// Not commitReceipt, which throws on an index the ledger lacks: a dispense
|
|
1906
|
+
// has to render whatever run.json holds.
|
|
1907
|
+
const receipt = step.commitLedgerIndex === undefined
|
|
1908
|
+
? undefined
|
|
1909
|
+
: state.commits[step.commitLedgerIndex];
|
|
1910
|
+
return step.commitStarted === true && receipt?.kind === 'failed';
|
|
1911
|
+
}
|
|
1680
1912
|
// Opens a capped dispense and is the reason a retry past the cap is refused.
|
|
1681
1913
|
function rearmCapLine(step, committed) {
|
|
1682
1914
|
return `This ${(0, state_machine_1.stepNoun)(step)} has already been retried ${step.attempt - 1} times without completing, and no further retry is accepted. ${committed
|
|
@@ -1734,7 +1966,7 @@ function assessPreMarkerRetry(root, step) {
|
|
|
1734
1966
|
reason: `HEAD is at ${head ?? '(unreadable)'} rather than the ${step.gitRefBefore ?? '(unrecorded)'} this migration started from, so the failed attempt's changes may already be committed and rerunning the generator could apply them twice.`,
|
|
1735
1967
|
};
|
|
1736
1968
|
}
|
|
1737
|
-
if ((0,
|
|
1969
|
+
if ((0, migrate_commits_1.migrateTreeStatus)(root) !== 'clean') {
|
|
1738
1970
|
return {
|
|
1739
1971
|
kind: 'unsafe',
|
|
1740
1972
|
reason: `the working tree is not verifiably clean, and the failed attempt may have written to it before failing; rerunning the generator over those changes could apply them twice.`,
|
|
@@ -1751,10 +1983,12 @@ function lastCoveringLandedEntry(state, step) {
|
|
|
1751
1983
|
// reset, a partial commit, or the tree left as it stands.
|
|
1752
1984
|
function unresolvedOptionLine(root, runId, state, step, resetTree) {
|
|
1753
1985
|
const command = reconcileCommand(root, runId, 'unresolved');
|
|
1754
|
-
const recorded =
|
|
1986
|
+
const recorded = state.singleMigration === true
|
|
1987
|
+
? `The failure is listed in the completion report.`
|
|
1988
|
+
: `The failure is recorded as a run issue and listed in the completion report.`;
|
|
1755
1989
|
const noun = (0, state_machine_1.stepNoun)(step);
|
|
1756
1990
|
if (resetTree) {
|
|
1757
|
-
return ` unresolved: give up on this ${noun}, resetting the tree to ${step.gitRefBefore ?? 'the pre-migration ref'} (discarding what the failed attempt left, ${
|
|
1991
|
+
return ` unresolved: give up on this ${noun}, resetting the tree to ${step.gitRefBefore ?? 'the pre-migration ref'} (discarding what the failed attempt left, keeping ${KEPT_OWNED_PATHS}), then run: ${command}. ${recorded}`;
|
|
1758
1992
|
}
|
|
1759
1993
|
if (state.createCommits) {
|
|
1760
1994
|
return ` unresolved: give up on this ${noun}; its partial changes are committed under its name, marked unresolved, and the run moves on. Then run: ${command}. ${recorded}`;
|
|
@@ -1776,6 +2010,7 @@ function emitDied(root, runId, state, step, noProgress) {
|
|
|
1776
2010
|
` current HEAD: ${head ?? '(unknown)'}`,
|
|
1777
2011
|
` working tree: ${tree === null ? '(unknown)' : tree ? `\n${tree}` : '(clean)'}`,
|
|
1778
2012
|
``,
|
|
2013
|
+
...unfinishedOperationLines(root, state, step),
|
|
1779
2014
|
retryBudgetLine(step, committed),
|
|
1780
2015
|
``,
|
|
1781
2016
|
];
|
|
@@ -1784,7 +2019,7 @@ function emitDied(root, runId, state, step, noProgress) {
|
|
|
1784
2019
|
options.push(` retry: keep everything this migration already produced (its commit, if any, and the current tree) and run only the part that did not complete, then run: ${reconcileCommand(root, runId, 'retry')}`);
|
|
1785
2020
|
}
|
|
1786
2021
|
if (cleanRetry && !capReached) {
|
|
1787
|
-
options.push(` retry-clean: reset the tree to ${ref ?? 'the pre-migration ref'} (discarding its uncommitted tracked changes and the untracked files git does not ignore, ${
|
|
2022
|
+
options.push(` retry-clean: reset the tree to ${ref ?? 'the pre-migration ref'} (discarding its uncommitted tracked changes and the untracked files git does not ignore, keeping ${KEPT_OWNED_PATHS}) and retry from that clean state by running: ${reconcileCommand(root, runId, 'retry-clean')}`);
|
|
1788
2023
|
}
|
|
1789
2024
|
else if (!capReached) {
|
|
1790
2025
|
lines.push(`A clean retry is unavailable: ${(0, clean_retry_1.cleanRetryUnavailableReason)(root, state, step, head)}`);
|
|
@@ -1810,9 +2045,19 @@ function emitDied(root, runId, state, step, noProgress) {
|
|
|
1810
2045
|
}, noProgress);
|
|
1811
2046
|
}
|
|
1812
2047
|
function emitHeld(root, runId, state, step, held, noProgress) {
|
|
2048
|
+
const heldStep = state.steps.find((s) => s.id === held.stepId);
|
|
2049
|
+
const heldStepName = heldStep && (0, state_machine_1.stepLabel)(heldStep);
|
|
1813
2050
|
emit(root, runId, state, step, 'held', {
|
|
1814
2051
|
next: reconcileCommand(root, runId),
|
|
1815
|
-
instructionLines: [
|
|
2052
|
+
instructionLines: [
|
|
2053
|
+
(0, broker_1.treeBusyMessage)(held, heldStepName),
|
|
2054
|
+
// Quitting this session frees the terminal only for its own parent.
|
|
2055
|
+
...(held.owner === process.env[broker_1.BROKER_ENV_VAR]
|
|
2056
|
+
? [
|
|
2057
|
+
`If it seems stuck, the user can ${(0, broker_1.sessionOperationWayOut)((0, broker_1.treeOperationLabel)(held, heldStepName))}.`,
|
|
2058
|
+
]
|
|
2059
|
+
: []),
|
|
2060
|
+
],
|
|
1816
2061
|
}, noProgress);
|
|
1817
2062
|
}
|
|
1818
2063
|
function emitStillRunning(root, runId, state, step, held, noProgress) {
|
|
@@ -1830,7 +2075,7 @@ function emitStillRunning(root, runId, state, step, held, noProgress) {
|
|
|
1830
2075
|
if (ageMs >= HANG_THRESHOLD_MS) {
|
|
1831
2076
|
const hung = `It has been running for ${Math.floor(ageMs / 60000)} minutes and may be hung.`;
|
|
1832
2077
|
lines.push(parentOperation
|
|
1833
|
-
? `${hung} Report this to the user: they can
|
|
2078
|
+
? `${hung} Report this to the user: they can ${(0, broker_1.sessionOperationWayOut)(parentOperation)}.`
|
|
1834
2079
|
: `${hung} Verify pid ${step.pid}; either keep waiting, or kill it so the next reconcile can classify it as died.`);
|
|
1835
2080
|
}
|
|
1836
2081
|
emit(root, runId, state, step, 'still-running', {
|
|
@@ -2004,9 +2249,8 @@ function planPromptPath(dir, state, migrationId) {
|
|
|
2004
2249
|
}
|
|
2005
2250
|
return null;
|
|
2006
2251
|
}
|
|
2007
|
-
//
|
|
2008
|
-
//
|
|
2009
|
-
// refused to consume it, and only this response can tell the agent why.
|
|
2252
|
+
// A readable handoff whose issue report is invalid is a rejection too: the
|
|
2253
|
+
// fold refused to consume it, and only this response can tell the agent why.
|
|
2010
2254
|
function describeRejectedHandoff(dir, state, step) {
|
|
2011
2255
|
const result = readStepHandoff(dir, step.id);
|
|
2012
2256
|
const followUp = 'Rewrite the handoff file, then run the "next" command.';
|
|
@@ -2049,7 +2293,7 @@ function describeRejectedHandoff(dir, state, step) {
|
|
|
2049
2293
|
// to the request, and it carries the reconcile command to run next. A non-zero
|
|
2050
2294
|
// exit would tell the driving agent that reconcile itself crashed, and it
|
|
2051
2295
|
// would stop reading for the correction it is being handed.
|
|
2052
|
-
function emitError(root, runId, reason) {
|
|
2296
|
+
function emitError(root, runId, state, reason) {
|
|
2053
2297
|
// A rejected action is an active response like any dispense, so it carries
|
|
2054
2298
|
// the runbook footer: the master receiving it may have just lost its
|
|
2055
2299
|
// context. One sanitized array feeds both outputs, as in emit().
|
|
@@ -2062,7 +2306,9 @@ function emitError(root, runId, reason) {
|
|
|
2062
2306
|
next: reconcileCommand(root, runId),
|
|
2063
2307
|
instructions: lines.join('\n'),
|
|
2064
2308
|
});
|
|
2065
|
-
(
|
|
2309
|
+
if (reportsOrchestratorAnalytics(state)) {
|
|
2310
|
+
(0, migrate_analytics_1.reportMigrateOrchestratorDispense)({ action: 'error', attempt: 0 });
|
|
2311
|
+
}
|
|
2066
2312
|
}
|
|
2067
2313
|
/**
|
|
2068
2314
|
* What a completed run leaves behind for whoever reads its summary, one line
|
|
@@ -2074,14 +2320,19 @@ function completionWarnings(root, runId, state) {
|
|
|
2074
2320
|
// fact absorbed; suppress the warning only on a verified-clean tree. A dirty
|
|
2075
2321
|
// tree can still be unrelated edits, so the warning only claims the changes
|
|
2076
2322
|
// "may remain".
|
|
2077
|
-
const
|
|
2323
|
+
const debtStepIds = (0, state_machine_1.uncoveredFailedStepIds)(state);
|
|
2324
|
+
const uncommitted = debtStepIds.length > 0 && (0, migrate_commits_1.migrateTreeStatus)(root) !== 'clean'
|
|
2325
|
+
? state.steps.filter((s) => debtStepIds.includes(s.id))
|
|
2326
|
+
: [];
|
|
2078
2327
|
const uninstalled = state.steps.filter((s) => s.installFailed);
|
|
2079
2328
|
const issueLines = (0, issues_1.renderUnresolvedIssueLines)(state, runId);
|
|
2080
2329
|
return [
|
|
2081
|
-
...(
|
|
2330
|
+
...(uncommitted.length > 0
|
|
2082
2331
|
? [
|
|
2083
2332
|
[
|
|
2084
|
-
|
|
2333
|
+
`The changes made by ${uncommitted
|
|
2334
|
+
.map(state_machine_1.stepLabel)
|
|
2335
|
+
.join(', ')} could not be committed and may remain in the working tree; review and commit them manually.`,
|
|
2085
2336
|
],
|
|
2086
2337
|
]
|
|
2087
2338
|
: []),
|
|
@@ -2117,7 +2368,7 @@ function completeRun(root, dir, runId, state) {
|
|
|
2117
2368
|
};
|
|
2118
2369
|
});
|
|
2119
2370
|
}
|
|
2120
|
-
if (shouldEmit) {
|
|
2371
|
+
if (shouldEmit && reportsOrchestratorAnalytics(current)) {
|
|
2121
2372
|
(0, migrate_analytics_1.reportMigrateOrchestratorComplete)((0, state_machine_1.runTallies)(current));
|
|
2122
2373
|
}
|
|
2123
2374
|
const warnings = completionWarnings(root, runId, current);
|
|
@@ -2156,11 +2407,18 @@ function emit(root, runId, state, step, action, payload, noProgress) {
|
|
|
2156
2407
|
...rest,
|
|
2157
2408
|
...(lines ? { instructions: lines.join('\n') } : {}),
|
|
2158
2409
|
});
|
|
2159
|
-
(
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2410
|
+
if (reportsOrchestratorAnalytics(state)) {
|
|
2411
|
+
(0, migrate_analytics_1.reportMigrateOrchestratorDispense)({
|
|
2412
|
+
action: effectiveAction,
|
|
2413
|
+
attempt: step.attempt,
|
|
2414
|
+
ordinal: (0, state_machine_1.runTallies)(state).dispenseCount,
|
|
2415
|
+
});
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
// A `--run-migration` run reports its own events, so only `--run-migrations`
|
|
2419
|
+
// runs report orchestrator events.
|
|
2420
|
+
function reportsOrchestratorAnalytics(state) {
|
|
2421
|
+
return state.singleMigration !== true;
|
|
2164
2422
|
}
|
|
2165
2423
|
function noProgressLines(step, streak) {
|
|
2166
2424
|
let subject;
|
|
@@ -2254,12 +2512,18 @@ function removeHandoff(dir, stepId) {
|
|
|
2254
2512
|
(0, fs_1.rmSync)((0, handoff_1.runStepHandoffPath)(dir, stepId), { force: true });
|
|
2255
2513
|
}
|
|
2256
2514
|
}
|
|
2515
|
+
const KEPT_OWNED_PATHS = `${migrate_commits_1.MIGRATE_OWNED_PATHS.slice(0, -1).join(', ')} and ${migrate_commits_1.MIGRATE_OWNED_PATHS[migrate_commits_1.MIGRATE_OWNED_PATHS.length - 1]}`;
|
|
2257
2516
|
// null means the probe itself failed; the death dispense renders that as
|
|
2258
2517
|
// '(unknown)', because '(clean)' would invite a retry-clean reset over
|
|
2259
2518
|
// evidence that was never gathered (getWorkingTreeStatus's contract).
|
|
2260
2519
|
function dirtyTreeSummary(root) {
|
|
2261
2520
|
try {
|
|
2262
|
-
return (0, child_process_1.
|
|
2521
|
+
return (0, child_process_1.execFileSync)('git', [
|
|
2522
|
+
'status',
|
|
2523
|
+
'--porcelain',
|
|
2524
|
+
'--',
|
|
2525
|
+
...(0, migrate_commits_1.migrateCommitExcludes)(root).map((path) => `:(exclude)${path}`),
|
|
2526
|
+
], {
|
|
2263
2527
|
encoding: 'utf8',
|
|
2264
2528
|
cwd: root,
|
|
2265
2529
|
stdio: ['ignore', 'pipe', 'pipe'],
|