nx 23.3.0-beta.2 → 23.3.0-beta.4
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/dist/bin/init-local.js +7 -1
- package/dist/src/command-line/add/add.js +5 -1
- package/dist/src/command-line/init/implementation/utils.js +6 -1
- package/dist/src/command-line/migrate/agentic/capture-generator-output.d.ts +25 -0
- package/dist/src/command-line/migrate/agentic/capture-generator-output.js +121 -5
- package/dist/src/command-line/migrate/agentic/close-agent-session.d.ts +21 -0
- package/dist/src/command-line/migrate/agentic/close-agent-session.js +126 -0
- package/dist/src/command-line/migrate/agentic/definitions.d.ts +24 -0
- package/dist/src/command-line/migrate/agentic/definitions.js +5 -2
- package/dist/src/command-line/migrate/agentic/handoff.d.ts +23 -0
- package/dist/src/command-line/migrate/agentic/handoff.js +62 -3
- package/dist/src/command-line/migrate/agentic/master/invocations.d.ts +12 -0
- package/dist/src/command-line/migrate/agentic/master/invocations.js +54 -0
- package/dist/src/command-line/migrate/agentic/master/run-master-session.d.ts +12 -0
- package/dist/src/command-line/migrate/agentic/master/run-master-session.js +99 -0
- package/dist/src/command-line/migrate/agentic/master/spawn-master.d.ts +31 -0
- package/dist/src/command-line/migrate/agentic/master/spawn-master.js +217 -0
- package/dist/src/command-line/migrate/agentic/prompts/fragments.js +3 -0
- package/dist/src/command-line/migrate/agentic/runner.d.ts +0 -21
- package/dist/src/command-line/migrate/agentic/runner.js +12 -249
- package/dist/src/command-line/migrate/agentic/terminal-repair.d.ts +1 -0
- package/dist/src/command-line/migrate/agentic/terminal-repair.js +27 -0
- package/dist/src/command-line/migrate/agentic/windows-cmd.d.ts +33 -0
- package/dist/src/command-line/migrate/agentic/windows-cmd.js +88 -0
- package/dist/src/command-line/migrate/deferred-output.d.ts +44 -0
- package/dist/src/command-line/migrate/deferred-output.js +92 -0
- package/dist/src/command-line/migrate/execute-migration.d.ts +9 -3
- package/dist/src/command-line/migrate/execute-migration.js +53 -16
- package/dist/src/command-line/migrate/migrate-commits.d.ts +4 -4
- package/dist/src/command-line/migrate/migrate-commits.js +7 -6
- package/dist/src/command-line/migrate/migrate.js +28 -13
- package/dist/src/command-line/migrate/run/broker.d.ts +138 -0
- package/dist/src/command-line/migrate/run/broker.js +507 -0
- package/dist/src/command-line/migrate/run/clean-retry.d.ts +12 -0
- package/dist/src/command-line/migrate/run/clean-retry.js +98 -0
- package/dist/src/command-line/migrate/run/index.d.ts +4 -3
- package/dist/src/command-line/migrate/run/index.js +7 -1
- package/dist/src/command-line/migrate/run/issues.d.ts +8 -1
- package/dist/src/command-line/migrate/run/issues.js +40 -6
- package/dist/src/command-line/migrate/run/orchestrator.d.ts +18 -1
- package/dist/src/command-line/migrate/run/orchestrator.js +409 -377
- package/dist/src/command-line/migrate/run/run-state.d.ts +16 -0
- package/dist/src/command-line/migrate/run/run-state.js +33 -2
- package/dist/src/command-line/migrate/run/runbook.js +8 -4
- package/dist/src/command-line/migrate/run/state-machine.d.ts +24 -1
- package/dist/src/command-line/migrate/run/state-machine.js +112 -21
- package/dist/src/command-line/migrate/run/util.d.ts +2 -1
- package/dist/src/command-line/migrate/run/util.js +3 -3
- package/dist/src/command-line/migrate/run/worker.js +170 -164
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/daemon/server/server.js +11 -8
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/tasks-runner/run-command.js +3 -0
- package/dist/src/utils/git-utils.d.ts +12 -0
- package/dist/src/utils/git-utils.js +76 -30
- package/dist/src/utils/package-json.js +8 -0
- package/package.json +11 -11
|
@@ -22,6 +22,7 @@ const run_id_1 = require("./run-id");
|
|
|
22
22
|
const state_machine_1 = require("./state-machine");
|
|
23
23
|
const state_lock_1 = require("./state-lock");
|
|
24
24
|
const util_1 = require("./util");
|
|
25
|
+
const broker_1 = require("./broker");
|
|
25
26
|
const text_1 = require("../text");
|
|
26
27
|
const agent_output_1 = require("./agent-output");
|
|
27
28
|
const agent_work_payload_1 = require("./agent-work-payload");
|
|
@@ -98,10 +99,9 @@ async function runSingleMigrationWorker(input) {
|
|
|
98
99
|
function readMigrationsSource(root, runId) {
|
|
99
100
|
if (runId) {
|
|
100
101
|
const dir = (0, run_state_1.runDir)(root, runId);
|
|
101
|
-
// A missing run.json would surface a raw ENOENT from readRunState
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
// and `--run-migrations` would run the whole plan in process instead.
|
|
102
|
+
// A missing run.json would surface a raw ENOENT from readRunState; report
|
|
103
|
+
// it the way the orchestrator does instead, with no remediation: starting
|
|
104
|
+
// a run is a gated entry point of its own.
|
|
105
105
|
if (!(0, run_state_1.hasRunState)(dir)) {
|
|
106
106
|
throw new Error(`No migrate run '${runId}' was found under ${types_1.MIGRATE_RUNS_RELATIVE_DIR}.`);
|
|
107
107
|
}
|
|
@@ -358,177 +358,180 @@ async function runRecorded(root, runId, migration, skipInstall, isVerbose) {
|
|
|
358
358
|
// invocation may have claimed a later attempt whose flag `step` predates.
|
|
359
359
|
const startedStep = state.steps.find((s) => s.id === step.id);
|
|
360
360
|
const generatorAlreadyCompleted = startedStep.generatorCompleted === true;
|
|
361
|
-
//
|
|
362
|
-
//
|
|
363
|
-
//
|
|
361
|
+
// Dispensed worker commands never carry the user's flags, so the run records
|
|
362
|
+
// its install policy. An explicit --skip-install here applies only to the
|
|
363
|
+
// installs this process runs itself; a parent session answers from its own.
|
|
364
364
|
const effectiveSkipInstall = state.skipInstall === true || skipInstall;
|
|
365
365
|
// The run records the resolved validation policy at init; absent (a state
|
|
366
366
|
// predating the field) falls back to the same default init applies.
|
|
367
367
|
const shouldValidate = state.validate !== false;
|
|
368
|
+
// The tree reservation an in-process install or commit takes is held until
|
|
369
|
+
// the transition that records this attempt's outcome is written.
|
|
370
|
+
const scope = {};
|
|
368
371
|
// Called before a retry hands the step's work back: the prompt or validation
|
|
369
372
|
// may need the dependencies the earlier attempt's generator added.
|
|
370
|
-
const reinstallFromBaseline = () => recordingInstallFailure(dir, step.id, () => (0, util_1.installDepsChangedSinceDispense)(root, dir, startedStep, effectiveSkipInstall, `${(0, execute_migration_1.formatSingleMigrationRerunCommand)(migrationId)} --run-id=${runId}`));
|
|
373
|
+
const reinstallFromBaseline = () => (0, broker_1.installStepTree)(dir, startedStep, 'install', () => recordingInstallFailure(dir, step.id, () => (0, util_1.installDepsChangedSinceDispense)(root, dir, startedStep, effectiveSkipInstall, `${(0, execute_migration_1.formatSingleMigrationRerunCommand)(migrationId)} --run-id=${runId}`)), scope);
|
|
371
374
|
let outcome;
|
|
372
375
|
let awaitingKind;
|
|
373
376
|
const payloadPath = (0, agent_work_payload_1.agentWorkPayloadPath)(dir, step.id, startedStep.attempt);
|
|
374
377
|
try {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
persistPath: payloadPath,
|
|
378
|
-
});
|
|
379
|
-
awaitingKind = 'migration-prompt';
|
|
380
|
-
}
|
|
381
|
-
else if (generatorAlreadyCompleted &&
|
|
382
|
-
startedStep.agenticWaived !== true &&
|
|
383
|
-
(0, migration_shape_1.isHybridMigration)(migration)) {
|
|
384
|
-
await reinstallFromBaseline();
|
|
385
|
-
const carried = (0, agent_work_payload_1.latestStoredAgentWorkPayload)(dir, step.id, startedStep.attempt, startedStep.generatorCompletedAtAttempt, {
|
|
386
|
-
migrationId,
|
|
387
|
-
kind: 'migration-prompt',
|
|
388
|
-
promptPath: migration.prompt,
|
|
389
|
-
});
|
|
390
|
-
if (carried) {
|
|
391
|
-
reemitCarriedAgentWork(migrationId, 'migration-prompt', payloadPath, carried);
|
|
392
|
-
}
|
|
393
|
-
else {
|
|
378
|
+
try {
|
|
379
|
+
if ((0, migration_shape_1.isPromptOnlyMigration)(migration)) {
|
|
394
380
|
emitOrPrintPrompt(root, migration, agenticKind, {
|
|
395
381
|
persistPath: payloadPath,
|
|
396
382
|
});
|
|
383
|
+
awaitingKind = 'migration-prompt';
|
|
397
384
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
385
|
+
else if (generatorAlreadyCompleted &&
|
|
386
|
+
startedStep.agenticWaived !== true &&
|
|
387
|
+
(0, migration_shape_1.isHybridMigration)(migration)) {
|
|
388
|
+
await reinstallFromBaseline();
|
|
389
|
+
const carried = (0, agent_work_payload_1.latestStoredAgentWorkPayload)(dir, step.id, startedStep.attempt, startedStep.generatorCompletedAtAttempt, {
|
|
390
|
+
migrationId,
|
|
391
|
+
kind: 'migration-prompt',
|
|
392
|
+
promptPath: migration.prompt,
|
|
393
|
+
});
|
|
394
|
+
if (carried) {
|
|
395
|
+
reemitCarriedAgentWork(migrationId, 'migration-prompt', payloadPath, carried);
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
emitOrPrintPrompt(root, migration, agenticKind, {
|
|
399
|
+
persistPath: payloadPath,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
awaitingKind = 'migration-prompt';
|
|
403
|
+
}
|
|
404
|
+
else if (generatorAlreadyCompleted &&
|
|
405
|
+
startedStep.validationOwed === true) {
|
|
406
|
+
// The flag is the only record that these changes still owe a validation
|
|
407
|
+
// pass; the decision needed the generator result, which is gone with the
|
|
408
|
+
// attempt that made it. The commit stays with the fold.
|
|
409
|
+
await reinstallFromBaseline();
|
|
410
|
+
const carried = (0, agent_work_payload_1.latestStoredAgentWorkPayload)(dir, step.id, startedStep.attempt, startedStep.generatorCompletedAtAttempt, { migrationId, kind: 'generator-validation' });
|
|
411
|
+
if (carried) {
|
|
412
|
+
reemitCarriedAgentWork(migrationId, 'generator-validation', payloadPath, carried);
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
emitValidationBlock(root, migration, undefined, {
|
|
416
|
+
persistPath: payloadPath,
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
awaitingKind = 'generator-validation';
|
|
420
|
+
}
|
|
421
|
+
else if (generatorAlreadyCompleted) {
|
|
422
|
+
state = await finishCompletedGenerator(dir, root, state, startedStep, migration, effectiveSkipInstall, runId, scope);
|
|
423
|
+
outcome = buildOutcome([], [], 'The generator ran in an earlier attempt; this attempt completed its install and commit.', root);
|
|
410
424
|
}
|
|
411
425
|
else {
|
|
412
|
-
|
|
413
|
-
|
|
426
|
+
const installer = new execute_migration_1.ChangedDepInstaller(root, effectiveSkipInstall, `${(0, execute_migration_1.formatSingleMigrationRerunCommand)(migrationId)} --run-id=${runId}`);
|
|
427
|
+
// Read once; the run and the hybrid documentation resolution share it.
|
|
428
|
+
const resolvedCollection = (0, execute_migration_1.readMigrationCollection)(migration.package, root);
|
|
429
|
+
const { changes, nextSteps, agentContext, skipAgentic, logs, madeChanges, } = await (0, execute_migration_1.runNxOrAngularMigration)(root, migration, isVerbose,
|
|
430
|
+
// Captured whenever an agent step may consume it: a hybrid's prompt
|
|
431
|
+
// payload, or the validation pass over the generator's changes.
|
|
432
|
+
(0, migration_shape_1.isHybridMigration)(migration) || shouldValidate, resolvedCollection);
|
|
433
|
+
// Mirrors the classic loop's waiver semantics (migrate.ts): the two must
|
|
434
|
+
// agree on when an AI step was owed for skipAgentic to waive.
|
|
435
|
+
const validationApplies = shouldValidate && !(0, migration_shape_1.isHybridMigration)(migration) && changes.length > 0;
|
|
436
|
+
const waivedAgenticStep = skipAgentic && ((0, migration_shape_1.isHybridMigration)(migration) || validationApplies);
|
|
437
|
+
const validationOwed = validationApplies && !waivedAgenticStep;
|
|
438
|
+
const promptOwed = (0, migration_shape_1.isHybridMigration)(migration) && !waivedAgenticStep;
|
|
439
|
+
// Recorded before the commit is attempted: from here on the changes are
|
|
440
|
+
// in the tree, so a failed install or commit must leave a retry with
|
|
441
|
+
// only those left to do rather than running the generator again.
|
|
442
|
+
state = transition(dir, {
|
|
443
|
+
type: 'markGeneratorCompleted',
|
|
444
|
+
stepId: step.id,
|
|
445
|
+
agenticWaived: waivedAgenticStep,
|
|
446
|
+
validationOwed,
|
|
447
|
+
madeChanges,
|
|
414
448
|
});
|
|
449
|
+
if (waivedAgenticStep) {
|
|
450
|
+
(0, migrate_output_1.logWaivedAgenticStep)(migration, agentContext);
|
|
451
|
+
}
|
|
452
|
+
else if (!(0, migration_shape_1.isHybridMigration)(migration) && !validationApplies) {
|
|
453
|
+
forwardDroppedAgentContext(migration, agentContext, agenticKind);
|
|
454
|
+
}
|
|
455
|
+
// Stored before the install: a failed install fails the attempt, and
|
|
456
|
+
// the retry re-hands this payload instead of a context-free one.
|
|
457
|
+
const impl = { logs, changes, agentContext };
|
|
458
|
+
const owed = validationOwed
|
|
459
|
+
? validationPayload(root, migration, impl, resolvedCollection)
|
|
460
|
+
: promptOwed
|
|
461
|
+
? promptPayload(root, migration, impl, resolvedCollection)
|
|
462
|
+
: undefined;
|
|
463
|
+
if (owed) {
|
|
464
|
+
(0, agent_work_payload_1.persistAgentWorkPayload)(payloadPath, owed);
|
|
465
|
+
}
|
|
466
|
+
const install = () => recordingInstallFailure(dir, step.id, () => installer.installDepsIfChanged());
|
|
467
|
+
// A no-op step's commit would absorb prior pending diffs under its name;
|
|
468
|
+
// a step handing work back defers its commit to the fold, so the
|
|
469
|
+
// migration still lands as one commit. The install runs either way: the
|
|
470
|
+
// agent may run tasks that need what the generator added.
|
|
471
|
+
if (state.createCommits &&
|
|
472
|
+
madeChanges &&
|
|
473
|
+
!validationOwed &&
|
|
474
|
+
!promptOwed) {
|
|
475
|
+
state = await commitStepChanges(dir, root, state, startedStep, migration, install, scope);
|
|
476
|
+
}
|
|
477
|
+
else {
|
|
478
|
+
await (0, broker_1.installStepTree)(dir, startedStep, 'install', install, scope);
|
|
479
|
+
}
|
|
480
|
+
if (installer.skippedInstall) {
|
|
481
|
+
(0, execute_migration_1.logSkippedPostMigrationInstall)(root);
|
|
482
|
+
}
|
|
483
|
+
else if (installer.installed) {
|
|
484
|
+
(0, util_1.recordInstallLanded)(root, dir, step.id);
|
|
485
|
+
}
|
|
486
|
+
printNextSteps(migration, nextSteps);
|
|
487
|
+
if (validationOwed) {
|
|
488
|
+
emitValidationPayload(migrationId, owed);
|
|
489
|
+
awaitingKind = 'generator-validation';
|
|
490
|
+
}
|
|
491
|
+
else if (promptOwed) {
|
|
492
|
+
emitPromptForOuterAgent(migrationId, owed, true);
|
|
493
|
+
awaitingKind = 'migration-prompt';
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
outcome = buildOutcome(changes, nextSteps, migration.description, root);
|
|
497
|
+
}
|
|
415
498
|
}
|
|
416
|
-
awaitingKind = 'generator-validation';
|
|
417
499
|
}
|
|
418
|
-
|
|
419
|
-
//
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
500
|
+
catch (e) {
|
|
501
|
+
// Another attempt owns the step, and `fail` is not attempt-bound.
|
|
502
|
+
if (e instanceof broker_1.BrokerStaleRequestError)
|
|
503
|
+
throw e;
|
|
504
|
+
// The failed-step dispense surfaces this so the agent can decide
|
|
505
|
+
// retry-vs-skip; carry the error's first line, not a full stack.
|
|
506
|
+
transition(dir, {
|
|
507
|
+
type: 'fail',
|
|
508
|
+
stepId: step.id,
|
|
509
|
+
finishedAt: (0, util_1.nowIso)(),
|
|
510
|
+
outcome: { summary: (0, util_1.summarizeError)(e) },
|
|
511
|
+
});
|
|
512
|
+
throw e;
|
|
423
513
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
// Captured whenever an agent step may consume it: a hybrid's prompt
|
|
430
|
-
// payload, or the validation pass over the generator's changes.
|
|
431
|
-
(0, migration_shape_1.isHybridMigration)(migration) || shouldValidate, resolvedCollection);
|
|
432
|
-
// Mirrors the classic loop's waiver semantics (migrate.ts): the two must
|
|
433
|
-
// agree on when an AI step was owed for skipAgentic to waive.
|
|
434
|
-
const validationApplies = shouldValidate && !(0, migration_shape_1.isHybridMigration)(migration) && changes.length > 0;
|
|
435
|
-
const waivedAgenticStep = skipAgentic && ((0, migration_shape_1.isHybridMigration)(migration) || validationApplies);
|
|
436
|
-
const validationOwed = validationApplies && !waivedAgenticStep;
|
|
437
|
-
const promptOwed = (0, migration_shape_1.isHybridMigration)(migration) && !waivedAgenticStep;
|
|
438
|
-
// Recorded before the commit is attempted: from here on the changes are
|
|
439
|
-
// in the tree, so a failed install or commit must leave a retry with
|
|
440
|
-
// only those left to do rather than running the generator again. The
|
|
441
|
-
// waiver and the owed validation ride along so that retry re-emits
|
|
442
|
-
// exactly the agent work this attempt decided was owed.
|
|
443
|
-
state = transition(dir, {
|
|
444
|
-
type: 'markGeneratorCompleted',
|
|
514
|
+
// Handed-back work is applied by a separate actor, so parking exits
|
|
515
|
+
// successfully rather than failing the step.
|
|
516
|
+
if (awaitingKind !== undefined) {
|
|
517
|
+
transition(dir, {
|
|
518
|
+
type: 'awaitPromptOutcome',
|
|
445
519
|
stepId: step.id,
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
madeChanges,
|
|
520
|
+
finishedAt: (0, util_1.nowIso)(),
|
|
521
|
+
awaitingKind,
|
|
449
522
|
});
|
|
450
|
-
|
|
451
|
-
(0, migrate_output_1.logWaivedAgenticStep)(migration, agentContext);
|
|
452
|
-
}
|
|
453
|
-
else if (!(0, migration_shape_1.isHybridMigration)(migration) && !validationApplies) {
|
|
454
|
-
forwardDroppedAgentContext(migration, agentContext, agenticKind);
|
|
455
|
-
}
|
|
456
|
-
// Stored before the install: a failed install fails the attempt, and
|
|
457
|
-
// the retry re-hands this payload instead of a context-free one.
|
|
458
|
-
const impl = { logs, changes, agentContext };
|
|
459
|
-
const owed = validationOwed
|
|
460
|
-
? validationPayload(root, migration, impl, resolvedCollection)
|
|
461
|
-
: promptOwed
|
|
462
|
-
? promptPayload(root, migration, impl, resolvedCollection)
|
|
463
|
-
: undefined;
|
|
464
|
-
if (owed) {
|
|
465
|
-
(0, agent_work_payload_1.persistAgentWorkPayload)(payloadPath, owed);
|
|
466
|
-
}
|
|
467
|
-
const install = () => recordingInstallFailure(dir, step.id, () => installer.installDepsIfChanged());
|
|
468
|
-
// Commits follow the run config, not CLI flags, and only when the
|
|
469
|
-
// generator changed something: a no-op step's commit would absorb prior
|
|
470
|
-
// pending diffs under its name. A step handing work back defers its
|
|
471
|
-
// commit to the fold, so the migration lands as one commit and a failed
|
|
472
|
-
// hand-back leaves the changes uncommitted for review, as in the classic
|
|
473
|
-
// loop. The install still runs first: the agent may run tasks that need
|
|
474
|
-
// what the generator added.
|
|
475
|
-
if (state.createCommits &&
|
|
476
|
-
madeChanges &&
|
|
477
|
-
!validationOwed &&
|
|
478
|
-
!promptOwed) {
|
|
479
|
-
state = await commitStepChanges(dir, root, state, step, migration, install);
|
|
480
|
-
}
|
|
481
|
-
else {
|
|
482
|
-
await install();
|
|
483
|
-
}
|
|
484
|
-
if (installer.skippedInstall) {
|
|
485
|
-
(0, execute_migration_1.logSkippedPostMigrationInstall)(root);
|
|
486
|
-
}
|
|
487
|
-
else if (installer.installed) {
|
|
488
|
-
(0, util_1.recordInstallLanded)(root, dir, step.id);
|
|
489
|
-
}
|
|
490
|
-
printNextSteps(migration, nextSteps);
|
|
491
|
-
if (validationOwed) {
|
|
492
|
-
emitValidationPayload(migrationId, owed);
|
|
493
|
-
awaitingKind = 'generator-validation';
|
|
494
|
-
}
|
|
495
|
-
else if (promptOwed) {
|
|
496
|
-
emitPromptForOuterAgent(migrationId, owed, true);
|
|
497
|
-
awaitingKind = 'migration-prompt';
|
|
498
|
-
}
|
|
499
|
-
else {
|
|
500
|
-
outcome = buildOutcome(changes, nextSteps, migration.description, root);
|
|
501
|
-
}
|
|
523
|
+
return;
|
|
502
524
|
}
|
|
503
|
-
}
|
|
504
|
-
catch (e) {
|
|
505
|
-
// The failed-step dispense surfaces this so the agent can decide
|
|
506
|
-
// retry-vs-skip; carry the error's first line, not a full stack.
|
|
507
525
|
transition(dir, {
|
|
508
|
-
type: '
|
|
526
|
+
type: 'succeed',
|
|
509
527
|
stepId: step.id,
|
|
510
528
|
finishedAt: (0, util_1.nowIso)(),
|
|
511
|
-
outcome
|
|
529
|
+
...(outcome ? { outcome } : {}),
|
|
512
530
|
});
|
|
513
|
-
throw e;
|
|
514
531
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
if (awaitingKind !== undefined) {
|
|
518
|
-
transition(dir, {
|
|
519
|
-
type: 'awaitPromptOutcome',
|
|
520
|
-
stepId: step.id,
|
|
521
|
-
finishedAt: (0, util_1.nowIso)(),
|
|
522
|
-
awaitingKind,
|
|
523
|
-
});
|
|
524
|
-
return;
|
|
532
|
+
finally {
|
|
533
|
+
scope.lease?.release();
|
|
525
534
|
}
|
|
526
|
-
transition(dir, {
|
|
527
|
-
type: 'succeed',
|
|
528
|
-
stepId: step.id,
|
|
529
|
-
finishedAt: (0, util_1.nowIso)(),
|
|
530
|
-
...(outcome ? { outcome } : {}),
|
|
531
|
-
});
|
|
532
535
|
}
|
|
533
536
|
// Applies a step event to the freshest on-disk state under the lock, writes it,
|
|
534
537
|
// and returns it. Reading fresh is what makes a racing worker's 'start' see the
|
|
@@ -561,49 +564,52 @@ async function recordingInstallFailure(dir, stepId, install) {
|
|
|
561
564
|
// on the install or the commit. The generator's changes are already in the
|
|
562
565
|
// tree, so only those two are left, and the install compares against the
|
|
563
566
|
// step's persisted baseline rather than against what that generator wrote.
|
|
564
|
-
async function finishCompletedGenerator(dir, root, state, step, migration, skipInstall, runId) {
|
|
567
|
+
async function finishCompletedGenerator(dir, root, state, step, migration, skipInstall, runId, scope) {
|
|
565
568
|
const migrationId = `${migration.package}:${migration.name}`;
|
|
566
569
|
const installDeps = () => recordingInstallFailure(dir, step.id, () => (0, util_1.installDepsChangedSinceDispense)(root, dir, step, skipInstall, `${(0, execute_migration_1.formatSingleMigrationRerunCommand)(migrationId)} --run-id=${runId}`));
|
|
567
570
|
// Same no-op guard as the first attempt. Only an explicit false skips the
|
|
568
571
|
// commit: absent means an older nx wrote the marker without recording the
|
|
569
572
|
// answer, and the commit is kept as that version's retries did.
|
|
570
573
|
if (!state.createCommits || step.generatorMadeChanges === false) {
|
|
571
|
-
await
|
|
574
|
+
await (0, broker_1.installStepTree)(dir, step, 'install', installDeps, scope);
|
|
572
575
|
return state;
|
|
573
576
|
}
|
|
574
|
-
return commitStepChanges(dir, root, state, step, migration, installDeps);
|
|
577
|
+
return commitStepChanges(dir, root, state, step, migration, installDeps, scope);
|
|
575
578
|
}
|
|
576
579
|
// Installs what the step changed, commits it, and records the result in the
|
|
577
580
|
// ledger, shared by a step's first attempt and by one that only has the commit
|
|
578
581
|
// left to do. The absorbed step ids are computed before the commit so the
|
|
579
582
|
// ledger entry and the commit body name the same ones.
|
|
580
|
-
async function commitStepChanges(dir, root, state, step, migration, installDeps) {
|
|
583
|
+
async function commitStepChanges(dir, root, state, step, migration, installDeps, scope) {
|
|
581
584
|
const absorbedStepIds = (0, state_machine_1.uncoveredFailedStepIds)(state).filter((id) => id !== step.id);
|
|
582
|
-
let
|
|
585
|
+
let commit;
|
|
583
586
|
try {
|
|
584
|
-
|
|
587
|
+
commit = await (0, broker_1.commitStepTree)(dir, step, absorbedStepIds, () => (0, migrate_commits_1.commitMigrationIfRequested)(root, migration, true, state.commitPrefix, installDeps, (0, state_machine_1.stepsToPendingMigrations)(state, absorbedStepIds)), scope);
|
|
585
588
|
}
|
|
586
589
|
catch (commitError) {
|
|
587
|
-
//
|
|
588
|
-
|
|
589
|
-
|
|
590
|
+
// Nothing ran for a stale or refused request, so there is no debt.
|
|
591
|
+
if (commitError instanceof broker_1.BrokerStaleRequestError ||
|
|
592
|
+
commitError instanceof broker_1.TreeBusyError) {
|
|
593
|
+
throw commitError;
|
|
594
|
+
}
|
|
595
|
+
// The install failed, or the session ended unanswered and the commit may
|
|
596
|
+
// or may not have landed; record the debt until a landed entry covers it.
|
|
597
|
+
recordCommitEntry(dir, { kind: 'failed', stepIds: [step.id] });
|
|
590
598
|
throw commitError;
|
|
591
599
|
}
|
|
592
|
-
if (result.status === 'failed') {
|
|
600
|
+
if (commit.result.status === 'failed') {
|
|
593
601
|
(0, util_1.warnCommitFailed)(migration.name);
|
|
594
602
|
}
|
|
595
|
-
const entry = (0, state_machine_1.commitResultToLedgerEntry)(result, step.id, absorbedStepIds);
|
|
596
|
-
|
|
603
|
+
const entry = (0, state_machine_1.commitResultToLedgerEntry)(commit.result, step.id, commit.absorbedStepIds);
|
|
604
|
+
// A session's parent records what it commits itself, receipt included.
|
|
605
|
+
return entry && !commit.recorded ? recordCommitEntry(dir, entry) : state;
|
|
597
606
|
}
|
|
598
607
|
// The git commit itself already ran outside the lock; only this pure append is
|
|
599
608
|
// locked. A landed entry carries the resolved issues of every step it names,
|
|
600
609
|
// same as the orchestrator's fold and adopt appends: an absorbed step's
|
|
601
610
|
// resolutions would otherwise land unattached.
|
|
602
|
-
function
|
|
603
|
-
return (0, state_lock_1.updateRunState)(dir, (fresh) => (
|
|
604
|
-
...fresh,
|
|
605
|
-
commits: [...fresh.commits, (0, issues_1.attachIssueIdsToCommitEntry)(fresh, entry)],
|
|
606
|
-
}));
|
|
611
|
+
function recordCommitEntry(dir, entry) {
|
|
612
|
+
return (0, state_lock_1.updateRunState)(dir, (fresh) => (0, state_machine_1.appendCommit)(fresh, (0, issues_1.attachIssueIdsToCommitEntry)(fresh, entry)));
|
|
607
613
|
}
|
|
608
614
|
function buildOutcome(changes, nextSteps, description, root) {
|
|
609
615
|
const outcome = {};
|
|
@@ -668,7 +674,7 @@ async function runAgenticStep(agenticRun, input) {
|
|
|
668
674
|
// commit's diff, so it passes its own guidance instead of the default.
|
|
669
675
|
// `commitMigrationIfRequested` logs a failed commit itself with that guidance.
|
|
670
676
|
function attemptStandaloneCommit(root, migration, createCommits, commitPrefix, installDepsIfChanged) {
|
|
671
|
-
return (0, migrate_commits_1.commitMigrationIfRequested)(root, migration, createCommits, commitPrefix, installDepsIfChanged, [], 'Commit or revert
|
|
677
|
+
return (0, migrate_commits_1.commitMigrationIfRequested)(root, migration, createCommits, commitPrefix, installDepsIfChanged, [], 'Commit or revert any remaining uncommitted changes manually.');
|
|
672
678
|
}
|
|
673
679
|
async function commitAndLogAgenticOutcome(args) {
|
|
674
680
|
const commit = await attemptStandaloneCommit(args.root, args.migration, args.createCommits, args.commitPrefix, args.installDepsIfChanged);
|