@lumenflow/cli 2.17.0 → 2.18.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 +24 -23
- package/dist/gates.js +56 -6
- package/dist/gates.js.map +1 -1
- package/dist/hooks/enforcement-generator.js +3 -3
- package/dist/init.js +1 -1
- package/dist/orchestrate-initiative.js +4 -3
- package/dist/orchestrate-initiative.js.map +1 -1
- package/dist/orchestrate-monitor.js +2 -1
- package/dist/orchestrate-monitor.js.map +1 -1
- package/dist/public-manifest.js +11 -4
- package/dist/public-manifest.js.map +1 -1
- package/dist/spawn-list.js +1 -0
- package/dist/spawn-list.js.map +1 -1
- package/dist/wu-block.js +89 -45
- package/dist/wu-block.js.map +1 -1
- package/dist/wu-brief.js +40 -0
- package/dist/wu-brief.js.map +1 -0
- package/dist/wu-claim-cloud.js +61 -0
- package/dist/wu-claim-cloud.js.map +1 -0
- package/dist/wu-claim.js +166 -57
- package/dist/wu-claim.js.map +1 -1
- package/dist/wu-cleanup-cloud.js +76 -0
- package/dist/wu-cleanup-cloud.js.map +1 -0
- package/dist/wu-cleanup.js +42 -19
- package/dist/wu-cleanup.js.map +1 -1
- package/dist/wu-create-cloud.js +40 -0
- package/dist/wu-create-cloud.js.map +1 -0
- package/dist/wu-create.js +137 -53
- package/dist/wu-create.js.map +1 -1
- package/dist/wu-delegate.js +21 -0
- package/dist/wu-delegate.js.map +1 -0
- package/dist/wu-delete.js +102 -61
- package/dist/wu-delete.js.map +1 -1
- package/dist/wu-done-auto-cleanup.js +5 -16
- package/dist/wu-done-auto-cleanup.js.map +1 -1
- package/dist/wu-done-cloud.js +46 -0
- package/dist/wu-done-cloud.js.map +1 -0
- package/dist/wu-done.js +153 -44
- package/dist/wu-done.js.map +1 -1
- package/dist/wu-edit.js +217 -55
- package/dist/wu-edit.js.map +1 -1
- package/dist/wu-prep.js +1 -1
- package/dist/wu-prep.js.map +1 -1
- package/dist/wu-recover.js +128 -55
- package/dist/wu-recover.js.map +1 -1
- package/dist/wu-release.js +99 -45
- package/dist/wu-release.js.map +1 -1
- package/dist/wu-spawn.js +108 -42
- package/dist/wu-spawn.js.map +1 -1
- package/dist/wu-state-cloud.js +39 -0
- package/dist/wu-state-cloud.js.map +1 -0
- package/dist/wu-unblock.js +99 -49
- package/dist/wu-unblock.js.map +1 -1
- package/package.json +8 -7
- package/templates/core/.lumenflow/constraints.md.template +31 -4
- package/templates/core/LUMENFLOW.md.template +31 -9
- package/templates/core/ai/onboarding/agent-invocation-guide.md.template +21 -13
- package/templates/core/ai/onboarding/agent-safety-card.md.template +2 -2
- package/templates/core/ai/onboarding/docs-generation.md.template +1 -1
- package/templates/core/ai/onboarding/lumenflow-force-usage.md.template +1 -1
- package/templates/core/ai/onboarding/quick-ref-commands.md.template +235 -66
- package/templates/core/ai/onboarding/rapid-prototyping.md +2 -2
- package/templates/core/ai/onboarding/starting-prompt.md.template +124 -24
- package/templates/core/ai/onboarding/troubleshooting-wu-done.md.template +11 -0
- package/templates/core/ai/onboarding/vendor-support.md.template +58 -69
- package/templates/vendors/claude/.claude/skills/context-management/SKILL.md.template +3 -3
- package/templates/vendors/claude/.claude/skills/design-first/SKILL.md.template +151 -0
- package/templates/vendors/claude/.claude/skills/initiative-management/SKILL.md.template +8 -8
- package/templates/vendors/claude/.claude/skills/library-first/SKILL.md.template +1 -0
- package/templates/vendors/claude/.claude/skills/multi-agent-coordination/SKILL.md.template +5 -5
- package/templates/vendors/claude/.claude/skills/orchestration/SKILL.md.template +19 -16
- package/templates/vendors/claude/.claude/skills/tdd-workflow/SKILL.md.template +2 -0
package/dist/wu-claim.js
CHANGED
|
@@ -33,14 +33,16 @@ import { validateLaneCodePaths, logLaneValidationWarnings, } from '@lumenflow/co
|
|
|
33
33
|
// WU-1574: parseBacklogFrontmatter/getSectionHeadings removed - state store replaces backlog parsing
|
|
34
34
|
import { detectConflicts } from '@lumenflow/core/code-paths-overlap';
|
|
35
35
|
import { getGitForCwd, createGitForPath } from '@lumenflow/core/git-adapter';
|
|
36
|
-
import { die } from '@lumenflow/core/error-handler';
|
|
36
|
+
import { die, getErrorMessage } from '@lumenflow/core/error-handler';
|
|
37
37
|
import { createWUParser, WU_OPTIONS } from '@lumenflow/core/arg-parser';
|
|
38
38
|
// WU-1491: Mode resolution for --cloud and flag combinations
|
|
39
39
|
import { resolveClaimMode } from './wu-claim-mode.js';
|
|
40
|
+
// WU-1590: Cloud claim helpers for branch-pr/cloud execution behavior
|
|
41
|
+
import { shouldSkipBranchExistsCheck, resolveBranchClaimExecution } from './wu-claim-cloud.js';
|
|
40
42
|
// WU-1495: Cloud auto-detection from config-driven env signals
|
|
41
|
-
import { detectCloudMode } from '@lumenflow/core/cloud-detect';
|
|
43
|
+
import { detectCloudMode, resolveEffectiveCloudActivation, CLOUD_ACTIVATION_SOURCE, } from '@lumenflow/core/cloud-detect';
|
|
42
44
|
import { WU_PATHS, getStateStoreDirFromBacklog } from '@lumenflow/core/wu-paths';
|
|
43
|
-
import { BRANCHES, REMOTES, WU_STATUS, CLAIMED_MODES, STATUS_SECTIONS, PATTERNS, toKebab, LOG_PREFIX, GIT_REFS, MICRO_WORKTREE_OPERATIONS, COMMIT_FORMATS, EMOJI, FILE_SYSTEM, STRING_LITERALS, LUMENFLOW_PATHS, } from '@lumenflow/core/wu-constants';
|
|
45
|
+
import { BRANCHES, REMOTES, WU_STATUS, CLAIMED_MODES, STATUS_SECTIONS, PATTERNS, toKebab, LOG_PREFIX, GIT_REFS, MICRO_WORKTREE_OPERATIONS, COMMIT_FORMATS, EMOJI, FILE_SYSTEM, STRING_LITERALS, LUMENFLOW_PATHS, resolveWUStatus, } from '@lumenflow/core/wu-constants';
|
|
44
46
|
import { withMicroWorktree } from '@lumenflow/core/micro-worktree';
|
|
45
47
|
import { ensureOnMain, ensureMainUpToDate } from '@lumenflow/core/wu-helpers';
|
|
46
48
|
import { emitWUFlowEvent } from '@lumenflow/core/telemetry';
|
|
@@ -90,7 +92,7 @@ async function surfaceUnreadSignalsForDisplay(baseDir) {
|
|
|
90
92
|
}
|
|
91
93
|
catch (err) {
|
|
92
94
|
// WU-1473 AC4: Fail-open - never block claim on memory errors
|
|
93
|
-
console.warn(`${PREFIX} Warning: Could not surface unread signals: ${err
|
|
95
|
+
console.warn(`${PREFIX} Warning: Could not surface unread signals: ${getErrorMessage(err)}`);
|
|
94
96
|
}
|
|
95
97
|
}
|
|
96
98
|
async function ensureCleanOrClaimOnlyWhenNoAuto() {
|
|
@@ -116,6 +118,23 @@ async function ensureCleanOrClaimOnlyWhenNoAuto() {
|
|
|
116
118
|
}
|
|
117
119
|
}
|
|
118
120
|
const PREFIX = LOG_PREFIX.CLAIM;
|
|
121
|
+
/**
|
|
122
|
+
* Resolve branch-aware cloud activation for wu:claim.
|
|
123
|
+
*
|
|
124
|
+
* This preserves source attribution from detectCloudMode while enforcing
|
|
125
|
+
* protected-branch behavior for explicit vs env-signal activation.
|
|
126
|
+
*/
|
|
127
|
+
export function resolveCloudActivationForClaim(input) {
|
|
128
|
+
const detection = detectCloudMode({
|
|
129
|
+
cloudFlag: input.cloudFlag,
|
|
130
|
+
env: input.env,
|
|
131
|
+
config: input.config,
|
|
132
|
+
});
|
|
133
|
+
return resolveEffectiveCloudActivation({
|
|
134
|
+
detection,
|
|
135
|
+
currentBranch: input.currentBranch,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
119
138
|
/**
|
|
120
139
|
* WU-1508: Enforce tests.manual at claim time for non-doc/process WUs.
|
|
121
140
|
* This is non-bypassable (independent of --allow-incomplete) to fail early.
|
|
@@ -133,6 +152,27 @@ export function validateManualTestsForClaim(doc, id) {
|
|
|
133
152
|
`Add at least one manual verification step under tests.manual before claiming.`,
|
|
134
153
|
};
|
|
135
154
|
}
|
|
155
|
+
export function resolveClaimStatus(status) {
|
|
156
|
+
return resolveWUStatus(status, WU_STATUS.READY);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Decide whether wu:claim should update canonical state on origin/main.
|
|
160
|
+
*
|
|
161
|
+
* Cloud branch-pr claims run on platform-managed branches and should not mutate
|
|
162
|
+
* canonical state on main during claim; they commit claim metadata on their own branch.
|
|
163
|
+
*/
|
|
164
|
+
export function shouldApplyCanonicalClaimUpdate(input) {
|
|
165
|
+
if (input.noPush) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
return !(input.isCloud && input.claimedMode === CLAIMED_MODES.BRANCH_PR);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Decide whether wu:claim should write claim metadata directly to the active branch.
|
|
172
|
+
*/
|
|
173
|
+
export function shouldPersistClaimMetadataOnBranch(input) {
|
|
174
|
+
return input.noPush === true || input.claimedMode === CLAIMED_MODES.BRANCH_PR;
|
|
175
|
+
}
|
|
136
176
|
/**
|
|
137
177
|
* WU-1521: Build a rolled-back version of a WU YAML doc by stripping claim metadata.
|
|
138
178
|
*
|
|
@@ -152,6 +192,7 @@ export function buildRollbackYamlDoc(doc) {
|
|
|
152
192
|
rolled.status = WU_STATUS.READY;
|
|
153
193
|
// Remove claim-specific metadata fields
|
|
154
194
|
delete rolled.claimed_mode;
|
|
195
|
+
delete rolled.claimed_branch; // WU-1589: Clear claimed_branch on rollback
|
|
155
196
|
delete rolled.claimed_at;
|
|
156
197
|
delete rolled.worktree_path;
|
|
157
198
|
delete rolled.baseline_main_sha;
|
|
@@ -181,7 +222,7 @@ function preflightValidateWU(WU_PATH, id) {
|
|
|
181
222
|
}
|
|
182
223
|
catch (e) {
|
|
183
224
|
die(`Failed to parse WU YAML ${WU_PATH}\n\n` +
|
|
184
|
-
`YAML parsing error: ${e
|
|
225
|
+
`YAML parsing error: ${getErrorMessage(e)}\n\n` +
|
|
185
226
|
`Fix the YAML syntax errors before claiming.`);
|
|
186
227
|
}
|
|
187
228
|
// Validate ID matches
|
|
@@ -192,7 +233,7 @@ function preflightValidateWU(WU_PATH, id) {
|
|
|
192
233
|
`Fix the id field in the WU YAML before claiming.`);
|
|
193
234
|
}
|
|
194
235
|
// Validate state transition is allowed
|
|
195
|
-
const currentStatus = doc.status
|
|
236
|
+
const currentStatus = resolveClaimStatus(doc.status);
|
|
196
237
|
try {
|
|
197
238
|
assertTransition(currentStatus, WU_STATUS.IN_PROGRESS, id);
|
|
198
239
|
}
|
|
@@ -200,7 +241,7 @@ function preflightValidateWU(WU_PATH, id) {
|
|
|
200
241
|
die(`Cannot claim ${id} - invalid state transition\n\n` +
|
|
201
242
|
`Current status: ${currentStatus}\n` +
|
|
202
243
|
`Attempted transition: ${currentStatus} → in_progress\n\n` +
|
|
203
|
-
`Reason: ${error
|
|
244
|
+
`Reason: ${getErrorMessage(error)}`);
|
|
204
245
|
}
|
|
205
246
|
return doc;
|
|
206
247
|
}
|
|
@@ -252,7 +293,7 @@ function validateYAMLSchema(WU_PATH, doc, args) {
|
|
|
252
293
|
}
|
|
253
294
|
// WU-1576: validateBacklogConsistency removed - repair now happens inside micro-worktree
|
|
254
295
|
// See claimWorktreeMode() execute function for the new location
|
|
255
|
-
async function updateWUYaml(WU_PATH, id, lane, claimedMode = 'worktree', worktreePath = null, sessionId = null, gitAdapter = null) {
|
|
296
|
+
async function updateWUYaml(WU_PATH, id, lane, claimedMode = 'worktree', worktreePath = null, sessionId = null, gitAdapter = null, claimedBranch = null) {
|
|
256
297
|
// Check file exists
|
|
257
298
|
try {
|
|
258
299
|
await access(WU_PATH);
|
|
@@ -270,7 +311,7 @@ async function updateWUYaml(WU_PATH, id, lane, claimedMode = 'worktree', worktre
|
|
|
270
311
|
}
|
|
271
312
|
catch (e) {
|
|
272
313
|
die(`Failed to read WU file: ${WU_PATH}\n\n` +
|
|
273
|
-
`Error: ${e
|
|
314
|
+
`Error: ${getErrorMessage(e)}\n\n` +
|
|
274
315
|
`Options:\n` +
|
|
275
316
|
` 1. Check file permissions: ls -la ${WU_PATH}\n` +
|
|
276
317
|
` 2. Ensure you have read access to the repository`);
|
|
@@ -281,7 +322,7 @@ async function updateWUYaml(WU_PATH, id, lane, claimedMode = 'worktree', worktre
|
|
|
281
322
|
}
|
|
282
323
|
catch (e) {
|
|
283
324
|
die(`Failed to parse YAML ${WU_PATH}\n\n` +
|
|
284
|
-
`Error: ${e
|
|
325
|
+
`Error: ${getErrorMessage(e)}\n\n` +
|
|
285
326
|
`Options:\n` +
|
|
286
327
|
` 1. Validate YAML syntax: pnpm wu:validate --id ${id}\n` +
|
|
287
328
|
` 2. Fix YAML errors manually and retry`);
|
|
@@ -293,12 +334,12 @@ async function updateWUYaml(WU_PATH, id, lane, claimedMode = 'worktree', worktre
|
|
|
293
334
|
` 2. Verify you're claiming the right WU`);
|
|
294
335
|
}
|
|
295
336
|
// Validate state transition before updating
|
|
296
|
-
const currentStatus = doc.status
|
|
337
|
+
const currentStatus = resolveClaimStatus(doc.status);
|
|
297
338
|
try {
|
|
298
339
|
assertTransition(currentStatus, WU_STATUS.IN_PROGRESS, id);
|
|
299
340
|
}
|
|
300
341
|
catch (error) {
|
|
301
|
-
die(`State transition validation failed: ${error
|
|
342
|
+
die(`State transition validation failed: ${getErrorMessage(error)}`);
|
|
302
343
|
}
|
|
303
344
|
// Update status and lane (lane only if provided and different)
|
|
304
345
|
doc.status = WU_STATUS.IN_PROGRESS;
|
|
@@ -306,6 +347,11 @@ async function updateWUYaml(WU_PATH, id, lane, claimedMode = 'worktree', worktre
|
|
|
306
347
|
doc.lane = lane;
|
|
307
348
|
// Record claimed mode (worktree or branch-only)
|
|
308
349
|
doc.claimed_mode = claimedMode;
|
|
350
|
+
// WU-1590: Persist claimed_branch for branch-pr cloud agents so downstream commands
|
|
351
|
+
// (wu:prep, wu:done, wu:cleanup) can resolve the actual branch via defaultBranchFrom()
|
|
352
|
+
if (claimedBranch) {
|
|
353
|
+
doc.claimed_branch = claimedBranch;
|
|
354
|
+
}
|
|
309
355
|
// WU-1226: Record worktree path to prevent resolution failures if lane field changes
|
|
310
356
|
if (worktreePath) {
|
|
311
357
|
doc.worktree_path = worktreePath;
|
|
@@ -386,7 +432,7 @@ async function maybeProgressInitiativeStatus(worktreePath, initiativeRef, wuId)
|
|
|
386
432
|
}
|
|
387
433
|
catch (error) {
|
|
388
434
|
// Non-fatal: log warning and continue
|
|
389
|
-
console.warn(`${PREFIX} ⚠️ Could not check initiative status progression: ${error
|
|
435
|
+
console.warn(`${PREFIX} ⚠️ Could not check initiative status progression: ${getErrorMessage(error)}`);
|
|
390
436
|
return { updated: false, initPath: null };
|
|
391
437
|
}
|
|
392
438
|
}
|
|
@@ -526,7 +572,8 @@ async function applyStagedChangesToMicroWorktree(worktreePath, stagedChanges) {
|
|
|
526
572
|
* Ensures canonical state stays global while local main remains unchanged.
|
|
527
573
|
*/
|
|
528
574
|
async function applyCanonicalClaimUpdate(ctx, sessionId) {
|
|
529
|
-
const { args, id, laneK, worktree, WU_PATH, STATUS_PATH, BACKLOG_PATH, claimedMode, fixableIssues, stagedChanges,
|
|
575
|
+
const { args, id, laneK, worktree, WU_PATH, STATUS_PATH, BACKLOG_PATH, claimedMode, fixableIssues, stagedChanges, currentBranchForCloud, // WU-1590: For persisting claimed_branch
|
|
576
|
+
} = ctx;
|
|
530
577
|
const commitMsg = COMMIT_FORMATS.CLAIM(id.toLowerCase(), laneK);
|
|
531
578
|
const worktreePathForYaml = claimedMode === CLAIMED_MODES.BRANCH_ONLY ? null : path.resolve(worktree);
|
|
532
579
|
let updatedTitle = '';
|
|
@@ -554,7 +601,7 @@ async function applyCanonicalClaimUpdate(ctx, sessionId) {
|
|
|
554
601
|
}
|
|
555
602
|
const microGit = createGitForPath(worktreePath);
|
|
556
603
|
// WU-1211: updateWUYaml now returns {title, initiative}
|
|
557
|
-
const updateResult = await updateWUYaml(microWUPath, id, args.lane, claimedMode, worktreePathForYaml, sessionId, microGit);
|
|
604
|
+
const updateResult = await updateWUYaml(microWUPath, id, args.lane, claimedMode, worktreePathForYaml, sessionId, microGit, currentBranchForCloud || null);
|
|
558
605
|
updatedTitle = updateResult.title || updatedTitle;
|
|
559
606
|
await addOrReplaceInProgressStatus(microStatusPath, id, updatedTitle);
|
|
560
607
|
await removeFromReadyAndAddToInProgressBacklog(microBacklogPath, id, updatedTitle, args.lane);
|
|
@@ -790,7 +837,7 @@ function validateLaneFormatWithError(lane) {
|
|
|
790
837
|
validateLaneFormat(lane);
|
|
791
838
|
}
|
|
792
839
|
catch (error) {
|
|
793
|
-
die(`Invalid lane format: ${error
|
|
840
|
+
die(`Invalid lane format: ${getErrorMessage(error)}\n\n` +
|
|
794
841
|
`Valid formats:\n` +
|
|
795
842
|
` - Parent-only: "Operations", "Intelligence", "Experience", etc.\n` +
|
|
796
843
|
` - Sub-lane: "Operations: Tooling", "Intelligence: Prompts", etc.\n\n` +
|
|
@@ -917,28 +964,42 @@ async function validateBranchOnlyMode(STATUS_PATH, id) {
|
|
|
917
964
|
* Execute branch-only mode claim workflow
|
|
918
965
|
*/
|
|
919
966
|
async function claimBranchOnlyMode(ctx) {
|
|
920
|
-
const { args, id, laneK, title, branch, WU_PATH, STATUS_PATH, BACKLOG_PATH, claimedMode, sessionId, updatedTitle,
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
967
|
+
const { args, id, laneK, title, branch, WU_PATH, STATUS_PATH, BACKLOG_PATH, claimedMode, shouldCreateBranch, currentBranch, sessionId, updatedTitle, currentBranchForCloud, // WU-1590: For persisting claimed_branch
|
|
968
|
+
} = ctx;
|
|
969
|
+
if (shouldCreateBranch) {
|
|
970
|
+
// Create branch and switch to it from origin/main (avoids local main mutation)
|
|
971
|
+
try {
|
|
972
|
+
await getGitForCwd().createBranch(branch, `${REMOTES.ORIGIN}/${BRANCHES.MAIN}`);
|
|
973
|
+
}
|
|
974
|
+
catch (error) {
|
|
975
|
+
die(`Canonical claim state may be updated, but branch creation failed.\n\n` +
|
|
976
|
+
`Error: ${getErrorMessage(error)}\n\n` +
|
|
977
|
+
`Recovery:\n` +
|
|
978
|
+
` 1. Run: git fetch ${REMOTES.ORIGIN} ${BRANCHES.MAIN}\n` +
|
|
979
|
+
` 2. Retry: pnpm wu:claim --id ${id} --lane "${args.lane}"\n` +
|
|
980
|
+
` 3. If needed, delete local branch: git branch -D ${branch}`);
|
|
981
|
+
}
|
|
924
982
|
}
|
|
925
|
-
|
|
926
|
-
die(`
|
|
927
|
-
`
|
|
928
|
-
`
|
|
929
|
-
`
|
|
930
|
-
` 2. Retry: pnpm wu:claim --id ${id} --lane "${args.lane}"\n` +
|
|
931
|
-
` 3. If needed, delete local branch: git branch -D ${branch}`);
|
|
983
|
+
else if (currentBranch !== branch) {
|
|
984
|
+
die(`Cloud branch-pr claim must run on the active branch.\n\n` +
|
|
985
|
+
`Current branch: ${currentBranch}\n` +
|
|
986
|
+
`Resolved branch: ${branch}\n\n` +
|
|
987
|
+
`Switch to ${branch} and retry, or omit conflicting --branch flags.`);
|
|
932
988
|
}
|
|
933
989
|
let finalTitle = updatedTitle || title;
|
|
934
990
|
const msg = COMMIT_FORMATS.CLAIM(id.toLowerCase(), laneK);
|
|
935
|
-
|
|
991
|
+
const shouldPersistClaimMetadata = shouldPersistClaimMetadataOnBranch({
|
|
992
|
+
claimedMode,
|
|
993
|
+
noPush: Boolean(args.noPush),
|
|
994
|
+
});
|
|
995
|
+
if (shouldPersistClaimMetadata) {
|
|
936
996
|
if (args.noAuto) {
|
|
937
997
|
await ensureCleanOrClaimOnlyWhenNoAuto();
|
|
938
998
|
}
|
|
939
999
|
else {
|
|
940
1000
|
// WU-1211: updateWUYaml now returns {title, initiative}
|
|
941
|
-
|
|
1001
|
+
// WU-1590: Pass claimed_branch for branch-pr persistence
|
|
1002
|
+
const updateResult = await updateWUYaml(WU_PATH, id, args.lane, claimedMode, null, sessionId, null, currentBranchForCloud || null);
|
|
942
1003
|
finalTitle = updateResult.title || finalTitle;
|
|
943
1004
|
await addOrReplaceInProgressStatus(STATUS_PATH, id, finalTitle);
|
|
944
1005
|
await removeFromReadyAndAddToInProgressBacklog(BACKLOG_PATH, id, finalTitle, args.lane);
|
|
@@ -950,9 +1011,11 @@ async function claimBranchOnlyMode(ctx) {
|
|
|
950
1011
|
filesToAdd.push(initProgress.initPath);
|
|
951
1012
|
}
|
|
952
1013
|
}
|
|
953
|
-
await getGitForCwd().add(filesToAdd
|
|
1014
|
+
await getGitForCwd().add(filesToAdd);
|
|
954
1015
|
}
|
|
955
1016
|
await getGitForCwd().commit(msg);
|
|
1017
|
+
}
|
|
1018
|
+
if (args.noPush) {
|
|
956
1019
|
console.warn(`${PREFIX} Warning: --no-push enabled. Claim is local-only and NOT visible to other agents.`);
|
|
957
1020
|
}
|
|
958
1021
|
else {
|
|
@@ -1323,6 +1386,33 @@ async function main() {
|
|
|
1323
1386
|
if (!PATTERNS.WU_ID.test(id))
|
|
1324
1387
|
die(`Invalid WU id '${args.id}'. Expected format WU-123`);
|
|
1325
1388
|
await ensureOnMain(getGitForCwd());
|
|
1389
|
+
// WU-1609: Resolve branch-aware cloud activation at preflight so explicit
|
|
1390
|
+
// protected-branch cloud requests fail before lane locking/state mutation.
|
|
1391
|
+
const preflightBranch = await getGitForCwd().getCurrentBranch();
|
|
1392
|
+
const preflightCloudEffective = resolveCloudActivationForClaim({
|
|
1393
|
+
cloudFlag: Boolean(args.cloud),
|
|
1394
|
+
env: process.env,
|
|
1395
|
+
config: getConfig().cloud,
|
|
1396
|
+
currentBranch: preflightBranch,
|
|
1397
|
+
});
|
|
1398
|
+
if (preflightCloudEffective.blocked) {
|
|
1399
|
+
const sourceHint = preflightCloudEffective.source === CLOUD_ACTIVATION_SOURCE.FLAG
|
|
1400
|
+
? '--cloud'
|
|
1401
|
+
: 'LUMENFLOW_CLOUD=1';
|
|
1402
|
+
die(`Cloud mode blocked on protected branch "${preflightBranch}".\n\n` +
|
|
1403
|
+
`Explicit cloud activation (${sourceHint}) is not allowed on main/master.\n` +
|
|
1404
|
+
`Switch to a non-main branch for cloud mode, or run wu:claim without cloud activation on main/master.`);
|
|
1405
|
+
}
|
|
1406
|
+
if (preflightCloudEffective.suppressed) {
|
|
1407
|
+
const signalSuffix = preflightCloudEffective.matchedSignal
|
|
1408
|
+
? ` (signal: ${preflightCloudEffective.matchedSignal})`
|
|
1409
|
+
: '';
|
|
1410
|
+
console.log(`${PREFIX} Cloud auto-detection suppressed on protected branch "${preflightBranch}"${signalSuffix}; continuing with standard claim flow.`);
|
|
1411
|
+
}
|
|
1412
|
+
else if (preflightCloudEffective.isCloud &&
|
|
1413
|
+
preflightCloudEffective.source === CLOUD_ACTIVATION_SOURCE.ENV_SIGNAL) {
|
|
1414
|
+
console.log(`${PREFIX} Cloud mode auto-detected (source: ${preflightCloudEffective.source}${preflightCloudEffective.matchedSignal ? `, signal: ${preflightCloudEffective.matchedSignal}` : ''})`);
|
|
1415
|
+
}
|
|
1326
1416
|
// WU-2411: Handle --resume flag for agent handoff
|
|
1327
1417
|
if (args.resume) {
|
|
1328
1418
|
await handleResumeMode(args, id);
|
|
@@ -1449,18 +1539,11 @@ async function main() {
|
|
|
1449
1539
|
const title = (await readWUTitle(id)) || '';
|
|
1450
1540
|
const branch = args.branch || `lane/${laneK}/${idK}`;
|
|
1451
1541
|
const worktree = args.worktree || `worktrees/${laneK}-${idK}`;
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
const
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
env: process.env,
|
|
1458
|
-
config: config.cloud,
|
|
1459
|
-
});
|
|
1460
|
-
const effectiveCloud = cloudDetection.isCloud;
|
|
1461
|
-
if (cloudDetection.isCloud && !args.cloud) {
|
|
1462
|
-
console.log(`${PREFIX} Cloud mode auto-detected (source: ${cloudDetection.source}${cloudDetection.matchedSignal ? `, signal: ${cloudDetection.matchedSignal}` : ''})`);
|
|
1463
|
-
}
|
|
1542
|
+
const currentBranch = preflightBranch;
|
|
1543
|
+
const cloudEffective = preflightCloudEffective;
|
|
1544
|
+
const effectiveCloud = cloudEffective.isCloud;
|
|
1545
|
+
// WU-1590: Capture current branch for cloud claim metadata (before any branch switching)
|
|
1546
|
+
const currentBranchForCloud = effectiveCloud ? currentBranch : undefined;
|
|
1464
1547
|
// WU-1491: Resolve claimed mode from flag combination
|
|
1465
1548
|
const modeResult = resolveClaimMode({
|
|
1466
1549
|
branchOnly: args.branchOnly,
|
|
@@ -1476,11 +1559,24 @@ async function main() {
|
|
|
1476
1559
|
if (!modeResult.skipBranchOnlySingletonGuard) {
|
|
1477
1560
|
await validateBranchOnlyMode(STATUS_PATH, id);
|
|
1478
1561
|
}
|
|
1562
|
+
// WU-1590: Skip branch-exists checks in cloud mode (branch already exists by definition)
|
|
1563
|
+
const branchExecution = resolveBranchClaimExecution({
|
|
1564
|
+
claimedMode,
|
|
1565
|
+
isCloud: effectiveCloud,
|
|
1566
|
+
currentBranch,
|
|
1567
|
+
requestedBranch: branch,
|
|
1568
|
+
});
|
|
1569
|
+
const effectiveBranch = branchExecution.executionBranch;
|
|
1570
|
+
const skipBranchChecks = shouldSkipBranchExistsCheck({
|
|
1571
|
+
isCloud: effectiveCloud,
|
|
1572
|
+
currentBranch,
|
|
1573
|
+
laneBranch: effectiveBranch,
|
|
1574
|
+
});
|
|
1479
1575
|
// Check if remote branch already exists (prevents duplicate global claims)
|
|
1480
|
-
if (!args.noPush) {
|
|
1481
|
-
const remoteExists = await getGitForCwd().remoteBranchExists(REMOTES.ORIGIN,
|
|
1576
|
+
if (!args.noPush && !skipBranchChecks) {
|
|
1577
|
+
const remoteExists = await getGitForCwd().remoteBranchExists(REMOTES.ORIGIN, effectiveBranch);
|
|
1482
1578
|
if (remoteExists) {
|
|
1483
|
-
die(`Remote branch ${REMOTES.ORIGIN}/${
|
|
1579
|
+
die(`Remote branch ${REMOTES.ORIGIN}/${effectiveBranch} already exists. WU may already be claimed.\n\n` +
|
|
1484
1580
|
`Options:\n` +
|
|
1485
1581
|
` 1. Coordinate with the owning agent or wait for completion\n` +
|
|
1486
1582
|
` 2. Choose a different WU\n` +
|
|
@@ -1488,14 +1584,16 @@ async function main() {
|
|
|
1488
1584
|
}
|
|
1489
1585
|
}
|
|
1490
1586
|
// Check if branch already exists locally (prevents duplicate claims)
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
`
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1587
|
+
if (!skipBranchChecks) {
|
|
1588
|
+
const branchAlreadyExists = await getGitForCwd().branchExists(effectiveBranch);
|
|
1589
|
+
if (branchAlreadyExists) {
|
|
1590
|
+
die(`Branch ${effectiveBranch} already exists. WU may already be claimed.\n\n` +
|
|
1591
|
+
`Git branch existence = WU claimed (natural locking).\n\n` +
|
|
1592
|
+
`Options:\n` +
|
|
1593
|
+
` 1. Check git worktree list to see if worktree exists\n` +
|
|
1594
|
+
` 2. Coordinate with the owning agent or wait for them to complete\n` +
|
|
1595
|
+
` 3. Choose a different WU`);
|
|
1596
|
+
}
|
|
1499
1597
|
}
|
|
1500
1598
|
// Layer 3 defense (WU-1476): Pre-flight orphan check
|
|
1501
1599
|
// Clean up orphan directory if it exists at target worktree path
|
|
@@ -1508,7 +1606,7 @@ async function main() {
|
|
|
1508
1606
|
}
|
|
1509
1607
|
catch (err) {
|
|
1510
1608
|
die(`Failed to clean up orphan directory at ${worktree}\n\n` +
|
|
1511
|
-
`Error: ${err
|
|
1609
|
+
`Error: ${getErrorMessage(err)}\n\n` +
|
|
1512
1610
|
`Manual cleanup: rm -rf ${absoluteWorktreePath}`);
|
|
1513
1611
|
}
|
|
1514
1612
|
}
|
|
@@ -1529,7 +1627,7 @@ async function main() {
|
|
|
1529
1627
|
}
|
|
1530
1628
|
catch (err) {
|
|
1531
1629
|
// Non-blocking: session start failure should not block claim
|
|
1532
|
-
console.warn(`${PREFIX} Warning: Could not start agent session: ${err
|
|
1630
|
+
console.warn(`${PREFIX} Warning: Could not start agent session: ${getErrorMessage(err)}`);
|
|
1533
1631
|
}
|
|
1534
1632
|
// Execute claim workflow
|
|
1535
1633
|
const baseCtx = {
|
|
@@ -1537,18 +1635,26 @@ async function main() {
|
|
|
1537
1635
|
id,
|
|
1538
1636
|
laneK,
|
|
1539
1637
|
title,
|
|
1540
|
-
branch,
|
|
1638
|
+
branch: effectiveBranch,
|
|
1541
1639
|
worktree,
|
|
1542
1640
|
WU_PATH,
|
|
1543
1641
|
STATUS_PATH,
|
|
1544
1642
|
BACKLOG_PATH,
|
|
1545
1643
|
claimedMode,
|
|
1644
|
+
shouldCreateBranch: branchExecution.shouldCreateBranch,
|
|
1645
|
+
currentBranch,
|
|
1546
1646
|
fixableIssues, // WU-1361: Pass fixable issues for worktree application
|
|
1547
1647
|
stagedChanges,
|
|
1648
|
+
currentBranchForCloud, // WU-1590: For persisting claimed_branch in branch-pr mode
|
|
1548
1649
|
};
|
|
1549
1650
|
let updatedTitle = title;
|
|
1550
1651
|
claimTitle = title;
|
|
1551
|
-
|
|
1652
|
+
const shouldApplyCanonicalUpdate = shouldApplyCanonicalClaimUpdate({
|
|
1653
|
+
isCloud: effectiveCloud,
|
|
1654
|
+
claimedMode,
|
|
1655
|
+
noPush: Boolean(args.noPush),
|
|
1656
|
+
});
|
|
1657
|
+
if (shouldApplyCanonicalUpdate) {
|
|
1552
1658
|
updatedTitle = (await applyCanonicalClaimUpdate(baseCtx, sessionId)) || updatedTitle;
|
|
1553
1659
|
// WU-1521: Mark that canonical claim was pushed to origin/main
|
|
1554
1660
|
// If claim fails after this point, the finally block will rollback
|
|
@@ -1557,6 +1663,9 @@ async function main() {
|
|
|
1557
1663
|
// Refresh origin/main after push-only update so worktrees start from canonical state
|
|
1558
1664
|
await getGitForCwd().fetch(REMOTES.ORIGIN, BRANCHES.MAIN);
|
|
1559
1665
|
}
|
|
1666
|
+
else if (!args.noPush && claimedMode === CLAIMED_MODES.BRANCH_PR) {
|
|
1667
|
+
console.log(`${PREFIX} Skipping canonical claim update on origin/main for cloud branch-pr claim.`);
|
|
1668
|
+
}
|
|
1560
1669
|
const ctx = {
|
|
1561
1670
|
...baseCtx,
|
|
1562
1671
|
sessionId,
|