@lumenflow/cli 2.18.3 → 2.20.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 +44 -42
- package/dist/agent-session.js +1 -1
- package/dist/agent-session.js.map +1 -1
- package/dist/commands/integrate.js +1 -0
- package/dist/commands/integrate.js.map +1 -1
- package/dist/commands.js +1 -0
- package/dist/commands.js.map +1 -1
- package/dist/delegation-list.js +140 -0
- package/dist/delegation-list.js.map +1 -0
- package/dist/docs-sync.js +1 -0
- package/dist/docs-sync.js.map +1 -1
- package/dist/doctor.js +36 -99
- package/dist/doctor.js.map +1 -1
- package/dist/gates-plan-resolvers.js +150 -0
- package/dist/gates-plan-resolvers.js.map +1 -0
- package/dist/gates-runners.js +533 -0
- package/dist/gates-runners.js.map +1 -0
- package/dist/gates-types.js +3 -0
- package/dist/gates-types.js.map +1 -1
- package/dist/gates-utils.js +316 -0
- package/dist/gates-utils.js.map +1 -0
- package/dist/gates.js +44 -1016
- package/dist/gates.js.map +1 -1
- package/dist/hooks/enforcement-generator.js +16 -880
- package/dist/hooks/enforcement-generator.js.map +1 -1
- package/dist/hooks/enforcement-sync.js +6 -5
- package/dist/hooks/enforcement-sync.js.map +1 -1
- package/dist/hooks/generators/auto-checkpoint.js +123 -0
- package/dist/hooks/generators/auto-checkpoint.js.map +1 -0
- package/dist/hooks/generators/enforce-worktree.js +188 -0
- package/dist/hooks/generators/enforce-worktree.js.map +1 -0
- package/dist/hooks/generators/index.js +16 -0
- package/dist/hooks/generators/index.js.map +1 -0
- package/dist/hooks/generators/pre-compact-checkpoint.js +134 -0
- package/dist/hooks/generators/pre-compact-checkpoint.js.map +1 -0
- package/dist/hooks/generators/require-wu.js +115 -0
- package/dist/hooks/generators/require-wu.js.map +1 -0
- package/dist/hooks/generators/session-start-recovery.js +101 -0
- package/dist/hooks/generators/session-start-recovery.js.map +1 -0
- package/dist/hooks/generators/signal-utils.js +52 -0
- package/dist/hooks/generators/signal-utils.js.map +1 -0
- package/dist/hooks/generators/warn-incomplete.js +65 -0
- package/dist/hooks/generators/warn-incomplete.js.map +1 -0
- package/dist/init-detection.js +228 -0
- package/dist/init-detection.js.map +1 -0
- package/dist/init-scaffolding.js +146 -0
- package/dist/init-scaffolding.js.map +1 -0
- package/dist/init-templates.js +1928 -0
- package/dist/init-templates.js.map +1 -0
- package/dist/init.js +137 -2425
- package/dist/init.js.map +1 -1
- package/dist/initiative-edit.js +42 -11
- package/dist/initiative-edit.js.map +1 -1
- package/dist/initiative-remove-wu.js +0 -0
- package/dist/initiative-status.js +29 -2
- package/dist/initiative-status.js.map +1 -1
- package/dist/mem-context.js +22 -9
- package/dist/mem-context.js.map +1 -1
- package/dist/orchestrate-init-status.js +32 -1
- package/dist/orchestrate-init-status.js.map +1 -1
- package/dist/orchestrate-initiative.js +2 -2
- package/dist/orchestrate-initiative.js.map +1 -1
- package/dist/orchestrate-monitor.js +38 -38
- package/dist/orchestrate-monitor.js.map +1 -1
- package/dist/plan-link.js +7 -14
- package/dist/plan-link.js.map +1 -1
- package/dist/public-manifest.js +19 -5
- package/dist/public-manifest.js.map +1 -1
- package/dist/shared-validators.js +1 -0
- package/dist/shared-validators.js.map +1 -1
- package/dist/spawn-list.js +0 -0
- package/dist/sync-templates.js +2 -1
- package/dist/sync-templates.js.map +1 -1
- package/dist/wu-claim-branch.js +121 -0
- package/dist/wu-claim-branch.js.map +1 -0
- package/dist/wu-claim-output.js +83 -0
- package/dist/wu-claim-output.js.map +1 -0
- package/dist/wu-claim-resume-handler.js +85 -0
- package/dist/wu-claim-resume-handler.js.map +1 -0
- package/dist/wu-claim-state.js +572 -0
- package/dist/wu-claim-state.js.map +1 -0
- package/dist/wu-claim-validation.js +439 -0
- package/dist/wu-claim-validation.js.map +1 -0
- package/dist/wu-claim-worktree.js +221 -0
- package/dist/wu-claim-worktree.js.map +1 -0
- package/dist/wu-claim.js +96 -1394
- package/dist/wu-claim.js.map +1 -1
- package/dist/wu-code-path-coverage.js +81 -0
- package/dist/wu-code-path-coverage.js.map +1 -0
- package/dist/wu-create-content.js +256 -0
- package/dist/wu-create-content.js.map +1 -0
- package/dist/wu-create-readiness.js +57 -0
- package/dist/wu-create-readiness.js.map +1 -0
- package/dist/wu-create-validation.js +124 -0
- package/dist/wu-create-validation.js.map +1 -0
- package/dist/wu-create.js +45 -442
- package/dist/wu-create.js.map +1 -1
- package/dist/wu-done.js +151 -249
- package/dist/wu-done.js.map +1 -1
- package/dist/wu-edit-operations.js +401 -0
- package/dist/wu-edit-operations.js.map +1 -0
- package/dist/wu-edit-validators.js +280 -0
- package/dist/wu-edit-validators.js.map +1 -0
- package/dist/wu-edit.js +43 -759
- package/dist/wu-edit.js.map +1 -1
- package/dist/wu-prep.js +43 -127
- package/dist/wu-prep.js.map +1 -1
- package/dist/wu-repair.js +1 -1
- package/dist/wu-repair.js.map +1 -1
- package/dist/wu-sandbox.js +253 -0
- package/dist/wu-sandbox.js.map +1 -0
- package/dist/wu-spawn-prompt-builders.js +1124 -0
- package/dist/wu-spawn-prompt-builders.js.map +1 -0
- package/dist/wu-spawn-strategy-resolver.js +319 -0
- package/dist/wu-spawn-strategy-resolver.js.map +1 -0
- package/dist/wu-spawn.js +9 -1398
- package/dist/wu-spawn.js.map +1 -1
- package/dist/wu-status.js +4 -0
- package/dist/wu-status.js.map +1 -1
- package/dist/wu-validate.js +1 -1
- package/dist/wu-validate.js.map +1 -1
- package/package.json +15 -11
- package/templates/core/LUMENFLOW.md.template +29 -99
- package/templates/core/UPGRADING.md.template +2 -2
- package/templates/core/ai/onboarding/agent-invocation-guide.md.template +1 -1
- package/templates/core/ai/onboarding/quick-ref-commands.md.template +29 -4
- package/templates/core/ai/onboarding/release-process.md.template +1 -1
- package/templates/vendors/claude/.claude/skills/orchestration/SKILL.md.template +8 -8
package/dist/wu-done.js
CHANGED
|
@@ -33,12 +33,16 @@
|
|
|
33
33
|
*/
|
|
34
34
|
// WU-2542: Import from @lumenflow/core to establish shim layer dependency
|
|
35
35
|
import '@lumenflow/core';
|
|
36
|
+
// WU-1663: XState pipeline actor for state-driven orchestration
|
|
37
|
+
import { createActor } from 'xstate';
|
|
38
|
+
import { wuDoneMachine, WU_DONE_EVENTS } from '@lumenflow/core/wu-done-machine';
|
|
36
39
|
// WU-1153: wu:done guard for uncommitted code_paths is implemented in core package
|
|
37
40
|
// The guard runs in executeWorktreeCompletion() before metadata transaction
|
|
38
41
|
// See: packages/@lumenflow/core/src/wu-done-validation.ts
|
|
39
42
|
import { execSync } from 'node:child_process';
|
|
40
43
|
import prettyMs from 'pretty-ms';
|
|
41
44
|
import { runGates } from './gates.js';
|
|
45
|
+
import { resolveWuDonePreCommitGateDecision } from '@lumenflow/core/gates-agent-mode';
|
|
42
46
|
import { buildClaimRepairCommand } from './wu-claim-repair-guidance.js';
|
|
43
47
|
import { getGitForCwd, createGitForPath } from '@lumenflow/core/git-adapter';
|
|
44
48
|
import { die, getErrorMessage } from '@lumenflow/core/error-handler';
|
|
@@ -59,9 +63,8 @@ executePreflightCodePathValidation, buildPreflightCodePathErrorMessage,
|
|
|
59
63
|
// WU-2308: Pre-commit hooks with worktree context
|
|
60
64
|
validateAllPreCommitHooks,
|
|
61
65
|
// WU-2310: Type vs code_paths preflight validation
|
|
62
|
-
validateTypeVsCodePathsPreflight, buildTypeVsCodePathsErrorMessage,
|
|
63
|
-
|
|
64
|
-
validateDirtyMain, buildDirtyMainErrorMessage, } from '@lumenflow/core/wu-done-validators';
|
|
66
|
+
validateTypeVsCodePathsPreflight, buildTypeVsCodePathsErrorMessage, } from '@lumenflow/core/wu-done-validators';
|
|
67
|
+
import { formatPreflightWarnings } from '@lumenflow/core/wu-preflight-validators';
|
|
65
68
|
// WU-1825: validateCodePathsExist moved to unified code-path-validator
|
|
66
69
|
import { validateCodePathsExist } from '@lumenflow/core/code-path-validator';
|
|
67
70
|
import { BRANCHES, REMOTES, PATTERNS, DEFAULTS, LOG_PREFIX, EMOJI, GIT, SESSION, WU_STATUS, WU_EXPOSURE, PKG_MANAGER, SCRIPTS, CLI_FLAGS, FILE_SYSTEM, EXIT_CODES, STRING_LITERALS, MICRO_WORKTREE_OPERATIONS, TELEMETRY_STEPS, SKIP_GATES_REASONS, CHECKPOINT_MESSAGES, LUMENFLOW_PATHS, getWUStatusDisplay,
|
|
@@ -97,8 +100,8 @@ import { releaseLaneLock } from '@lumenflow/core/lane-lock';
|
|
|
97
100
|
// WU-1747: Checkpoint and lock for concurrent load resilience
|
|
98
101
|
import { createPreGatesCheckpoint as createWU1747Checkpoint, markGatesPassed, canSkipGates, clearCheckpoint, } from '@lumenflow/core/wu-checkpoint';
|
|
99
102
|
// WU-1946: Spawn registry for tracking sub-agent spawns
|
|
100
|
-
import {
|
|
101
|
-
import {
|
|
103
|
+
import { DelegationRegistryStore } from '@lumenflow/core/delegation-registry-store';
|
|
104
|
+
import { DelegationStatus } from '@lumenflow/core/delegation-registry-schema';
|
|
102
105
|
// WU-1999: Exposure validation for UI pairing
|
|
103
106
|
// WU-2022: Feature accessibility validation (blocking)
|
|
104
107
|
import { validateExposure, validateFeatureAccessibility } from '@lumenflow/core/wu-validation';
|
|
@@ -454,7 +457,7 @@ export async function enforceSpawnProvenanceForDone(id, doc, options = {}) {
|
|
|
454
457
|
const initiativeId = doc.initiative.trim();
|
|
455
458
|
const baseDir = options.baseDir ?? process.cwd();
|
|
456
459
|
const force = options.force === true;
|
|
457
|
-
const store = new
|
|
460
|
+
const store = new DelegationRegistryStore(path.join(baseDir, '.lumenflow', 'state'));
|
|
458
461
|
await store.load();
|
|
459
462
|
const spawnEntry = store.getByTarget(id);
|
|
460
463
|
if (!spawnEntry) {
|
|
@@ -493,7 +496,7 @@ export async function enforceSpawnProvenanceForDone(id, doc, options = {}) {
|
|
|
493
496
|
*/
|
|
494
497
|
export async function updateSpawnRegistryOnCompletion(id, baseDir = process.cwd()) {
|
|
495
498
|
try {
|
|
496
|
-
const store = new
|
|
499
|
+
const store = new DelegationRegistryStore(path.join(baseDir, '.lumenflow', 'state'));
|
|
497
500
|
await store.load();
|
|
498
501
|
const spawnEntry = store.getByTarget(id);
|
|
499
502
|
// Graceful skip if no spawn entry found (legacy WU)
|
|
@@ -502,7 +505,7 @@ export async function updateSpawnRegistryOnCompletion(id, baseDir = process.cwd(
|
|
|
502
505
|
return;
|
|
503
506
|
}
|
|
504
507
|
// Update status to completed with completedAt timestamp
|
|
505
|
-
await store.updateStatus(spawnEntry.id,
|
|
508
|
+
await store.updateStatus(spawnEntry.id, DelegationStatus.COMPLETED);
|
|
506
509
|
console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Spawn registry updated: ${id} marked as completed`);
|
|
507
510
|
}
|
|
508
511
|
catch (err) {
|
|
@@ -655,118 +658,6 @@ async function _ensureCleanWorkingTree() {
|
|
|
655
658
|
` - Leftover changes from previous session`);
|
|
656
659
|
}
|
|
657
660
|
}
|
|
658
|
-
const INTERNAL_LIFECYCLE_DIRTY_FILES = new Set([
|
|
659
|
-
'.lumenflow/flow.log',
|
|
660
|
-
'.lumenflow/skip-gates-audit.log',
|
|
661
|
-
'.lumenflow/skip-cos-gates-audit.log',
|
|
662
|
-
]);
|
|
663
|
-
/**
|
|
664
|
-
* WU-1554: Prefixes for metadata files that wu:done manages.
|
|
665
|
-
* These files may be dirty after merge+rebase when wu:claim used push-only mode
|
|
666
|
-
* or when concurrent agents advance origin/main. Instead of blocking wu:done,
|
|
667
|
-
* these are auto-committed by the caller.
|
|
668
|
-
*/
|
|
669
|
-
const METADATA_LIFECYCLE_PREFIXES = [
|
|
670
|
-
'.lumenflow/state/',
|
|
671
|
-
'.lumenflow/stamps/',
|
|
672
|
-
'.lumenflow/archive/',
|
|
673
|
-
];
|
|
674
|
-
const PROTECTED_MAIN_LIKE_BRANCHES = new Set([BRANCHES.MAIN, BRANCHES.MASTER]);
|
|
675
|
-
export function isProtectedMainLikeBranch(branchName) {
|
|
676
|
-
if (!branchName) {
|
|
677
|
-
return false;
|
|
678
|
-
}
|
|
679
|
-
return PROTECTED_MAIN_LIKE_BRANCHES.has(branchName.trim());
|
|
680
|
-
}
|
|
681
|
-
export function shouldAutoCommitLifecycleWrites(branchName) {
|
|
682
|
-
if (!branchName || branchName.trim().length === 0) {
|
|
683
|
-
// Fail-safe: unknown branch should never trigger direct lifecycle commits.
|
|
684
|
-
return false;
|
|
685
|
-
}
|
|
686
|
-
return !isProtectedMainLikeBranch(branchName);
|
|
687
|
-
}
|
|
688
|
-
function parsePorcelainPath(line) {
|
|
689
|
-
if (line.length < 4)
|
|
690
|
-
return null;
|
|
691
|
-
const pathPart = line.slice(3).trim();
|
|
692
|
-
if (!pathPart)
|
|
693
|
-
return null;
|
|
694
|
-
const renameIndex = pathPart.indexOf(' -> ');
|
|
695
|
-
if (renameIndex !== -1) {
|
|
696
|
-
return pathPart.slice(renameIndex + 4);
|
|
697
|
-
}
|
|
698
|
-
return pathPart;
|
|
699
|
-
}
|
|
700
|
-
/**
|
|
701
|
-
* WU-1554: Check if a file is a metadata lifecycle file.
|
|
702
|
-
* Metadata files are managed by wu:done (wu-events.jsonl, status.md, backlog.md,
|
|
703
|
-
* WU YAML, stamps, archives) and may be dirty after merge+rebase.
|
|
704
|
-
*/
|
|
705
|
-
function isMetadataLifecycleFile(filePath, metadataDir) {
|
|
706
|
-
if (METADATA_LIFECYCLE_PREFIXES.some((prefix) => filePath.startsWith(prefix))) {
|
|
707
|
-
return true;
|
|
708
|
-
}
|
|
709
|
-
if (metadataDir && filePath.startsWith(metadataDir + '/')) {
|
|
710
|
-
return true;
|
|
711
|
-
}
|
|
712
|
-
return false;
|
|
713
|
-
}
|
|
714
|
-
/**
|
|
715
|
-
* WU-1084: Check for uncommitted changes on main after merge completes.
|
|
716
|
-
*
|
|
717
|
-
* This catches cases where pnpm format (or other tooling) touched files
|
|
718
|
-
* outside the WU's code_paths during worktree work. These changes survive
|
|
719
|
-
* the merge and would be silently left behind when the worktree is removed.
|
|
720
|
-
*
|
|
721
|
-
* WU-1554: Added metadataDir parameter and metadataFiles return field.
|
|
722
|
-
* Metadata files (wu-events.jsonl, status.md, backlog.md, WU YAML, stamps)
|
|
723
|
-
* may be dirty after merge+rebase when wu:claim used push-only mode.
|
|
724
|
-
* These are returned separately for auto-commit by the caller.
|
|
725
|
-
*
|
|
726
|
-
* @param gitStatus - Output from git status (porcelain format)
|
|
727
|
-
* @param wuId - The WU ID for error messaging
|
|
728
|
-
* @param metadataDir - Optional task directory prefix for metadata file detection
|
|
729
|
-
* @returns Object with isDirty flag, file categories, and optional error message
|
|
730
|
-
*/
|
|
731
|
-
export function checkPostMergeDirtyState(gitStatus, wuId, metadataDir) {
|
|
732
|
-
// WU-1522: Split before trimming to preserve leading spaces in porcelain format.
|
|
733
|
-
// Porcelain lines like ' M .lumenflow/flow.log' use the leading space as a status
|
|
734
|
-
// indicator (working tree vs staging area). Trimming the whole string first strips
|
|
735
|
-
// the leading space from the first line, corrupting parsePorcelainPath output.
|
|
736
|
-
const lines = gitStatus.split('\n').filter((line) => line.length >= 4);
|
|
737
|
-
if (lines.length === 0) {
|
|
738
|
-
return { isDirty: false, internalOnlyFiles: [], metadataFiles: [], unrelatedFiles: [] };
|
|
739
|
-
}
|
|
740
|
-
const dirtyFiles = lines
|
|
741
|
-
.map((line) => parsePorcelainPath(line))
|
|
742
|
-
.filter((value) => Boolean(value));
|
|
743
|
-
// WU-1554: Three-category classification
|
|
744
|
-
const internalOnlyFiles = dirtyFiles.filter((file) => INTERNAL_LIFECYCLE_DIRTY_FILES.has(file));
|
|
745
|
-
const metadataFiles = dirtyFiles.filter((file) => !INTERNAL_LIFECYCLE_DIRTY_FILES.has(file) && isMetadataLifecycleFile(file, metadataDir));
|
|
746
|
-
const unrelatedFiles = dirtyFiles.filter((file) => !INTERNAL_LIFECYCLE_DIRTY_FILES.has(file) && !isMetadataLifecycleFile(file, metadataDir));
|
|
747
|
-
if (unrelatedFiles.length === 0) {
|
|
748
|
-
return { isDirty: false, internalOnlyFiles, metadataFiles, unrelatedFiles: [] };
|
|
749
|
-
}
|
|
750
|
-
const displayStatus = gitStatus.trim();
|
|
751
|
-
const error = `Main branch has uncommitted changes after merge:\n\n${displayStatus}\n\n` +
|
|
752
|
-
`This indicates files were modified outside the WU's code_paths.\n` +
|
|
753
|
-
`Common cause: pnpm format touched files outside the WU scope.\n\n` +
|
|
754
|
-
`The worktree has NOT been removed to allow investigation.\n\n` +
|
|
755
|
-
`Options:\n` +
|
|
756
|
-
` 1. Review and commit the changes: git add . && git commit -m "format: fix formatting"\n` +
|
|
757
|
-
` 2. Discard if unwanted: git checkout -- .\n` +
|
|
758
|
-
` 3. Then re-run: pnpm wu:done --id ${wuId} --skip-worktree-completion`;
|
|
759
|
-
return { isDirty: true, internalOnlyFiles, metadataFiles, unrelatedFiles, error };
|
|
760
|
-
}
|
|
761
|
-
/**
|
|
762
|
-
* Build the list of lifecycle files that are safe to restore after a failed wu:done attempt.
|
|
763
|
-
*
|
|
764
|
-
* On failure, wu:done can leave internal lifecycle logs and metadata lifecycle files dirty on main.
|
|
765
|
-
* These files are managed by lifecycle tooling and should not block re-claim/retry.
|
|
766
|
-
*/
|
|
767
|
-
export function getLifecycleRestoreTargetsOnFailure(dirtyState) {
|
|
768
|
-
return Array.from(new Set([...dirtyState.internalOnlyFiles, ...dirtyState.metadataFiles]));
|
|
769
|
-
}
|
|
770
661
|
/**
|
|
771
662
|
* Extract completed WU IDs from git log output.
|
|
772
663
|
* @param {string} logOutput - Git log output (one commit per line)
|
|
@@ -1663,6 +1554,12 @@ async function executePreFlightChecks({ id, args, isBranchOnly, isDocsOnly, docM
|
|
|
1663
1554
|
const errorMessage = buildPreflightCodePathErrorMessage(id, preflightResult);
|
|
1664
1555
|
die(errorMessage);
|
|
1665
1556
|
}
|
|
1557
|
+
if (Array.isArray(preflightResult.warnings) && preflightResult.warnings.length > 0) {
|
|
1558
|
+
const warningLines = formatPreflightWarnings(preflightResult.warnings, `${LOG_PREFIX.DONE} ${EMOJI.WARNING} Reality preflight warnings:`);
|
|
1559
|
+
for (const line of warningLines) {
|
|
1560
|
+
console.log(line.startsWith(' - ') ? `${LOG_PREFIX.DONE} ${line}` : line);
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1666
1563
|
// WU-2310: Preflight type vs code_paths validation
|
|
1667
1564
|
// Run BEFORE transaction to prevent documentation WUs with code paths from failing at git commit
|
|
1668
1565
|
console.log(`${LOG_PREFIX.DONE} Validating type vs code_paths (WU-2310)...`);
|
|
@@ -1710,42 +1607,6 @@ async function executePreFlightChecks({ id, args, isBranchOnly, isDocsOnly, docM
|
|
|
1710
1607
|
else {
|
|
1711
1608
|
// Worktree mode: must be on main
|
|
1712
1609
|
await ensureOnMain(getGitForCwd());
|
|
1713
|
-
// WU-1503: Dirty-main pre-merge guard (replaces blanket ensureCleanWorkingTree)
|
|
1714
|
-
// Distinguishes between WU-related dirty files (allowed) and unrelated dirty
|
|
1715
|
-
// files (blocked with actionable guidance). Uses --force for audited bypass.
|
|
1716
|
-
{
|
|
1717
|
-
const gitAdapter = getGitForCwd();
|
|
1718
|
-
const gitStatus = await gitAdapter.raw(['status', '--porcelain']);
|
|
1719
|
-
if (gitStatus && gitStatus.trim()) {
|
|
1720
|
-
const wuCodePaths = docForValidation.code_paths || [];
|
|
1721
|
-
const dirtyResult = validateDirtyMain(gitStatus, id, wuCodePaths);
|
|
1722
|
-
if (!dirtyResult.valid) {
|
|
1723
|
-
if (args.force) {
|
|
1724
|
-
console.log(`\n${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-1503: Dirty-main guard bypassed with --force`);
|
|
1725
|
-
console.log(`${LOG_PREFIX.DONE} Unrelated dirty files (${dirtyResult.unrelatedFiles.length}):`);
|
|
1726
|
-
for (const f of dirtyResult.unrelatedFiles) {
|
|
1727
|
-
console.log(`${LOG_PREFIX.DONE} - ${f}`);
|
|
1728
|
-
}
|
|
1729
|
-
}
|
|
1730
|
-
else {
|
|
1731
|
-
die(buildDirtyMainErrorMessage(id, dirtyResult.unrelatedFiles));
|
|
1732
|
-
}
|
|
1733
|
-
}
|
|
1734
|
-
else if (dirtyResult.relatedFiles.length > 0) {
|
|
1735
|
-
console.log(`${LOG_PREFIX.DONE} ${EMOJI.INFO} WU-1503: ${dirtyResult.relatedFiles.length} related dirty file(s) on main (allowed)`);
|
|
1736
|
-
// WU-1554: Auto-restore related dirty files so ff-only merge can proceed.
|
|
1737
|
-
// These files will be overwritten by the merge commit anyway.
|
|
1738
|
-
// Without this, git merge --ff-only refuses to overwrite dirty tracked files.
|
|
1739
|
-
try {
|
|
1740
|
-
await gitAdapter.raw(['checkout', '--', ...dirtyResult.relatedFiles]);
|
|
1741
|
-
console.log(`${LOG_PREFIX.DONE} ${EMOJI.INFO} WU-1554: Auto-restored ${dirtyResult.relatedFiles.length} related file(s) for clean merge`);
|
|
1742
|
-
}
|
|
1743
|
-
catch (restoreErr) {
|
|
1744
|
-
console.warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-1554: Could not auto-restore related files: ${restoreErr.message}`);
|
|
1745
|
-
}
|
|
1746
|
-
}
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
1749
1610
|
// Prevent coordination failures by ensuring main is up-to-date
|
|
1750
1611
|
await ensureMainUpToDate();
|
|
1751
1612
|
// P0 EMERGENCY FIX Part 1: Restore wu-events.jsonl BEFORE parallel completion check
|
|
@@ -1939,10 +1800,17 @@ async function executePreFlightChecks({ id, args, isBranchOnly, isDocsOnly, docM
|
|
|
1939
1800
|
return { title, docForValidation };
|
|
1940
1801
|
}
|
|
1941
1802
|
async function executeGates({ id, args, isBranchOnly, isDocsOnly, worktreePath, branchName, }) {
|
|
1803
|
+
const gateResult = {
|
|
1804
|
+
fullGatesRanInCurrentRun: false,
|
|
1805
|
+
skippedByCheckpoint: false,
|
|
1806
|
+
checkpointId: null,
|
|
1807
|
+
};
|
|
1942
1808
|
// WU-1747: Check if gates can be skipped based on valid checkpoint
|
|
1943
1809
|
// This allows resuming wu:done without re-running gates if nothing changed
|
|
1944
1810
|
const skipResult = canSkipGates(id, { currentHeadSha: undefined });
|
|
1945
1811
|
if (skipResult.canSkip) {
|
|
1812
|
+
gateResult.skippedByCheckpoint = true;
|
|
1813
|
+
gateResult.checkpointId = skipResult.checkpoint.checkpointId;
|
|
1946
1814
|
console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} ${CHECKPOINT_MESSAGES.SKIPPING_GATES_VALID}`);
|
|
1947
1815
|
console.log(`${LOG_PREFIX.DONE} ${CHECKPOINT_MESSAGES.CHECKPOINT_LABEL}: ${skipResult.checkpoint.checkpointId}`);
|
|
1948
1816
|
console.log(`${LOG_PREFIX.DONE} ${CHECKPOINT_MESSAGES.GATES_PASSED_AT}: ${skipResult.checkpoint.gatesPassedAt}`);
|
|
@@ -1954,7 +1822,7 @@ async function executeGates({ id, args, isBranchOnly, isDocsOnly, worktreePath,
|
|
|
1954
1822
|
reason: SKIP_GATES_REASONS.CHECKPOINT_VALID,
|
|
1955
1823
|
checkpoint_id: skipResult.checkpoint.checkpointId,
|
|
1956
1824
|
});
|
|
1957
|
-
return; // Skip gates entirely
|
|
1825
|
+
return gateResult; // Skip gates entirely
|
|
1958
1826
|
}
|
|
1959
1827
|
// WU-1747: Create checkpoint before gates for resumption on failure
|
|
1960
1828
|
if (worktreePath && branchName) {
|
|
@@ -2071,6 +1939,7 @@ async function executeGates({ id, args, isBranchOnly, isDocsOnly, worktreePath,
|
|
|
2071
1939
|
});
|
|
2072
1940
|
die(`Gates failed in Branch-Only mode. Fix issues and try again.`);
|
|
2073
1941
|
}
|
|
1942
|
+
gateResult.fullGatesRanInCurrentRun = true;
|
|
2074
1943
|
}
|
|
2075
1944
|
else if (worktreePath && existsSync(worktreePath)) {
|
|
2076
1945
|
// Worktree mode: run gates in the dedicated worktree
|
|
@@ -2079,6 +1948,7 @@ async function executeGates({ id, args, isBranchOnly, isDocsOnly, worktreePath,
|
|
|
2079
1948
|
isDocsOnly,
|
|
2080
1949
|
docsOnly: Boolean(args.docsOnly),
|
|
2081
1950
|
});
|
|
1951
|
+
gateResult.fullGatesRanInCurrentRun = true;
|
|
2082
1952
|
}
|
|
2083
1953
|
else {
|
|
2084
1954
|
die(`Worktree not found (${worktreePath || 'unknown'}). Gates must run in the lane worktree.\n` +
|
|
@@ -2136,6 +2006,7 @@ async function executeGates({ id, args, isBranchOnly, isDocsOnly, worktreePath,
|
|
|
2136
2006
|
// WU-1747: Mark checkpoint as gates passed for resumption on failure
|
|
2137
2007
|
// This allows subsequent wu:done attempts to skip gates if nothing changed
|
|
2138
2008
|
markGatesPassed(id);
|
|
2009
|
+
return gateResult;
|
|
2139
2010
|
}
|
|
2140
2011
|
/**
|
|
2141
2012
|
* Print State HUD for visibility
|
|
@@ -2194,6 +2065,29 @@ async function main() {
|
|
|
2194
2065
|
isBranchPR, derivedWorktree, docForValidation: initialDocForValidation, isDocsOnly, } = pathInfo;
|
|
2195
2066
|
// Capture main checkout path once. process.cwd() may drift later during recovery flows.
|
|
2196
2067
|
const mainCheckoutPath = process.cwd();
|
|
2068
|
+
// WU-1663: Determine prepPassed early for pipeline actor input.
|
|
2069
|
+
// canSkipGates checks if wu:prep already ran gates successfully via checkpoint.
|
|
2070
|
+
// This drives the isPrepPassed guard on the GATES_SKIPPED transition.
|
|
2071
|
+
const earlySkipResult = canSkipGates(id, { currentHeadSha: undefined });
|
|
2072
|
+
const prepPassed = earlySkipResult.canSkip;
|
|
2073
|
+
// WU-1663: Create XState pipeline actor for state-driven orchestration.
|
|
2074
|
+
// The actor tracks which pipeline stage we're in (validating, gating, committing, etc.)
|
|
2075
|
+
// and provides explicit state/transition contracts. Existing procedural logic continues
|
|
2076
|
+
// to do the real work; the actor provides structured state tracking alongside it.
|
|
2077
|
+
const pipelineActor = createActor(wuDoneMachine, {
|
|
2078
|
+
input: {
|
|
2079
|
+
wuId: id,
|
|
2080
|
+
worktreePath: derivedWorktree,
|
|
2081
|
+
prepPassed,
|
|
2082
|
+
},
|
|
2083
|
+
});
|
|
2084
|
+
pipelineActor.start();
|
|
2085
|
+
// WU-1663: Send START event to transition from idle -> validating
|
|
2086
|
+
pipelineActor.send({
|
|
2087
|
+
type: WU_DONE_EVENTS.START,
|
|
2088
|
+
wuId: id,
|
|
2089
|
+
worktreePath: derivedWorktree || '',
|
|
2090
|
+
});
|
|
2197
2091
|
// WU-1590: branch-pr has no worktree, treat like branch-only for path resolution and ensureOnMain skip
|
|
2198
2092
|
const isNoWorktreeMode = isBranchOnly || isBranchPR;
|
|
2199
2093
|
const resolvedWorktreePath = derivedWorktree && !isNoWorktreeMode
|
|
@@ -2220,18 +2114,32 @@ async function main() {
|
|
|
2220
2114
|
await ensureCleanWorktree(effectiveWorktreePath);
|
|
2221
2115
|
}
|
|
2222
2116
|
// Pre-flight checks (WU-1215: extracted to executePreFlightChecks function)
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2117
|
+
// WU-1663: Wrap in try/catch to send pipeline failure event before die() propagates
|
|
2118
|
+
let preFlightResult;
|
|
2119
|
+
try {
|
|
2120
|
+
preFlightResult = await executePreFlightChecks({
|
|
2121
|
+
id,
|
|
2122
|
+
args,
|
|
2123
|
+
isBranchOnly: effectiveBranchOnly,
|
|
2124
|
+
isDocsOnly,
|
|
2125
|
+
docMain,
|
|
2126
|
+
docForValidation: initialDocForValidation,
|
|
2127
|
+
derivedWorktree: effectiveDerivedWorktree,
|
|
2128
|
+
});
|
|
2129
|
+
}
|
|
2130
|
+
catch (preFlightErr) {
|
|
2131
|
+
pipelineActor.send({
|
|
2132
|
+
type: WU_DONE_EVENTS.VALIDATION_FAILED,
|
|
2133
|
+
error: getErrorMessage(preFlightErr),
|
|
2134
|
+
});
|
|
2135
|
+
pipelineActor.stop();
|
|
2136
|
+
throw preFlightErr;
|
|
2137
|
+
}
|
|
2232
2138
|
const title = preFlightResult.title;
|
|
2233
2139
|
// Note: docForValidation is returned but not used after pre-flight checks
|
|
2234
2140
|
// The metadata transaction uses docForUpdate instead
|
|
2141
|
+
// WU-1663: Pre-flight checks passed - transition to preparing state
|
|
2142
|
+
pipelineActor.send({ type: WU_DONE_EVENTS.VALIDATION_PASSED });
|
|
2235
2143
|
// WU-1599: Enforce auditable spawn provenance for initiative-governed WUs.
|
|
2236
2144
|
await enforceSpawnProvenanceForDone(id, docMain, {
|
|
2237
2145
|
baseDir: mainCheckoutPath,
|
|
@@ -2272,7 +2180,35 @@ async function main() {
|
|
|
2272
2180
|
}
|
|
2273
2181
|
// Otherwise silently allow - fail-open
|
|
2274
2182
|
}
|
|
2275
|
-
|
|
2183
|
+
// WU-1663: Preparation complete - transition to gating state
|
|
2184
|
+
pipelineActor.send({ type: WU_DONE_EVENTS.PREPARATION_COMPLETE });
|
|
2185
|
+
// WU-1663: Wrap gates in try/catch to send pipeline failure event
|
|
2186
|
+
let gateExecutionResult;
|
|
2187
|
+
try {
|
|
2188
|
+
gateExecutionResult = await executeGates({
|
|
2189
|
+
id,
|
|
2190
|
+
args,
|
|
2191
|
+
isBranchOnly: effectiveBranchOnly,
|
|
2192
|
+
isDocsOnly,
|
|
2193
|
+
worktreePath,
|
|
2194
|
+
});
|
|
2195
|
+
}
|
|
2196
|
+
catch (gateErr) {
|
|
2197
|
+
pipelineActor.send({
|
|
2198
|
+
type: WU_DONE_EVENTS.GATES_FAILED,
|
|
2199
|
+
error: getErrorMessage(gateErr),
|
|
2200
|
+
});
|
|
2201
|
+
pipelineActor.stop();
|
|
2202
|
+
throw gateErr;
|
|
2203
|
+
}
|
|
2204
|
+
// WU-1663: Gates passed - transition from gating state.
|
|
2205
|
+
// Use GATES_SKIPPED if checkpoint dedup allowed skip, GATES_PASSED otherwise.
|
|
2206
|
+
if (gateExecutionResult.skippedByCheckpoint) {
|
|
2207
|
+
pipelineActor.send({ type: WU_DONE_EVENTS.GATES_SKIPPED });
|
|
2208
|
+
}
|
|
2209
|
+
else {
|
|
2210
|
+
pipelineActor.send({ type: WU_DONE_EVENTS.GATES_PASSED });
|
|
2211
|
+
}
|
|
2276
2212
|
// Print State HUD for visibility (WU-1215: extracted to printStateHUD function)
|
|
2277
2213
|
printStateHUD({
|
|
2278
2214
|
id,
|
|
@@ -2282,12 +2218,17 @@ async function main() {
|
|
|
2282
2218
|
derivedWorktree: effectiveDerivedWorktree,
|
|
2283
2219
|
STAMPS_DIR,
|
|
2284
2220
|
});
|
|
2285
|
-
// Step 0.5: Pre-flight validation
|
|
2286
|
-
//
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2221
|
+
// Step 0.5: Pre-flight hook validation policy.
|
|
2222
|
+
// WU-1659: Reuse Step 0 gate attestation/checkpoint and avoid duplicate full-suite execution.
|
|
2223
|
+
const preCommitGateDecision = resolveWuDonePreCommitGateDecision({
|
|
2224
|
+
skipGates: Boolean(args.skipGates),
|
|
2225
|
+
fullGatesRanInCurrentRun: gateExecutionResult.fullGatesRanInCurrentRun,
|
|
2226
|
+
skippedByCheckpoint: gateExecutionResult.skippedByCheckpoint,
|
|
2227
|
+
checkpointId: gateExecutionResult.checkpointId,
|
|
2228
|
+
});
|
|
2229
|
+
console.log(`${LOG_PREFIX.DONE} ${preCommitGateDecision.message}`);
|
|
2230
|
+
// Fallback path remains available if gate attestation is missing for any reason.
|
|
2231
|
+
if (preCommitGateDecision.runPreCommitFullSuite) {
|
|
2291
2232
|
const hookResult = await validateAllPreCommitHooks(id, worktreePath, {
|
|
2292
2233
|
runGates: ({ cwd }) => runGates({ cwd, docsOnly: false }),
|
|
2293
2234
|
});
|
|
@@ -2295,9 +2236,6 @@ async function main() {
|
|
|
2295
2236
|
die('Pre-flight validation failed. Fix hook issues and try again.');
|
|
2296
2237
|
}
|
|
2297
2238
|
}
|
|
2298
|
-
else {
|
|
2299
|
-
console.log(`${LOG_PREFIX.DONE} Skipping pre-flight hook validation (--skip-gates)`);
|
|
2300
|
-
}
|
|
2301
2239
|
// Step 0.6: WU-1781 - Run tasks:validate preflight BEFORE any merge/push operations
|
|
2302
2240
|
// This prevents deadlocks where validation fails after merge, leaving local main ahead of origin
|
|
2303
2241
|
// Specifically catches stamp-status mismatches from legacy WUs that would block pre-push hooks
|
|
@@ -2358,6 +2296,12 @@ async function main() {
|
|
|
2358
2296
|
};
|
|
2359
2297
|
completionResult = await executeWorktreeCompletion(worktreeContext);
|
|
2360
2298
|
}
|
|
2299
|
+
// WU-1663: Mode-specific completion succeeded - send pipeline events.
|
|
2300
|
+
// The completion modules handle commit, merge, and push internally.
|
|
2301
|
+
// We send the corresponding pipeline events based on the completion result.
|
|
2302
|
+
pipelineActor.send({ type: WU_DONE_EVENTS.COMMIT_COMPLETE });
|
|
2303
|
+
pipelineActor.send({ type: WU_DONE_EVENTS.MERGE_COMPLETE });
|
|
2304
|
+
pipelineActor.send({ type: WU_DONE_EVENTS.PUSH_COMPLETE });
|
|
2361
2305
|
// Handle recovery mode (zombie state cleanup completed)
|
|
2362
2306
|
if ('recovered' in completionResult && completionResult.recovered) {
|
|
2363
2307
|
// P0 FIX: Release lane lock before early exit
|
|
@@ -2369,30 +2313,28 @@ async function main() {
|
|
|
2369
2313
|
catch {
|
|
2370
2314
|
// Intentionally ignore lock release errors during cleanup
|
|
2371
2315
|
}
|
|
2316
|
+
pipelineActor.stop();
|
|
2372
2317
|
process.exit(EXIT_CODES.SUCCESS);
|
|
2373
2318
|
}
|
|
2374
2319
|
}
|
|
2375
2320
|
catch (err) {
|
|
2376
|
-
// WU-
|
|
2377
|
-
//
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
console.warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} Could not restore lifecycle files after failure: ${cleanupErr.message}`);
|
|
2394
|
-
}
|
|
2395
|
-
}
|
|
2321
|
+
// WU-1663: Mode execution failed - determine which stage failed
|
|
2322
|
+
// based on completion result flags and send appropriate failure event.
|
|
2323
|
+
const failureStage = completionResult.committed === false
|
|
2324
|
+
? WU_DONE_EVENTS.COMMIT_FAILED
|
|
2325
|
+
: completionResult.merged === false
|
|
2326
|
+
? WU_DONE_EVENTS.MERGE_FAILED
|
|
2327
|
+
: completionResult.pushed === false
|
|
2328
|
+
? WU_DONE_EVENTS.PUSH_FAILED
|
|
2329
|
+
: WU_DONE_EVENTS.COMMIT_FAILED; // Default to commit as earliest possible failure
|
|
2330
|
+
pipelineActor.send({
|
|
2331
|
+
type: failureStage,
|
|
2332
|
+
error: getErrorMessage(err),
|
|
2333
|
+
});
|
|
2334
|
+
// WU-1663: Log pipeline state for diagnostics
|
|
2335
|
+
const failedSnapshot = pipelineActor.getSnapshot();
|
|
2336
|
+
console.error(`${LOG_PREFIX.DONE} Pipeline state: ${failedSnapshot.value} (failedAt: ${failedSnapshot.context.failedAt})`);
|
|
2337
|
+
pipelineActor.stop();
|
|
2396
2338
|
// P0 FIX: Release lane lock before error exit
|
|
2397
2339
|
try {
|
|
2398
2340
|
const lane = docMain.lane;
|
|
@@ -2402,6 +2344,8 @@ async function main() {
|
|
|
2402
2344
|
catch {
|
|
2403
2345
|
// Intentionally ignore lock release errors during error handling
|
|
2404
2346
|
}
|
|
2347
|
+
console.error(`\n${LOG_PREFIX.DONE} ${EMOJI.FAILURE} Mode execution failed: ${getErrorMessage(err)}`);
|
|
2348
|
+
console.error(`${LOG_PREFIX.DONE} ${EMOJI.INFO} Next step: resolve the reported error and retry: pnpm wu:done --id ${id}`);
|
|
2405
2349
|
// WU-1811: Check if cleanup is safe before removing worktree
|
|
2406
2350
|
// If cleanupSafe is false (or undefined), preserve worktree for recovery
|
|
2407
2351
|
if (err.cleanupSafe === false) {
|
|
@@ -2415,58 +2359,6 @@ async function main() {
|
|
|
2415
2359
|
else {
|
|
2416
2360
|
await ensureNoAutoStagedOrNoop([WU_PATH, STATUS_PATH, BACKLOG_PATH, STAMPS_DIR]);
|
|
2417
2361
|
}
|
|
2418
|
-
// WU-1084: Check for uncommitted changes on main after merge
|
|
2419
|
-
// WU-1554: Pass metadataDir so metadata files are classified separately
|
|
2420
|
-
const gitMain = getGitForCwd();
|
|
2421
|
-
const currentBranch = await gitMain.getCurrentBranch();
|
|
2422
|
-
const allowLifecycleAutoCommit = shouldAutoCommitLifecycleWrites(currentBranch);
|
|
2423
|
-
const metadataDir = path.dirname(STATUS_PATH);
|
|
2424
|
-
const postMergeStatus = await gitMain.getStatus();
|
|
2425
|
-
const dirtyCheck = checkPostMergeDirtyState(postMergeStatus, id, metadataDir);
|
|
2426
|
-
if (dirtyCheck.internalOnlyFiles.length > 0) {
|
|
2427
|
-
try {
|
|
2428
|
-
await gitMain.raw(['restore', '--', ...dirtyCheck.internalOnlyFiles]);
|
|
2429
|
-
console.log(`${LOG_PREFIX.DONE} ${EMOJI.INFO} Restored internal lifecycle log files: ${dirtyCheck.internalOnlyFiles.join(', ')}`);
|
|
2430
|
-
}
|
|
2431
|
-
catch (restoreErr) {
|
|
2432
|
-
console.warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} Could not auto-restore internal lifecycle files: ${restoreErr.message}`);
|
|
2433
|
-
}
|
|
2434
|
-
}
|
|
2435
|
-
// WU-1554: Auto-commit metadata files left dirty after merge+rebase
|
|
2436
|
-
// This handles push-only claim mode and concurrent agent advancement
|
|
2437
|
-
if (dirtyCheck.metadataFiles.length > 0) {
|
|
2438
|
-
if (allowLifecycleAutoCommit) {
|
|
2439
|
-
try {
|
|
2440
|
-
await gitMain.add(dirtyCheck.metadataFiles);
|
|
2441
|
-
await gitMain.commit(`chore: post-merge metadata sync for ${id} [skip ci]`);
|
|
2442
|
-
await gitMain.raw(['pull', '--rebase', '--autostash', 'origin', currentBranch]);
|
|
2443
|
-
await gitMain.push('origin', currentBranch);
|
|
2444
|
-
console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Auto-committed post-merge metadata: ${dirtyCheck.metadataFiles.join(', ')}`);
|
|
2445
|
-
}
|
|
2446
|
-
catch (commitErr) {
|
|
2447
|
-
console.warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} Could not auto-commit metadata files: ${commitErr.message}`);
|
|
2448
|
-
}
|
|
2449
|
-
}
|
|
2450
|
-
else {
|
|
2451
|
-
try {
|
|
2452
|
-
await gitMain.raw(['restore', '--', ...dirtyCheck.metadataFiles]);
|
|
2453
|
-
console.log(`${LOG_PREFIX.DONE} ${EMOJI.INFO} Protected branch ${currentBranch}: restored lifecycle metadata files instead of committing on branch`);
|
|
2454
|
-
}
|
|
2455
|
-
catch (restoreErr) {
|
|
2456
|
-
console.warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} Could not restore metadata lifecycle files on protected branch ${currentBranch}: ${restoreErr.message}`);
|
|
2457
|
-
}
|
|
2458
|
-
}
|
|
2459
|
-
}
|
|
2460
|
-
const postLifecycleStatus = await gitMain.getStatus();
|
|
2461
|
-
const postLifecycleDirty = checkPostMergeDirtyState(postLifecycleStatus, id, metadataDir);
|
|
2462
|
-
if (!allowLifecycleAutoCommit && postLifecycleDirty.metadataFiles.length > 0) {
|
|
2463
|
-
die(`Protected branch ${currentBranch} still has lifecycle metadata changes after wu:done:\n` +
|
|
2464
|
-
`${postLifecycleDirty.metadataFiles.join(STRING_LITERALS.NEWLINE)}\n\n` +
|
|
2465
|
-
`wu:done will not commit directly on ${currentBranch}. Resolve and retry from the WU worktree/lane branch.`);
|
|
2466
|
-
}
|
|
2467
|
-
if (postLifecycleDirty.isDirty) {
|
|
2468
|
-
die(postLifecycleDirty.error);
|
|
2469
|
-
}
|
|
2470
2362
|
// Step 6 & 7: Cleanup (remove worktree, delete branch) - WU-1215
|
|
2471
2363
|
// WU-1811: Only run cleanup if all completion steps succeeded
|
|
2472
2364
|
if (completionResult.cleanupSafe !== false) {
|
|
@@ -2539,6 +2431,12 @@ async function main() {
|
|
|
2539
2431
|
// Double fail-open: even if runDecayOnDone itself throws unexpectedly, never block wu:done
|
|
2540
2432
|
console.warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} Decay archival error (fail-open): ${getErrorMessage(err)}`);
|
|
2541
2433
|
}
|
|
2434
|
+
// WU-1663: Cleanup complete - transition to final done state
|
|
2435
|
+
pipelineActor.send({ type: WU_DONE_EVENTS.CLEANUP_COMPLETE });
|
|
2436
|
+
// WU-1663: Log final pipeline state for diagnostics
|
|
2437
|
+
const finalSnapshot = pipelineActor.getSnapshot();
|
|
2438
|
+
console.log(`${LOG_PREFIX.DONE} Pipeline state: ${finalSnapshot.value} (WU-1663)`);
|
|
2439
|
+
pipelineActor.stop();
|
|
2542
2440
|
console.log(`\n${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Transaction COMMIT - all steps succeeded (WU-755)`);
|
|
2543
2441
|
console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Marked done, pushed, and cleaned up.`);
|
|
2544
2442
|
console.log(`- WU: ${id} — ${title}`);
|
|
@@ -2555,7 +2453,11 @@ async function main() {
|
|
|
2555
2453
|
// WU-1983: Migration deployment nudge - only if supabase paths in code_paths
|
|
2556
2454
|
const codePaths = docMain.code_paths || [];
|
|
2557
2455
|
await printMigrationDeploymentNudge(codePaths, mainCheckoutPath);
|
|
2558
|
-
|
|
2456
|
+
const currentBranch = (await getGitForCwd().getCurrentBranch()).trim();
|
|
2457
|
+
const shouldRunCleanupMutations = currentBranch.length > 0 &&
|
|
2458
|
+
currentBranch !== BRANCHES.MAIN &&
|
|
2459
|
+
currentBranch !== BRANCHES.MASTER;
|
|
2460
|
+
if (shouldRunCleanupMutations) {
|
|
2559
2461
|
// WU-1366: Auto state cleanup after successful completion
|
|
2560
2462
|
// Non-fatal: errors are logged but do not block completion
|
|
2561
2463
|
await runAutoCleanupAfterDone(mainCheckoutPath);
|