@lumenflow/cli 3.21.0 → 3.22.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/dist/chunk-2D2VOCA4.js +37 -0
- package/dist/chunk-2D5KFYGX.js +284 -0
- package/dist/chunk-2GXVIN57.js +14072 -0
- package/dist/chunk-2MQ7HZWZ.js +26 -0
- package/dist/chunk-2UFQ3A3C.js +643 -0
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/chunk-4N74J3UT.js +15 -0
- package/dist/chunk-5GTOXFYR.js +392 -0
- package/dist/chunk-5VY6MQMC.js +240 -0
- package/dist/chunk-67XVPMRY.js +1297 -0
- package/dist/chunk-6HO4GWJE.js +164 -0
- package/dist/chunk-6W5XHWYV.js +1890 -0
- package/dist/chunk-6X4EMYJQ.js +64 -0
- package/dist/chunk-6XYXI2NQ.js +772 -0
- package/dist/chunk-7ANSOV6Q.js +285 -0
- package/dist/chunk-A624LFLB.js +1380 -0
- package/dist/chunk-ADN5NHG4.js +126 -0
- package/dist/chunk-B7YJYJKG.js +33 -0
- package/dist/chunk-CCLHCPKG.js +210 -0
- package/dist/chunk-CK36VROC.js +1584 -0
- package/dist/chunk-D3UOFRSB.js +81 -0
- package/dist/chunk-DFR4DJBM.js +230 -0
- package/dist/chunk-DSYBDHYH.js +79 -0
- package/dist/chunk-DWMLTXKQ.js +1176 -0
- package/dist/chunk-E3REJTAJ.js +28 -0
- package/dist/chunk-EA3IVO64.js +633 -0
- package/dist/chunk-EK2AKZKD.js +55 -0
- package/dist/chunk-ELD7JTTT.js +343 -0
- package/dist/chunk-EX6TT2XI.js +195 -0
- package/dist/chunk-EXINSFZE.js +82 -0
- package/dist/chunk-EZ6ZBYBM.js +510 -0
- package/dist/chunk-FBKAPTJ2.js +16 -0
- package/dist/chunk-FVLV5RYH.js +1118 -0
- package/dist/chunk-GDNSBQVK.js +2485 -0
- package/dist/chunk-GPQHMBNN.js +278 -0
- package/dist/chunk-GTFJB67L.js +68 -0
- package/dist/chunk-HANJXVKW.js +1127 -0
- package/dist/chunk-HEVS5YLD.js +269 -0
- package/dist/chunk-HMEVZKPQ.js +9 -0
- package/dist/chunk-HRGSYNLM.js +3511 -0
- package/dist/chunk-ISZR5N4K.js +60 -0
- package/dist/chunk-J6SUPR2C.js +226 -0
- package/dist/chunk-JERYVEIZ.js +244 -0
- package/dist/chunk-JHHWGL2N.js +87 -0
- package/dist/chunk-JONWQUB5.js +775 -0
- package/dist/chunk-K2DIWWDM.js +1766 -0
- package/dist/chunk-KY4PGL5V.js +969 -0
- package/dist/chunk-L737LQ4C.js +1285 -0
- package/dist/chunk-LFTWYIB2.js +497 -0
- package/dist/chunk-LV47RFNJ.js +41 -0
- package/dist/chunk-MKSAITI7.js +15 -0
- package/dist/chunk-MZ7RKIX4.js +212 -0
- package/dist/chunk-NAP6CFSO.js +84 -0
- package/dist/chunk-ND6MY37M.js +16 -0
- package/dist/chunk-NMG736UR.js +683 -0
- package/dist/chunk-NRAXROED.js +32 -0
- package/dist/chunk-NRIZR3A7.js +690 -0
- package/dist/chunk-NX43BG3M.js +233 -0
- package/dist/chunk-O645XLSI.js +297 -0
- package/dist/chunk-OMJD6A3S.js +235 -0
- package/dist/chunk-QB6SJD4T.js +430 -0
- package/dist/chunk-QFSTL4J3.js +276 -0
- package/dist/chunk-QLGDFMFX.js +212 -0
- package/dist/chunk-RIAAGL2E.js +13 -0
- package/dist/chunk-RWO5XMZ6.js +86 -0
- package/dist/chunk-RXRKBBSM.js +149 -0
- package/dist/chunk-RZOZMML6.js +363 -0
- package/dist/chunk-U7I7FS7T.js +113 -0
- package/dist/chunk-UI42RODY.js +717 -0
- package/dist/chunk-UTVMVSCO.js +519 -0
- package/dist/chunk-V6OJGLBA.js +1746 -0
- package/dist/chunk-W2JHVH7D.js +152 -0
- package/dist/chunk-WD3Y7VQN.js +280 -0
- package/dist/chunk-WOCTQ5MS.js +303 -0
- package/dist/chunk-WZR3ZUNN.js +696 -0
- package/dist/chunk-XGI665H7.js +150 -0
- package/dist/chunk-XKY65P2T.js +304 -0
- package/dist/chunk-Y4CQZY65.js +57 -0
- package/dist/chunk-YFEXKLVE.js +194 -0
- package/dist/chunk-YHO3HS5X.js +287 -0
- package/dist/chunk-YLS7AZSX.js +738 -0
- package/dist/chunk-ZE473AO6.js +49 -0
- package/dist/chunk-ZF747T3O.js +644 -0
- package/dist/chunk-ZHCZHZH3.js +43 -0
- package/dist/chunk-ZZNZX2XY.js +87 -0
- package/dist/constants-7QAP3VQ4.js +23 -0
- package/dist/dist-IY3UUMWK.js +33 -0
- package/dist/invariants-runner-W5RGHCSU.js +27 -0
- package/dist/lane-lock-6J36HD5O.js +35 -0
- package/dist/mem-checkpoint-core-EANG2GVN.js +14 -0
- package/dist/mem-signal-core-2LZ2WYHW.js +19 -0
- package/dist/memory-store-OLB5FO7K.js +18 -0
- package/dist/service-6BYCOCO5.js +13 -0
- package/dist/spawn-policy-resolver-NTSZYQ6R.js +17 -0
- package/dist/spawn-task-builder-R4E2BHSW.js +22 -0
- package/dist/wu-done-pr-WLFFFEPJ.js +25 -0
- package/dist/wu-done-validation-3J5E36FE.js +30 -0
- package/dist/wu-duplicate-id-detector-5S7JHELK.js +232 -0
- package/dist/wu-spawn-strategy-resolver.js +18 -5
- package/dist/wu-spawn-strategy-resolver.js.map +1 -1
- package/package.json +8 -8
- package/packs/agent-runtime/.turbo/turbo-build.log +1 -1
- package/packs/agent-runtime/.turbo/turbo-typecheck.log +4 -0
- package/packs/agent-runtime/package.json +1 -1
- package/packs/sidekick/.turbo/turbo-build.log +1 -1
- package/packs/sidekick/.turbo/turbo-test.log +12 -0
- package/packs/sidekick/.turbo/turbo-typecheck.log +4 -0
- package/packs/sidekick/package.json +1 -1
- package/packs/software-delivery/.turbo/turbo-build.log +1 -1
- package/packs/software-delivery/.turbo/turbo-typecheck.log +4 -0
- package/packs/software-delivery/package.json +1 -1
|
@@ -0,0 +1,1890 @@
|
|
|
1
|
+
import {
|
|
2
|
+
checkLaneLock
|
|
3
|
+
} from "./chunk-JONWQUB5.js";
|
|
4
|
+
import {
|
|
5
|
+
TEST_METHODOLOGY_HINTS,
|
|
6
|
+
classifyWork,
|
|
7
|
+
generateCodeCraftGuidance,
|
|
8
|
+
generateDesignContextSection,
|
|
9
|
+
generateEnforcementSummary,
|
|
10
|
+
generateMandatoryStandards,
|
|
11
|
+
generatePolicyBasedTestGuidance,
|
|
12
|
+
generateReadBeforeWriteDiscipline,
|
|
13
|
+
generateSelfReviewDirective
|
|
14
|
+
} from "./chunk-NMG736UR.js";
|
|
15
|
+
import {
|
|
16
|
+
INVARIANT_TYPES,
|
|
17
|
+
loadInvariants
|
|
18
|
+
} from "./chunk-EZ6ZBYBM.js";
|
|
19
|
+
import {
|
|
20
|
+
BRANCHES,
|
|
21
|
+
DIRECTORIES,
|
|
22
|
+
LUMENFLOW_PATHS,
|
|
23
|
+
REMOTES
|
|
24
|
+
} from "./chunk-DWMLTXKQ.js";
|
|
25
|
+
import {
|
|
26
|
+
WORKSPACE_CONFIG_FILE_NAME,
|
|
27
|
+
WORKSPACE_V2_KEYS,
|
|
28
|
+
WU_STATUS,
|
|
29
|
+
getConfig,
|
|
30
|
+
getDefaultConfig,
|
|
31
|
+
resolvePolicy
|
|
32
|
+
} from "./chunk-V6OJGLBA.js";
|
|
33
|
+
import {
|
|
34
|
+
ErrorCodes,
|
|
35
|
+
createError
|
|
36
|
+
} from "./chunk-RXRKBBSM.js";
|
|
37
|
+
|
|
38
|
+
// ../core/dist/wu-spawn-helpers.js
|
|
39
|
+
import { Command } from "commander";
|
|
40
|
+
|
|
41
|
+
// ../core/dist/delegation-registry-store.js
|
|
42
|
+
import fs from "fs/promises";
|
|
43
|
+
import path from "path";
|
|
44
|
+
|
|
45
|
+
// ../core/dist/delegation-registry-schema.js
|
|
46
|
+
import crypto from "crypto";
|
|
47
|
+
import { z } from "zod";
|
|
48
|
+
var DelegationStatus = {
|
|
49
|
+
PENDING: "pending",
|
|
50
|
+
COMPLETED: WU_STATUS.COMPLETED,
|
|
51
|
+
TIMEOUT: "timeout",
|
|
52
|
+
CRASHED: "crashed",
|
|
53
|
+
ESCALATED: "escalated"
|
|
54
|
+
};
|
|
55
|
+
var DELEGATION_STATUSES = [
|
|
56
|
+
DelegationStatus.PENDING,
|
|
57
|
+
DelegationStatus.COMPLETED,
|
|
58
|
+
DelegationStatus.TIMEOUT,
|
|
59
|
+
DelegationStatus.CRASHED,
|
|
60
|
+
DelegationStatus.ESCALATED
|
|
61
|
+
];
|
|
62
|
+
var DelegationIntent = {
|
|
63
|
+
DELEGATION: "delegation",
|
|
64
|
+
LEGACY_SPAWN: "legacy-spawn"
|
|
65
|
+
};
|
|
66
|
+
var DELEGATION_INTENTS = [
|
|
67
|
+
DelegationIntent.DELEGATION,
|
|
68
|
+
DelegationIntent.LEGACY_SPAWN
|
|
69
|
+
];
|
|
70
|
+
var DELEGATION_PATTERNS = {
|
|
71
|
+
DELEGATION_ID: /^dlg-[0-9a-f]{4}$/,
|
|
72
|
+
WU_ID: /^WU-\d+$/
|
|
73
|
+
};
|
|
74
|
+
var ERROR_MESSAGES = {
|
|
75
|
+
DELEGATION_ID: "Delegation ID must match pattern dlg-XXXX (e.g., dlg-a1b2)",
|
|
76
|
+
WU_ID: "WU ID must match pattern WU-XXX (e.g., WU-1000)",
|
|
77
|
+
LANE_REQUIRED: "Lane is required",
|
|
78
|
+
STATUS: `Status must be one of: ${DELEGATION_STATUSES.join(", ")}`,
|
|
79
|
+
INTENT: `Intent must be one of: ${DELEGATION_INTENTS.join(", ")}`,
|
|
80
|
+
TIMESTAMP_REQUIRED: "Timestamp is required",
|
|
81
|
+
PICKUP_BY_REQUIRED: "pickedUpBy must be a non-empty string when pickup is recorded",
|
|
82
|
+
BRIEF_HASH: "briefAttestation.promptHash must be a 64-character lowercase sha256 hex digest",
|
|
83
|
+
BRIEF_ALGORITHM: "briefAttestation.algorithm must be sha256",
|
|
84
|
+
BRIEF_CLIENT_REQUIRED: "briefAttestation.clientName must be a non-empty string"
|
|
85
|
+
};
|
|
86
|
+
var SHA256_HEX_256_REGEX = /^[a-f0-9]{64}$/;
|
|
87
|
+
var DelegationBriefAttestationSchema = z.object({
|
|
88
|
+
algorithm: z.literal("sha256", { error: ERROR_MESSAGES.BRIEF_ALGORITHM }),
|
|
89
|
+
promptHash: z.string().regex(SHA256_HEX_256_REGEX, { message: ERROR_MESSAGES.BRIEF_HASH }),
|
|
90
|
+
promptLength: z.number().int().nonnegative(),
|
|
91
|
+
generatedAt: z.string().datetime({ message: ERROR_MESSAGES.TIMESTAMP_REQUIRED }),
|
|
92
|
+
clientName: z.string().min(1, { message: ERROR_MESSAGES.BRIEF_CLIENT_REQUIRED })
|
|
93
|
+
});
|
|
94
|
+
var DelegationEventSchema = z.object({
|
|
95
|
+
id: z.string().regex(DELEGATION_PATTERNS.DELEGATION_ID, {
|
|
96
|
+
message: ERROR_MESSAGES.DELEGATION_ID
|
|
97
|
+
}),
|
|
98
|
+
parentWuId: z.string().regex(DELEGATION_PATTERNS.WU_ID, { message: ERROR_MESSAGES.WU_ID }),
|
|
99
|
+
targetWuId: z.string().regex(DELEGATION_PATTERNS.WU_ID, { message: ERROR_MESSAGES.WU_ID }),
|
|
100
|
+
lane: z.string().min(1, { message: ERROR_MESSAGES.LANE_REQUIRED }),
|
|
101
|
+
intent: z.enum(DELEGATION_INTENTS, { error: ERROR_MESSAGES.INTENT }).optional(),
|
|
102
|
+
delegatedAt: z.string().datetime({ message: ERROR_MESSAGES.TIMESTAMP_REQUIRED }),
|
|
103
|
+
status: z.enum(DELEGATION_STATUSES, { error: ERROR_MESSAGES.STATUS }),
|
|
104
|
+
completedAt: z.string().datetime().nullable(),
|
|
105
|
+
pickedUpAt: z.string().datetime().optional(),
|
|
106
|
+
pickedUpBy: z.string().min(1, { message: ERROR_MESSAGES.PICKUP_BY_REQUIRED }).optional(),
|
|
107
|
+
briefAttestation: DelegationBriefAttestationSchema.optional()
|
|
108
|
+
});
|
|
109
|
+
function validateDelegationEvent(data) {
|
|
110
|
+
return DelegationEventSchema.safeParse(data);
|
|
111
|
+
}
|
|
112
|
+
function generateDelegationId(parentWuId, targetWuId) {
|
|
113
|
+
const timestamp = Date.now().toString();
|
|
114
|
+
const randomBytes = crypto.randomBytes(4).toString("hex");
|
|
115
|
+
const input = `${parentWuId}:${targetWuId}:${timestamp}:${randomBytes}`;
|
|
116
|
+
const hash = crypto.createHash("sha256").update(input).digest("hex");
|
|
117
|
+
return `dlg-${hash.slice(0, 4)}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ../core/dist/delegation-registry-store.js
|
|
121
|
+
var DELEGATION_REGISTRY_FILE_NAME = "delegation-registry.jsonl";
|
|
122
|
+
var DelegationRegistryStore = class {
|
|
123
|
+
baseDir;
|
|
124
|
+
registryFilePath;
|
|
125
|
+
delegations;
|
|
126
|
+
byParent;
|
|
127
|
+
byTarget;
|
|
128
|
+
/**
|
|
129
|
+
* @param {string} baseDir - Directory containing .lumenflow/state/
|
|
130
|
+
*/
|
|
131
|
+
constructor(baseDir) {
|
|
132
|
+
this.baseDir = baseDir;
|
|
133
|
+
this.registryFilePath = path.join(baseDir, DELEGATION_REGISTRY_FILE_NAME);
|
|
134
|
+
this.delegations = /* @__PURE__ */ new Map();
|
|
135
|
+
this.byParent = /* @__PURE__ */ new Map();
|
|
136
|
+
this.byTarget = /* @__PURE__ */ new Map();
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Loads and replays events from JSONL file into current state.
|
|
140
|
+
*
|
|
141
|
+
* Handles:
|
|
142
|
+
* - Missing file: returns empty state
|
|
143
|
+
* - Empty file: returns empty state
|
|
144
|
+
* - Empty lines: skipped gracefully
|
|
145
|
+
* - Malformed JSON: throws error with line info
|
|
146
|
+
* - Invalid events: throws validation error
|
|
147
|
+
*
|
|
148
|
+
* @returns {Promise<void>}
|
|
149
|
+
* @throws {Error} If file contains malformed JSON or invalid events
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* const store = new DelegationRegistryStore('/path/to/project');
|
|
153
|
+
* await store.load();
|
|
154
|
+
* const pending = store.getPendingDelegations();
|
|
155
|
+
*/
|
|
156
|
+
async load() {
|
|
157
|
+
this.delegations.clear();
|
|
158
|
+
this.byParent.clear();
|
|
159
|
+
this.byTarget.clear();
|
|
160
|
+
let content;
|
|
161
|
+
try {
|
|
162
|
+
content = await fs.readFile(this.registryFilePath, "utf-8");
|
|
163
|
+
} catch (error) {
|
|
164
|
+
if (error.code === "ENOENT") {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
const lines = content.split("\n");
|
|
170
|
+
for (let i = 0; i < lines.length; i++) {
|
|
171
|
+
const rawLine = lines[i];
|
|
172
|
+
if (typeof rawLine !== "string") {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
const line = rawLine.trim();
|
|
176
|
+
if (!line) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
let parsed;
|
|
180
|
+
try {
|
|
181
|
+
parsed = JSON.parse(line);
|
|
182
|
+
} catch (error) {
|
|
183
|
+
throw createError(ErrorCodes.PARSE_ERROR, `Malformed JSON on line ${i + 1}: ${error.message}`);
|
|
184
|
+
}
|
|
185
|
+
const validation = validateDelegationEvent(parsed);
|
|
186
|
+
if (!validation.success) {
|
|
187
|
+
const issues = validation.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join(", ");
|
|
188
|
+
throw createError(ErrorCodes.VALIDATION_ERROR, `Validation error on line ${i + 1}: ${issues}`);
|
|
189
|
+
}
|
|
190
|
+
const event = validation.data;
|
|
191
|
+
this._applyEvent(event);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Applies an event to the in-memory state.
|
|
196
|
+
* If event for same delegation ID exists, updates it (latest wins).
|
|
197
|
+
*
|
|
198
|
+
* @private
|
|
199
|
+
*/
|
|
200
|
+
_applyEvent(event) {
|
|
201
|
+
const { id, parentWuId, targetWuId } = event;
|
|
202
|
+
this.delegations.set(id, event);
|
|
203
|
+
if (!this.byParent.has(parentWuId)) {
|
|
204
|
+
this.byParent.set(parentWuId, []);
|
|
205
|
+
}
|
|
206
|
+
const parentDelegations = this.byParent.get(parentWuId);
|
|
207
|
+
if (!parentDelegations) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (!parentDelegations.includes(id)) {
|
|
211
|
+
parentDelegations.push(id);
|
|
212
|
+
}
|
|
213
|
+
this.byTarget.set(targetWuId, id);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Appends an event to the registry file.
|
|
217
|
+
*
|
|
218
|
+
* Uses append mode to avoid full file rewrite.
|
|
219
|
+
* Creates file and parent directories if they don't exist.
|
|
220
|
+
* Validates event before appending.
|
|
221
|
+
*
|
|
222
|
+
* @private
|
|
223
|
+
* @throws {Error} If event fails validation
|
|
224
|
+
*/
|
|
225
|
+
async _appendEvent(event) {
|
|
226
|
+
const validation = validateDelegationEvent(event);
|
|
227
|
+
if (!validation.success) {
|
|
228
|
+
const issues = validation.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join(", ");
|
|
229
|
+
throw createError(ErrorCodes.VALIDATION_ERROR, `Validation error: ${issues}`);
|
|
230
|
+
}
|
|
231
|
+
const line = JSON.stringify(event) + "\n";
|
|
232
|
+
await fs.mkdir(this.baseDir, { recursive: true });
|
|
233
|
+
await fs.appendFile(this.registryFilePath, line, "utf-8");
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Records a new delegation event with pending status.
|
|
237
|
+
*
|
|
238
|
+
* @param {string} parentWuId - Parent WU ID (orchestrator)
|
|
239
|
+
* @param {string} targetWuId - Target WU ID (delegated work)
|
|
240
|
+
* @param {string} lane - Lane for the delegated work
|
|
241
|
+
* @param {DelegationIntentValue} [intent] - Optional intent source (e.g., delegation)
|
|
242
|
+
* @returns {Promise<string>} The generated delegation ID
|
|
243
|
+
* @throws {Error} If validation fails
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* const delegationId = await store.record('WU-1000', 'WU-1001', 'Operations: Tooling');
|
|
247
|
+
*/
|
|
248
|
+
async record(parentWuId, targetWuId, lane, intent, briefAttestation) {
|
|
249
|
+
const id = generateDelegationId(parentWuId, targetWuId);
|
|
250
|
+
const event = {
|
|
251
|
+
id,
|
|
252
|
+
parentWuId,
|
|
253
|
+
targetWuId,
|
|
254
|
+
lane,
|
|
255
|
+
...intent ? { intent } : {},
|
|
256
|
+
delegatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
257
|
+
status: DelegationStatus.PENDING,
|
|
258
|
+
completedAt: null,
|
|
259
|
+
...briefAttestation ? { briefAttestation } : {}
|
|
260
|
+
};
|
|
261
|
+
await this._appendEvent(event);
|
|
262
|
+
this._applyEvent(event);
|
|
263
|
+
return id;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Updates the status of a delegation.
|
|
267
|
+
*
|
|
268
|
+
* @param {string} delegationId - Delegation ID to update
|
|
269
|
+
* @param {string} status - New status
|
|
270
|
+
* @returns {Promise<void>}
|
|
271
|
+
* @throws {Error} If delegation ID not found
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* await store.updateStatus('dlg-a1b2', 'completed');
|
|
275
|
+
*/
|
|
276
|
+
async updateStatus(delegationId, status) {
|
|
277
|
+
const existing = this.delegations.get(delegationId);
|
|
278
|
+
if (!existing) {
|
|
279
|
+
throw createError(ErrorCodes.DELEGATION_NOT_FOUND, `Delegation ID ${delegationId} not found`);
|
|
280
|
+
}
|
|
281
|
+
const event = {
|
|
282
|
+
...existing,
|
|
283
|
+
status,
|
|
284
|
+
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
285
|
+
};
|
|
286
|
+
await this._appendEvent(event);
|
|
287
|
+
this._applyEvent(event);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Records claim-time pickup evidence for a delegation entry.
|
|
291
|
+
*
|
|
292
|
+
* WU-1605: This distinguishes intent-only delegation records from
|
|
293
|
+
* delegated work that was actually picked up via wu:claim.
|
|
294
|
+
*
|
|
295
|
+
* @param {string} delegationId - Delegation ID to update
|
|
296
|
+
* @param {string} pickedUpBy - Agent identity that claimed the target WU
|
|
297
|
+
* @param {string} [pickedUpAt] - Optional ISO timestamp (defaults to now)
|
|
298
|
+
* @returns {Promise<void>}
|
|
299
|
+
* @throws {Error} If delegation ID not found
|
|
300
|
+
*/
|
|
301
|
+
async recordPickup(delegationId, pickedUpBy, pickedUpAt) {
|
|
302
|
+
const existing = this.delegations.get(delegationId);
|
|
303
|
+
if (!existing) {
|
|
304
|
+
throw createError(ErrorCodes.DELEGATION_NOT_FOUND, `Delegation ID ${delegationId} not found`);
|
|
305
|
+
}
|
|
306
|
+
const event = {
|
|
307
|
+
...existing,
|
|
308
|
+
pickedUpBy,
|
|
309
|
+
pickedUpAt: pickedUpAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
310
|
+
};
|
|
311
|
+
await this._appendEvent(event);
|
|
312
|
+
this._applyEvent(event);
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Gets all delegations for a parent WU.
|
|
316
|
+
*
|
|
317
|
+
* @param {string} parentWuId - Parent WU ID
|
|
318
|
+
* @returns {DelegationEvent[]} Array of delegation events
|
|
319
|
+
*
|
|
320
|
+
* @example
|
|
321
|
+
* const delegations = store.getByParent('WU-1000');
|
|
322
|
+
*/
|
|
323
|
+
getByParent(parentWuId) {
|
|
324
|
+
const delegationIds = this.byParent.get(parentWuId) ?? [];
|
|
325
|
+
return delegationIds.map((id) => this.delegations.get(id)).filter((event) => event !== void 0);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Gets delegation for a target WU.
|
|
329
|
+
*
|
|
330
|
+
* @param {string} targetWuId - Target WU ID
|
|
331
|
+
* @returns {DelegationEvent | null} Delegation event or null
|
|
332
|
+
*
|
|
333
|
+
* @example
|
|
334
|
+
* const delegation = store.getByTarget('WU-1001');
|
|
335
|
+
*/
|
|
336
|
+
getByTarget(targetWuId) {
|
|
337
|
+
const delegationId = this.byTarget.get(targetWuId);
|
|
338
|
+
if (!delegationId) {
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
return this.delegations.get(delegationId) ?? null;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Gets all pending delegations.
|
|
345
|
+
*
|
|
346
|
+
* @returns {DelegationEvent[]} Array of pending delegation events
|
|
347
|
+
*
|
|
348
|
+
* @example
|
|
349
|
+
* const pending = store.getPendingDelegations();
|
|
350
|
+
*/
|
|
351
|
+
getPendingDelegations() {
|
|
352
|
+
return Array.from(this.delegations.values()).filter((delegation) => delegation.status === DelegationStatus.PENDING);
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Gets all delegations as an array.
|
|
356
|
+
*
|
|
357
|
+
* @returns {DelegationEvent[]} Array of all delegation events
|
|
358
|
+
*
|
|
359
|
+
* @example
|
|
360
|
+
* const allDelegations = store.getAllDelegations();
|
|
361
|
+
*/
|
|
362
|
+
getAllDelegations() {
|
|
363
|
+
return Array.from(this.delegations.values());
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Gets delegation by ID.
|
|
367
|
+
*
|
|
368
|
+
* @param {string} delegationId - Delegation ID
|
|
369
|
+
* @returns {DelegationEvent | null} Delegation event or null
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* const delegation = store.getById('dlg-a1b2');
|
|
373
|
+
*/
|
|
374
|
+
getById(delegationId) {
|
|
375
|
+
return this.delegations.get(delegationId) ?? null;
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
// ../core/dist/wu-spawn-helpers.js
|
|
380
|
+
function validateSpawnArgs(args) {
|
|
381
|
+
if (args.thinking && args.noThinking) {
|
|
382
|
+
throw createError(ErrorCodes.INVALID_ARGUMENT, "--thinking and --no-thinking are mutually exclusive");
|
|
383
|
+
}
|
|
384
|
+
if (args.budget && !args.thinking) {
|
|
385
|
+
throw createError(ErrorCodes.INVALID_ARGUMENT, "--budget requires --thinking flag");
|
|
386
|
+
}
|
|
387
|
+
if (args.budget) {
|
|
388
|
+
const budgetNum = parseInt(args.budget, 10);
|
|
389
|
+
if (isNaN(budgetNum) || budgetNum <= 0 || !Number.isInteger(budgetNum)) {
|
|
390
|
+
throw createError(ErrorCodes.INVALID_ARGUMENT, "--budget must be a positive integer");
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
function generateExecutionModeSection(options) {
|
|
395
|
+
const { thinking, noThinking, budget } = options;
|
|
396
|
+
if (!thinking && !noThinking) {
|
|
397
|
+
return "";
|
|
398
|
+
}
|
|
399
|
+
const lines = ["## Execution Mode", ""];
|
|
400
|
+
if (thinking) {
|
|
401
|
+
lines.push("Extended thinking: **enabled**");
|
|
402
|
+
if (budget) {
|
|
403
|
+
lines.push(`Token budget: **${budget}**`);
|
|
404
|
+
}
|
|
405
|
+
lines.push("");
|
|
406
|
+
lines.push("The sub-agent will use extended thinking for complex reasoning tasks.");
|
|
407
|
+
} else if (noThinking) {
|
|
408
|
+
lines.push("Extended thinking: **disabled**");
|
|
409
|
+
lines.push("");
|
|
410
|
+
lines.push("The sub-agent will execute without extended thinking mode.");
|
|
411
|
+
}
|
|
412
|
+
return lines.join("\n");
|
|
413
|
+
}
|
|
414
|
+
function generateThinkToolGuidance(options) {
|
|
415
|
+
const { thinking, noThinking } = options;
|
|
416
|
+
if (!thinking || noThinking) {
|
|
417
|
+
return "";
|
|
418
|
+
}
|
|
419
|
+
return `## Think Tool Guidance
|
|
420
|
+
|
|
421
|
+
When extended thinking is enabled, use the think tool strategically for:
|
|
422
|
+
|
|
423
|
+
1. **Complex Decision Points**: Before making architectural decisions or choosing between approaches
|
|
424
|
+
2. **Multi-Step Reasoning**: When planning long tool-call chains or multi-file edits
|
|
425
|
+
3. **Mid-Execution Reflection**: After gathering information, before implementing changes
|
|
426
|
+
4. **Error Analysis**: When troubleshooting failures or unexpected behavior
|
|
427
|
+
|
|
428
|
+
### Best Practices
|
|
429
|
+
|
|
430
|
+
- Use think blocks to reason through acceptance criteria before implementation
|
|
431
|
+
- Document your reasoning for complex logic decisions
|
|
432
|
+
- Break down large tasks into thought-through steps
|
|
433
|
+
- Reflect on test results before proceeding with fixes
|
|
434
|
+
|
|
435
|
+
### When NOT to Use Think Blocks
|
|
436
|
+
|
|
437
|
+
- Simple file reads or writes with clear outcomes
|
|
438
|
+
- Routine git operations
|
|
439
|
+
- Running predefined commands like \`pnpm gates\`
|
|
440
|
+
- When the next action is obvious and low-risk`;
|
|
441
|
+
}
|
|
442
|
+
var LOG_PREFIX = "[wu:spawn]";
|
|
443
|
+
async function recordSpawnToRegistry(options) {
|
|
444
|
+
const { parentWuId, targetWuId, lane, baseDir = LUMENFLOW_PATHS.STATE_DIR, briefAttestation } = options;
|
|
445
|
+
try {
|
|
446
|
+
const store = new DelegationRegistryStore(baseDir);
|
|
447
|
+
await store.load();
|
|
448
|
+
const spawnId = await store.record(parentWuId, targetWuId, lane, void 0, briefAttestation);
|
|
449
|
+
return {
|
|
450
|
+
success: true,
|
|
451
|
+
spawnId
|
|
452
|
+
};
|
|
453
|
+
} catch (error) {
|
|
454
|
+
return {
|
|
455
|
+
success: false,
|
|
456
|
+
spawnId: null,
|
|
457
|
+
error: error.message
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
function formatSpawnRecordedMessage(spawnId, errorMessage = void 0) {
|
|
462
|
+
if (spawnId) {
|
|
463
|
+
return `${LOG_PREFIX} Spawn recorded ${spawnId}`;
|
|
464
|
+
}
|
|
465
|
+
if (errorMessage) {
|
|
466
|
+
return `${LOG_PREFIX} Warning: Registry write skipped (${errorMessage})`;
|
|
467
|
+
}
|
|
468
|
+
return `${LOG_PREFIX} Warning: Registry unavailable`;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// ../core/dist/wu-spawn-skills.js
|
|
472
|
+
import { existsSync } from "fs";
|
|
473
|
+
var DEFAULT_CONFIG = getDefaultConfig();
|
|
474
|
+
var KNOWN_SKILLS_DIRS = [
|
|
475
|
+
LUMENFLOW_PATHS.SKILLS_DIR,
|
|
476
|
+
DEFAULT_CONFIG.directories.skillsDir,
|
|
477
|
+
".codex/skills",
|
|
478
|
+
".gemini/skills"
|
|
479
|
+
];
|
|
480
|
+
var KNOWN_AGENTS_DIRS = [
|
|
481
|
+
LUMENFLOW_PATHS.AGENTS_DIR,
|
|
482
|
+
DEFAULT_CONFIG.directories.agentsDir,
|
|
483
|
+
".codex/agents",
|
|
484
|
+
".gemini/agents"
|
|
485
|
+
];
|
|
486
|
+
var SECTION = {
|
|
487
|
+
skillsSelection: "## Skills Selection",
|
|
488
|
+
skillsCatalog: "### Skills Catalog",
|
|
489
|
+
softPolicy: "### Soft Policy (baselines for this WU)",
|
|
490
|
+
additionalSkills: "### Additional Skills (load if needed)",
|
|
491
|
+
gracefulDegradation: "### Graceful Degradation",
|
|
492
|
+
clientSkills: "### Client Skills Guidance"
|
|
493
|
+
};
|
|
494
|
+
var MESSAGES = {
|
|
495
|
+
skillsIntro: "**IMPORTANT**: Before starting work, select and load relevant skills.",
|
|
496
|
+
catalogMissing: `No skills directories configured or found. Set \`directories.skillsDir\` or \`agents.clients.<client>.skillsDir\` in ${WORKSPACE_CONFIG_FILE_NAME} ${WORKSPACE_V2_KEYS.SOFTWARE_DELIVERY}.`,
|
|
497
|
+
baselineFallback: "- Load baseline skills: `/skill wu-lifecycle`, `/skill tdd-workflow` (for features)\n- Continue with implementation using Mandatory Standards below"
|
|
498
|
+
};
|
|
499
|
+
var CONTEXT_HINTS = {
|
|
500
|
+
wuLifecycle: "- `wu-lifecycle` \u2014 ALL WUs need workflow automation",
|
|
501
|
+
worktreeDiscipline: "- `worktree-discipline` \u2014 ALL WUs need path safety",
|
|
502
|
+
tddWorkflow: "- `tdd-workflow` \u2014 TDD is mandatory for feature/enhancement WUs",
|
|
503
|
+
bugClassification: "- `bug-classification` \u2014 Bug severity assessment",
|
|
504
|
+
lumenflowGates: "- `lumenflow-gates` \u2014 Tooling often affects gates",
|
|
505
|
+
llmCompliance: "- `llm-compliance` \u2014 Intelligence lane requires LLM validation",
|
|
506
|
+
promptManagement: "- `prompt-management` \u2014 For prompt template work",
|
|
507
|
+
frontendDesign: "- `frontend-design` \u2014 For UI component work"
|
|
508
|
+
};
|
|
509
|
+
var ADDITIONAL_SKILLS_TABLE = `| Skill | Use When |
|
|
510
|
+
|-------|----------|
|
|
511
|
+
| lumenflow-gates | Gates fail, debugging format/lint/typecheck errors |
|
|
512
|
+
| bug-classification | Bug discovered mid-WU, need priority classification |
|
|
513
|
+
| llm-compliance | Code touches LLM, prompts, classification |
|
|
514
|
+
| prompt-management | Working with prompt templates, golden datasets |
|
|
515
|
+
| frontend-design | Building UI components, pages |
|
|
516
|
+
| initiative-management | Multi-phase projects, INIT-XXX coordination |
|
|
517
|
+
| multi-agent-coordination | Spawning sub-agents, parallel WU work |
|
|
518
|
+
| orchestration | Agent coordination, mandatory agent checks |
|
|
519
|
+
| ops-maintenance | Metrics, validation, health checks |`;
|
|
520
|
+
function resolveClientConfig(config, clientName) {
|
|
521
|
+
const clients = config?.agents?.clients || {};
|
|
522
|
+
if (!clientName)
|
|
523
|
+
return void 0;
|
|
524
|
+
if (clients[clientName])
|
|
525
|
+
return clients[clientName];
|
|
526
|
+
const matchKey = Object.keys(clients).find((key) => key.toLowerCase() === clientName.toLowerCase());
|
|
527
|
+
return matchKey ? clients[matchKey] : void 0;
|
|
528
|
+
}
|
|
529
|
+
function uniqueNonEmpty(values) {
|
|
530
|
+
const seen = /* @__PURE__ */ new Set();
|
|
531
|
+
const result = [];
|
|
532
|
+
for (const value of values) {
|
|
533
|
+
if (!value)
|
|
534
|
+
continue;
|
|
535
|
+
if (seen.has(value))
|
|
536
|
+
continue;
|
|
537
|
+
seen.add(value);
|
|
538
|
+
result.push(value);
|
|
539
|
+
}
|
|
540
|
+
return result;
|
|
541
|
+
}
|
|
542
|
+
function resolveSkillsPaths(config, clientName) {
|
|
543
|
+
const clientConfig = resolveClientConfig(config, clientName);
|
|
544
|
+
const configuredSkillsDir = clientConfig?.skillsDir || config?.directories?.skillsDir;
|
|
545
|
+
const configuredAgentsDir = config?.directories?.agentsDir;
|
|
546
|
+
const skillsCandidates = uniqueNonEmpty([configuredSkillsDir, ...KNOWN_SKILLS_DIRS]);
|
|
547
|
+
const agentsCandidates = uniqueNonEmpty([configuredAgentsDir, ...KNOWN_AGENTS_DIRS]);
|
|
548
|
+
const skillsDir = skillsCandidates.find((candidate) => existsSync(candidate));
|
|
549
|
+
const agentsDir = agentsCandidates.find((candidate) => existsSync(candidate));
|
|
550
|
+
const configuredSkillsMissing = Boolean(configuredSkillsDir && !existsSync(configuredSkillsDir));
|
|
551
|
+
const configuredAgentsMissing = Boolean(configuredAgentsDir && !existsSync(configuredAgentsDir));
|
|
552
|
+
return {
|
|
553
|
+
clientConfig,
|
|
554
|
+
configuredSkillsDir,
|
|
555
|
+
configuredAgentsDir,
|
|
556
|
+
skillsDir,
|
|
557
|
+
agentsDir,
|
|
558
|
+
configuredSkillsMissing,
|
|
559
|
+
configuredAgentsMissing
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
function generateSkillsCatalogGuidance(config, clientName) {
|
|
563
|
+
const resolution = resolveSkillsPaths(config, clientName);
|
|
564
|
+
const lines = [];
|
|
565
|
+
if (resolution.skillsDir) {
|
|
566
|
+
lines.push(`- Check \`${resolution.skillsDir}\` for available skills.`);
|
|
567
|
+
}
|
|
568
|
+
if (resolution.agentsDir) {
|
|
569
|
+
lines.push(`- Check \`${resolution.agentsDir}\` for agent configs (optional).`);
|
|
570
|
+
}
|
|
571
|
+
if (lines.length > 0) {
|
|
572
|
+
return `${SECTION.skillsCatalog}
|
|
573
|
+
|
|
574
|
+
${lines.join("\n")}
|
|
575
|
+
`;
|
|
576
|
+
}
|
|
577
|
+
const configuredHint = resolution.configuredSkillsDir ? `Configured skillsDir \`${resolution.configuredSkillsDir}\` was not found. ` : "";
|
|
578
|
+
const clientHint = clientName ? `agents.clients.${clientName}.skillsDir` : "agents.clients.<client>.skillsDir";
|
|
579
|
+
return `${SECTION.skillsCatalog}
|
|
580
|
+
|
|
581
|
+
${configuredHint}No skills directories configured or found. Set \`directories.skillsDir\` or \`${clientHint}\` in ${WORKSPACE_CONFIG_FILE_NAME} ${WORKSPACE_V2_KEYS.SOFTWARE_DELIVERY}.
|
|
582
|
+
`;
|
|
583
|
+
}
|
|
584
|
+
function getByLaneSkills(clientContext, lane) {
|
|
585
|
+
const byLane = clientContext?.config?.skills?.byLane;
|
|
586
|
+
if (!byLane || !lane)
|
|
587
|
+
return [];
|
|
588
|
+
return byLane[lane] || [];
|
|
589
|
+
}
|
|
590
|
+
function generateClientSkillsGuidance(clientContext, lane) {
|
|
591
|
+
const skills = clientContext?.config?.skills;
|
|
592
|
+
if (!skills) {
|
|
593
|
+
return "";
|
|
594
|
+
}
|
|
595
|
+
const byLaneSkills = lane ? getByLaneSkills(clientContext, lane) : [];
|
|
596
|
+
const hasRecommended = skills.recommended && skills.recommended.length > 0;
|
|
597
|
+
const hasByLane = byLaneSkills.length > 0;
|
|
598
|
+
const hasInstructions = Boolean(skills.instructions);
|
|
599
|
+
if (!hasInstructions && !hasRecommended && !hasByLane) {
|
|
600
|
+
return "";
|
|
601
|
+
}
|
|
602
|
+
const instructions = skills.instructions ? `${skills.instructions.trim()}
|
|
603
|
+
|
|
604
|
+
` : "";
|
|
605
|
+
const recommendedSection = hasRecommended || hasByLane ? `Recommended skills:
|
|
606
|
+
${[...skills.recommended || [], ...byLaneSkills].filter((s, i, arr) => arr.indexOf(s) === i).map((s) => `- \`${s}\``).join("\n")}
|
|
607
|
+
` : "";
|
|
608
|
+
return `${SECTION.clientSkills} (${clientContext?.name})
|
|
609
|
+
|
|
610
|
+
${instructions}${recommendedSection}`;
|
|
611
|
+
}
|
|
612
|
+
function resolveCapabilitySkills(capabilities, clientConfig) {
|
|
613
|
+
const capMap = clientConfig?.capabilities_map;
|
|
614
|
+
if (!capMap || capabilities.length === 0)
|
|
615
|
+
return [];
|
|
616
|
+
const skills = [];
|
|
617
|
+
for (const capability of capabilities) {
|
|
618
|
+
const skillName = capMap[capability];
|
|
619
|
+
if (skillName) {
|
|
620
|
+
skills.push(skillName);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
return skills;
|
|
624
|
+
}
|
|
625
|
+
function generateSkillsSelectionSection(doc, config, clientName) {
|
|
626
|
+
const lane = doc.lane || "";
|
|
627
|
+
const type = doc.type || "feature";
|
|
628
|
+
const laneParent = lane.split(":")[0].trim();
|
|
629
|
+
const classificationConfig = config?.methodology?.work_classification;
|
|
630
|
+
const classification = classifyWork({
|
|
631
|
+
code_paths: doc.code_paths,
|
|
632
|
+
lane: doc.lane,
|
|
633
|
+
type: doc.type,
|
|
634
|
+
description: doc.description
|
|
635
|
+
}, classificationConfig);
|
|
636
|
+
const contextHints = [];
|
|
637
|
+
contextHints.push(CONTEXT_HINTS.wuLifecycle);
|
|
638
|
+
contextHints.push(CONTEXT_HINTS.worktreeDiscipline);
|
|
639
|
+
if (type === "feature" || type === "enhancement") {
|
|
640
|
+
contextHints.push(CONTEXT_HINTS.tddWorkflow);
|
|
641
|
+
}
|
|
642
|
+
if (type === "bug") {
|
|
643
|
+
contextHints.push(CONTEXT_HINTS.bugClassification);
|
|
644
|
+
}
|
|
645
|
+
if (laneParent === "Operations" && lane.includes("Tooling")) {
|
|
646
|
+
contextHints.push(CONTEXT_HINTS.lumenflowGates);
|
|
647
|
+
}
|
|
648
|
+
if (laneParent === "Intelligence") {
|
|
649
|
+
contextHints.push(CONTEXT_HINTS.llmCompliance);
|
|
650
|
+
contextHints.push(CONTEXT_HINTS.promptManagement);
|
|
651
|
+
}
|
|
652
|
+
if (classification.domain === "ui" || laneParent === "Experience") {
|
|
653
|
+
contextHints.push(CONTEXT_HINTS.frontendDesign);
|
|
654
|
+
}
|
|
655
|
+
const clientConfig = resolveClientConfig(config, clientName);
|
|
656
|
+
const capabilitySkills = resolveCapabilitySkills(classification.capabilities, clientConfig);
|
|
657
|
+
const capabilityHints = capabilitySkills.map((skill) => `- \`${skill}\` \u2014 Suggested by work classifier (${classification.domain} domain)`);
|
|
658
|
+
const softPolicySection = `${SECTION.softPolicy}
|
|
659
|
+
|
|
660
|
+
Based on WU context, consider loading:
|
|
661
|
+
|
|
662
|
+
${contextHints.join("\n")}${capabilityHints.length > 0 ? "\n" + capabilityHints.join("\n") : ""}
|
|
663
|
+
|
|
664
|
+
`;
|
|
665
|
+
const catalogGuidance = generateSkillsCatalogGuidance(config, clientName);
|
|
666
|
+
return `${SECTION.skillsSelection}
|
|
667
|
+
|
|
668
|
+
${MESSAGES.skillsIntro}
|
|
669
|
+
|
|
670
|
+
${catalogGuidance}${softPolicySection}${SECTION.additionalSkills}
|
|
671
|
+
|
|
672
|
+
${ADDITIONAL_SKILLS_TABLE}
|
|
673
|
+
|
|
674
|
+
${SECTION.gracefulDegradation}
|
|
675
|
+
|
|
676
|
+
If the skill catalogue is missing or invalid:
|
|
677
|
+
${MESSAGES.baselineFallback}
|
|
678
|
+
`;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// ../core/dist/template-loader.js
|
|
682
|
+
import { existsSync as existsSync2, readFileSync, readdirSync } from "fs";
|
|
683
|
+
import { join } from "path";
|
|
684
|
+
import matter from "gray-matter";
|
|
685
|
+
import yaml from "yaml";
|
|
686
|
+
var MANIFEST_PATH = LUMENFLOW_PATHS.TEMPLATE_MANIFEST;
|
|
687
|
+
var TEMPLATES_DIR = LUMENFLOW_PATHS.SPAWN_PROMPT_DIR;
|
|
688
|
+
var TEMPLATE_SUBDIR = "spawn-prompt";
|
|
689
|
+
var CLIENT_ALIAS_MAP = {
|
|
690
|
+
"claude-code": "claude",
|
|
691
|
+
"codex-cli": "codex",
|
|
692
|
+
"gemini-cli": "gemini"
|
|
693
|
+
};
|
|
694
|
+
function resolveClientAlias(clientName) {
|
|
695
|
+
const normalizedName = clientName.trim().toLowerCase();
|
|
696
|
+
const mappedAlias = CLIENT_ALIAS_MAP[normalizedName];
|
|
697
|
+
if (mappedAlias) {
|
|
698
|
+
return mappedAlias;
|
|
699
|
+
}
|
|
700
|
+
return normalizedName.replace(/-cli$/, "");
|
|
701
|
+
}
|
|
702
|
+
function loadTemplate(templatePath) {
|
|
703
|
+
if (!existsSync2(templatePath)) {
|
|
704
|
+
throw createError(ErrorCodes.FILE_NOT_FOUND, `Template not found: ${templatePath}`, {
|
|
705
|
+
path: templatePath
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
const fileContent = readFileSync(templatePath, "utf-8");
|
|
709
|
+
const { data, content } = matter(fileContent, {
|
|
710
|
+
engines: {
|
|
711
|
+
yaml: {
|
|
712
|
+
parse: yaml.parse.bind(yaml),
|
|
713
|
+
stringify: yaml.stringify.bind(yaml)
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
const frontmatter = data;
|
|
718
|
+
if (!frontmatter.id || typeof frontmatter.id !== "string") {
|
|
719
|
+
throw createError(ErrorCodes.VALIDATION_ERROR, `Template ${templatePath}: 'id' field is required in frontmatter`, { path: templatePath });
|
|
720
|
+
}
|
|
721
|
+
if (!frontmatter.name || typeof frontmatter.name !== "string") {
|
|
722
|
+
throw createError(ErrorCodes.VALIDATION_ERROR, `Template ${templatePath}: 'name' field is required in frontmatter`, { path: templatePath });
|
|
723
|
+
}
|
|
724
|
+
if (typeof frontmatter.required !== "boolean") {
|
|
725
|
+
throw createError(ErrorCodes.VALIDATION_ERROR, `Template ${templatePath}: 'required' field must be a boolean`, { path: templatePath });
|
|
726
|
+
}
|
|
727
|
+
if (typeof frontmatter.order !== "number") {
|
|
728
|
+
throw createError(ErrorCodes.VALIDATION_ERROR, `Template ${templatePath}: 'order' field must be a number`, { path: templatePath });
|
|
729
|
+
}
|
|
730
|
+
return {
|
|
731
|
+
frontmatter: {
|
|
732
|
+
id: frontmatter.id,
|
|
733
|
+
name: frontmatter.name,
|
|
734
|
+
required: frontmatter.required,
|
|
735
|
+
order: frontmatter.order,
|
|
736
|
+
tokens: Array.isArray(frontmatter.tokens) ? frontmatter.tokens : void 0,
|
|
737
|
+
condition: typeof frontmatter.condition === "string" ? frontmatter.condition : void 0
|
|
738
|
+
},
|
|
739
|
+
content: content.trim(),
|
|
740
|
+
sourcePath: templatePath
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
function loadTemplatesWithOverrides(baseDir, clientName) {
|
|
744
|
+
const templates = /* @__PURE__ */ new Map();
|
|
745
|
+
const baseTemplatesDir = join(baseDir, TEMPLATES_DIR);
|
|
746
|
+
const clientAlias = resolveClientAlias(clientName);
|
|
747
|
+
const clientTemplatesDir = join(baseDir, `${LUMENFLOW_PATHS.BASE}/templates.${clientAlias}/${TEMPLATE_SUBDIR}`);
|
|
748
|
+
if (existsSync2(baseTemplatesDir)) {
|
|
749
|
+
loadTemplatesFromDir(baseTemplatesDir, templates);
|
|
750
|
+
}
|
|
751
|
+
if (existsSync2(clientTemplatesDir)) {
|
|
752
|
+
loadTemplatesFromDir(clientTemplatesDir, templates);
|
|
753
|
+
}
|
|
754
|
+
return templates;
|
|
755
|
+
}
|
|
756
|
+
function loadTemplatesFromDir(dirPath, templates) {
|
|
757
|
+
const entries = readdirSync(dirPath, { withFileTypes: true });
|
|
758
|
+
for (const entry of entries) {
|
|
759
|
+
const fullPath = join(dirPath, entry.name);
|
|
760
|
+
if (entry.isDirectory()) {
|
|
761
|
+
loadTemplatesFromDir(fullPath, templates);
|
|
762
|
+
} else if (entry.name.endsWith(".md")) {
|
|
763
|
+
try {
|
|
764
|
+
const template = loadTemplate(fullPath);
|
|
765
|
+
templates.set(template.frontmatter.id, template);
|
|
766
|
+
} catch {
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
function replaceTokens(content, tokens) {
|
|
772
|
+
let result = content;
|
|
773
|
+
for (const [key, value] of Object.entries(tokens)) {
|
|
774
|
+
if (value !== void 0) {
|
|
775
|
+
const pattern = new RegExp(`\\{${escapeRegex(key)}\\}`, "g");
|
|
776
|
+
result = result.replace(pattern, value);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
return result;
|
|
780
|
+
}
|
|
781
|
+
function escapeRegex(str) {
|
|
782
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
783
|
+
}
|
|
784
|
+
function getNestedValue(obj, path3) {
|
|
785
|
+
if (path3 in obj) {
|
|
786
|
+
return obj[path3];
|
|
787
|
+
}
|
|
788
|
+
const parts = path3.split(".");
|
|
789
|
+
let current = obj;
|
|
790
|
+
for (const part of parts) {
|
|
791
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
792
|
+
return void 0;
|
|
793
|
+
}
|
|
794
|
+
current = current[part];
|
|
795
|
+
}
|
|
796
|
+
return current;
|
|
797
|
+
}
|
|
798
|
+
function evaluateCondition(condition, context) {
|
|
799
|
+
if (!condition || condition.trim() === "") {
|
|
800
|
+
return true;
|
|
801
|
+
}
|
|
802
|
+
if (condition.includes("||")) {
|
|
803
|
+
const parts = condition.split("||").map((p) => p.trim());
|
|
804
|
+
return parts.some((part) => evaluateCondition(part, context));
|
|
805
|
+
}
|
|
806
|
+
if (condition.includes("&&")) {
|
|
807
|
+
const parts = condition.split("&&").map((p) => p.trim());
|
|
808
|
+
return parts.every((part) => evaluateCondition(part, context));
|
|
809
|
+
}
|
|
810
|
+
const eqRegex = /^([\w.]+)\s*===\s*['"](.+)['"]$/;
|
|
811
|
+
const eqMatch = eqRegex.exec(condition);
|
|
812
|
+
if (eqMatch) {
|
|
813
|
+
const key = eqMatch[1];
|
|
814
|
+
const value = eqMatch[2];
|
|
815
|
+
if (!key)
|
|
816
|
+
return true;
|
|
817
|
+
return getNestedValue(context, key) === value;
|
|
818
|
+
}
|
|
819
|
+
const neqRegex = /^([\w.]+)\s*!==\s*['"](.+)['"]$/;
|
|
820
|
+
const neqMatch = neqRegex.exec(condition);
|
|
821
|
+
if (neqMatch) {
|
|
822
|
+
const key = neqMatch[1];
|
|
823
|
+
const value = neqMatch[2];
|
|
824
|
+
if (!key)
|
|
825
|
+
return true;
|
|
826
|
+
return getNestedValue(context, key) !== value;
|
|
827
|
+
}
|
|
828
|
+
const truthyRegex = /^([\w.]+)$/;
|
|
829
|
+
const truthyMatch = truthyRegex.exec(condition);
|
|
830
|
+
if (truthyMatch) {
|
|
831
|
+
const key = truthyMatch[1];
|
|
832
|
+
if (!key)
|
|
833
|
+
return false;
|
|
834
|
+
return Boolean(getNestedValue(context, key));
|
|
835
|
+
}
|
|
836
|
+
return true;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// ../core/dist/spawn-template-assembler.js
|
|
840
|
+
var TRUNCATION_WARNING_BANNER = `<!-- LUMENFLOW_TRUNCATION_WARNING -->
|
|
841
|
+
<!--
|
|
842
|
+
\u26A0\uFE0F CRITICAL: DO NOT TRUNCATE THIS OUTPUT
|
|
843
|
+
|
|
844
|
+
This spawn prompt MUST be copied verbatim. Truncation causes:
|
|
845
|
+
- Missing constraints (agents ignore safety rules)
|
|
846
|
+
- Missing TDD directives (agents skip tests)
|
|
847
|
+
- Degraded agent performance
|
|
848
|
+
|
|
849
|
+
VERIFICATION: The output MUST end with: <!-- LUMENFLOW_SPAWN_END -->
|
|
850
|
+
If you don't see that sentinel at the end, the output was truncated.
|
|
851
|
+
-->
|
|
852
|
+
`;
|
|
853
|
+
var SPAWN_END_SENTINEL = "<!-- LUMENFLOW_SPAWN_END -->";
|
|
854
|
+
|
|
855
|
+
// ../core/dist/spawn-constraints-generator.js
|
|
856
|
+
var DEFAULT_MAIN_REF = `${REMOTES.ORIGIN}/${BRANCHES.MAIN}`;
|
|
857
|
+
var DEFAULT_WORKTREES_DIR_SEGMENT = DIRECTORIES.WORKTREES.replace(/\/+$/g, "");
|
|
858
|
+
function normalizeWorktreesDirSegment(value) {
|
|
859
|
+
const normalized = (value ?? "").replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
|
|
860
|
+
return normalized.length > 0 ? normalized : DEFAULT_WORKTREES_DIR_SEGMENT;
|
|
861
|
+
}
|
|
862
|
+
function generateConstraints(id, options) {
|
|
863
|
+
const includeTdd = options?.includeTddCheckpoint !== false;
|
|
864
|
+
const mainRef = options?.mainRef ?? DEFAULT_MAIN_REF;
|
|
865
|
+
const worktreesDirSegment = normalizeWorktreesDirSegment(options?.worktreesDirSegment);
|
|
866
|
+
const tddCheckpointBlock = includeTdd ? `
|
|
867
|
+
1. TDD CHECKPOINT (VERIFY BEFORE IMPLEMENTATION)
|
|
868
|
+
- Did you write tests BEFORE implementation?
|
|
869
|
+
- Is there at least one failing test for each acceptance criterion?
|
|
870
|
+
- Never skip the RED phase \u2014 failing tests prove the test works
|
|
871
|
+
|
|
872
|
+
` : "";
|
|
873
|
+
const antiLoopNum = includeTdd ? 2 : 1;
|
|
874
|
+
const stopAskNum = includeTdd ? 3 : 2;
|
|
875
|
+
const verifyNum = includeTdd ? 4 : 3;
|
|
876
|
+
const neverFabNum = includeTdd ? 5 : 4;
|
|
877
|
+
const gitNum = includeTdd ? 6 : 5;
|
|
878
|
+
const memNum = includeTdd ? 7 : 6;
|
|
879
|
+
const skipGatesNum = includeTdd ? 8 : 7;
|
|
880
|
+
const worktreeNum = includeTdd ? 9 : 8;
|
|
881
|
+
return `---
|
|
882
|
+
|
|
883
|
+
<constraints>
|
|
884
|
+
CRITICAL RULES - ENFORCE BEFORE EVERY ACTION:
|
|
885
|
+
${tddCheckpointBlock}${antiLoopNum}. ANTI-LOOP GUARD (LumenFlow \xA77.3)
|
|
886
|
+
- Max 3 attempts per unique error before escalating
|
|
887
|
+
- If same error repeats 3x, STOP and report with full context
|
|
888
|
+
- Retry with different approach, not same command
|
|
889
|
+
|
|
890
|
+
${stopAskNum}. STOP-AND-ASK TRIGGERS (LumenFlow \xA77.2 - narrow scope)
|
|
891
|
+
- Policy changes, auth/permissions modifications
|
|
892
|
+
- PII/safety issues, cloud spend, secrets, backups
|
|
893
|
+
- Same error repeats 3x
|
|
894
|
+
- For ordinary errors: fix and retry autonomously (up to 3 attempts)
|
|
895
|
+
|
|
896
|
+
${verifyNum}. VERIFY COMPLETION before reporting success
|
|
897
|
+
- Run: node packages/@lumenflow/agent/verification ${id} (from shared checkout)
|
|
898
|
+
- Exit 0 = passed, Exit 1 = INCOMPLETE
|
|
899
|
+
- Never report "done" if verification fails
|
|
900
|
+
|
|
901
|
+
${neverFabNum}. NEVER FABRICATE COMPLETION
|
|
902
|
+
- If blockers remain, report INCOMPLETE
|
|
903
|
+
- If verification fails, summarize failures
|
|
904
|
+
- Honesty over false completion
|
|
905
|
+
|
|
906
|
+
${gitNum}. GIT WORKFLOW (CRITICAL - GitHub rules reject merge commits)
|
|
907
|
+
- GitHub REJECTS merge commits on main
|
|
908
|
+
- ALWAYS use \`git rebase ${mainRef}\` before push
|
|
909
|
+
- Push to main via \`git push origin lane/...:main\` (fast-forward only)
|
|
910
|
+
- NEVER use \`git merge\` on main branch
|
|
911
|
+
- Use \`pnpm wu:prep\` from worktree, then \`pnpm wu:done\` from main (WU-1223)
|
|
912
|
+
|
|
913
|
+
${memNum}. MEMORY LAYER COORDINATION (INIT-007)
|
|
914
|
+
- Use \`pnpm mem:checkpoint --wu ${id}\` to save progress before risky operations
|
|
915
|
+
- Check \`pnpm mem:inbox --wu ${id}\` periodically for parallel signals from other agents
|
|
916
|
+
- Checkpoint triggers (WU-1943): checkpoint after each acceptance criterion completed, checkpoint before gates, checkpoint every 30 tool calls
|
|
917
|
+
|
|
918
|
+
${skipGatesNum}. SKIP-GATES AUTONOMY (WU-1142)
|
|
919
|
+
- If gates fail, first check if failure is pre-existing on main: \`git checkout main && pnpm gates\`
|
|
920
|
+
- If failure exists on main (not your change), use: \`pnpm wu:done --id ${id} --skip-gates --reason "pre-existing on main" --fix-wu WU-XXXX\`
|
|
921
|
+
- Do NOT ask for approval - autonomous skip-gates for pre-existing failures is correct
|
|
922
|
+
- This prevents getting stuck on infrastructure debt
|
|
923
|
+
|
|
924
|
+
${worktreeNum}. WORKTREE DISCIPLINE (WU-1282)
|
|
925
|
+
- CRITICAL: PreToolUse hooks do not propagate to sub-agents spawned via Task tool
|
|
926
|
+
- BEFORE UnsafeAny Write/Edit operation, manually verify you are in a worktree:
|
|
927
|
+
- Run: \`pwd\` and confirm output contains \`${worktreesDirSegment}/\`
|
|
928
|
+
- If not in worktree, STOP and navigate: \`cd ${worktreesDirSegment}/<lane>-wu-xxx\`
|
|
929
|
+
- Use RELATIVE paths only (never full absolute paths starting with root directory)
|
|
930
|
+
- This constraint exists because Claude Code does not inherit settings.json hooks in sub-agent sessions
|
|
931
|
+
</constraints>
|
|
932
|
+
|
|
933
|
+
${SPAWN_END_SENTINEL}`;
|
|
934
|
+
}
|
|
935
|
+
function generateCodexConstraints(id, options) {
|
|
936
|
+
const includeTdd = options?.includeTddCheckpoint !== false;
|
|
937
|
+
const worktreesDirSegment = normalizeWorktreesDirSegment(options?.worktreesDirSegment);
|
|
938
|
+
const tddLine = includeTdd ? "1. **TDD checkpoint**: tests BEFORE implementation; never skip RED" : "";
|
|
939
|
+
const stopNum = includeTdd ? 2 : 1;
|
|
940
|
+
const verifyNum = includeTdd ? 3 : 2;
|
|
941
|
+
const fabricateNum = includeTdd ? 4 : 3;
|
|
942
|
+
const gitNum = includeTdd ? 5 : 4;
|
|
943
|
+
const scopeNum = includeTdd ? 6 : 5;
|
|
944
|
+
const worktreeNum = includeTdd ? 7 : 6;
|
|
945
|
+
return `## Constraints (Critical)
|
|
946
|
+
|
|
947
|
+
${tddLine}${tddLine ? "\n" : ""}${stopNum}. **Stop on errors**: if UnsafeAny command fails, report BLOCKED (never DONE) with the error
|
|
948
|
+
${verifyNum}. **Verify before success**: run \`pnpm gates\` in the worktree, then run \`node packages/@lumenflow/agent/verification ${id}\` (from the shared checkout)
|
|
949
|
+
${fabricateNum}. **No fabrication**: if blockers remain or verification fails, report INCOMPLETE
|
|
950
|
+
${gitNum}. **Git workflow**: avoid merge commits; use \`wu:prep\` from worktree, then \`wu:done\` from main
|
|
951
|
+
${scopeNum}. **Scope discipline**: stay within \`code_paths\`; capture out-of-scope issues via \`pnpm mem:create\`
|
|
952
|
+
${worktreeNum}. **Worktree discipline (WU-1282)**: BEFORE UnsafeAny Write/Edit, verify \`pwd\` shows \`${worktreesDirSegment}/\`; hooks do not propagate to sub-agents`;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
// ../core/dist/spawn-agent-guidance.js
|
|
956
|
+
var DEFAULT_WORKTREES_DIR_SEGMENT2 = DIRECTORIES.WORKTREES.replace(/\/+$/g, "");
|
|
957
|
+
function normalizeDirectorySegment(value) {
|
|
958
|
+
return value.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
|
|
959
|
+
}
|
|
960
|
+
function resolveWorktreesDirSegment(config) {
|
|
961
|
+
const normalized = normalizeDirectorySegment(config.directories.worktrees);
|
|
962
|
+
return normalized.length > 0 ? normalized : DEFAULT_WORKTREES_DIR_SEGMENT2;
|
|
963
|
+
}
|
|
964
|
+
function generateEffortScalingRules() {
|
|
965
|
+
return `## Effort Scaling (When to Spawn Sub-Agents)
|
|
966
|
+
|
|
967
|
+
Use this heuristic to decide complexity:
|
|
968
|
+
|
|
969
|
+
| Complexity | Approach | Tool Calls |
|
|
970
|
+
|------------|----------|------------|
|
|
971
|
+
| **Simple** (single file, <50 lines) | Handle inline | 3-10 |
|
|
972
|
+
| **Moderate** (2-3 files, clear scope) | Handle inline | 10-20 |
|
|
973
|
+
| **Complex** (4+ files, exploration needed) | Spawn Explore agent first | 20+ |
|
|
974
|
+
| **Multi-domain** (cross-cutting concerns) | Spawn specialized sub-agents | Varies |
|
|
975
|
+
|
|
976
|
+
**Rule**: If you need >30 tool calls for a subtask, consider spawning a sub-agent with a focused scope.`;
|
|
977
|
+
}
|
|
978
|
+
function generateParallelToolCallGuidance() {
|
|
979
|
+
return `## Parallel Tool Calls (Performance)
|
|
980
|
+
|
|
981
|
+
**IMPORTANT**: Make 3+ tool calls in parallel when operations are independent.
|
|
982
|
+
|
|
983
|
+
Good examples:
|
|
984
|
+
- Reading multiple files simultaneously
|
|
985
|
+
- Running independent grep searches
|
|
986
|
+
- Spawning multiple Explore agents for different areas
|
|
987
|
+
|
|
988
|
+
Bad examples:
|
|
989
|
+
- Reading a file then editing it (sequential dependency)
|
|
990
|
+
- Running tests then checking results (sequential)
|
|
991
|
+
|
|
992
|
+
Parallelism reduces latency by 50-90% for complex tasks.`;
|
|
993
|
+
}
|
|
994
|
+
function generateIterativeSearchHeuristics() {
|
|
995
|
+
return `## Search Strategy (Broad to Narrow)
|
|
996
|
+
|
|
997
|
+
When exploring the codebase:
|
|
998
|
+
|
|
999
|
+
1. **Start broad**: Use Explore agent or glob patterns to understand structure
|
|
1000
|
+
2. **Evaluate findings**: What patterns exist? What's relevant?
|
|
1001
|
+
3. **Narrow focus**: Target specific files/functions based on findings
|
|
1002
|
+
4. **Iterate**: Refine if initial approach misses the target
|
|
1003
|
+
|
|
1004
|
+
Avoid: Jumping directly to specific file edits without understanding context.`;
|
|
1005
|
+
}
|
|
1006
|
+
function generateTokenBudgetAwareness(id) {
|
|
1007
|
+
return `## Token Budget Awareness
|
|
1008
|
+
|
|
1009
|
+
Context limit is ~200K tokens. Monitor your usage:
|
|
1010
|
+
|
|
1011
|
+
- **At 50+ tool calls**: Create a checkpoint (\`pnpm mem:checkpoint --wu ${id}\`)
|
|
1012
|
+
- **At 100+ tool calls**: Consider spawning fresh sub-agent with focused scope
|
|
1013
|
+
- **Before risky operations**: Always checkpoint first
|
|
1014
|
+
|
|
1015
|
+
If approaching limits, summarize progress and spawn continuation agent.`;
|
|
1016
|
+
}
|
|
1017
|
+
function generateCompletionFormat(_id) {
|
|
1018
|
+
return `## Completion Report Format
|
|
1019
|
+
|
|
1020
|
+
When finishing, provide structured output:
|
|
1021
|
+
|
|
1022
|
+
\`\`\`
|
|
1023
|
+
## Summary
|
|
1024
|
+
<1-3 sentences describing what was accomplished>
|
|
1025
|
+
|
|
1026
|
+
## Artifacts
|
|
1027
|
+
- Files modified: <list>
|
|
1028
|
+
- Tests added: <list>
|
|
1029
|
+
- Documentation updated: <list>
|
|
1030
|
+
|
|
1031
|
+
## Verification
|
|
1032
|
+
- Gates: <pass/fail>
|
|
1033
|
+
- Tests: <X passing, Y failing>
|
|
1034
|
+
|
|
1035
|
+
## Blockers (if UnsafeAny)
|
|
1036
|
+
- <blocker description>
|
|
1037
|
+
|
|
1038
|
+
## Follow-up (if needed)
|
|
1039
|
+
- <suggested next WU or action>
|
|
1040
|
+
\`\`\`
|
|
1041
|
+
|
|
1042
|
+
This format enables orchestrator to track progress across waves.`;
|
|
1043
|
+
}
|
|
1044
|
+
function generateAgentCoordinationSection(id) {
|
|
1045
|
+
const config = getConfig();
|
|
1046
|
+
const progressSignals = config.memory?.progress_signals;
|
|
1047
|
+
const isEnabled = progressSignals?.enabled ?? true;
|
|
1048
|
+
const generateMilestoneTriggers = () => {
|
|
1049
|
+
if (!isEnabled) {
|
|
1050
|
+
return `For long-running work, send progress signals at milestones:
|
|
1051
|
+
|
|
1052
|
+
\`\`\`bash
|
|
1053
|
+
pnpm mem:signal "50% complete: tests passing, implementing adapter" --wu ${id}
|
|
1054
|
+
pnpm mem:signal "Blocked: waiting for WU-XXX dependency" --wu ${id}
|
|
1055
|
+
\`\`\``;
|
|
1056
|
+
}
|
|
1057
|
+
const triggers = [];
|
|
1058
|
+
if ((progressSignals?.on_milestone ?? true) !== false) {
|
|
1059
|
+
triggers.push("**After each acceptance criterion completed** - helps track progress");
|
|
1060
|
+
}
|
|
1061
|
+
if ((progressSignals?.on_tests_pass ?? true) !== false) {
|
|
1062
|
+
triggers.push("**When tests first pass** - indicates implementation is working");
|
|
1063
|
+
}
|
|
1064
|
+
if ((progressSignals?.before_gates ?? true) !== false) {
|
|
1065
|
+
triggers.push("**Before running gates** - signals imminent completion");
|
|
1066
|
+
}
|
|
1067
|
+
if ((progressSignals?.on_blocked ?? true) !== false) {
|
|
1068
|
+
triggers.push("**When blocked** - allows orchestrator to re-allocate or assist");
|
|
1069
|
+
}
|
|
1070
|
+
const frequency = progressSignals?.frequency ?? 0;
|
|
1071
|
+
let frequencyGuidance = "";
|
|
1072
|
+
if (frequency > 0) {
|
|
1073
|
+
frequencyGuidance = `
|
|
1074
|
+
5. **Every ${frequency} tool calls** - periodic progress update`;
|
|
1075
|
+
}
|
|
1076
|
+
const triggerList = triggers.length > 0 ? triggers.map((t, i) => `${i + 1}. ${t}`).join("\n") + frequencyGuidance : "Signal at key milestones to enable orchestrator visibility.";
|
|
1077
|
+
return `**Signal at these milestones** to enable orchestrator visibility:
|
|
1078
|
+
|
|
1079
|
+
${triggerList}
|
|
1080
|
+
|
|
1081
|
+
\`\`\`bash
|
|
1082
|
+
pnpm mem:signal "AC1 complete: tests passing for feature X" --wu ${id}
|
|
1083
|
+
pnpm mem:signal "All tests passing, running gates" --wu ${id}
|
|
1084
|
+
pnpm mem:signal "Blocked: waiting for WU-XXX dependency" --wu ${id}
|
|
1085
|
+
\`\`\``;
|
|
1086
|
+
};
|
|
1087
|
+
const progressSectionTitle = isEnabled ? "### Progress Signals (Required at Milestones)" : "### Progress Signals (Optional)";
|
|
1088
|
+
return `## Agent Coordination (Parallel Work)
|
|
1089
|
+
|
|
1090
|
+
### \u26A0\uFE0F CRITICAL: Use mem:signal, NOT TaskOutput
|
|
1091
|
+
|
|
1092
|
+
**DO NOT** use TaskOutput to check agent progress - it returns full transcripts
|
|
1093
|
+
and causes "prompt too long" errors. Always use the memory layer instead:
|
|
1094
|
+
|
|
1095
|
+
\`\`\`bash
|
|
1096
|
+
# \u2705 CORRECT: Compact signals (~6 lines)
|
|
1097
|
+
pnpm mem:inbox --since 30m
|
|
1098
|
+
|
|
1099
|
+
# \u274C WRONG: Full transcripts (context explosion)
|
|
1100
|
+
# TaskOutput with block=false <-- NEVER DO THIS FOR MONITORING
|
|
1101
|
+
\`\`\`
|
|
1102
|
+
|
|
1103
|
+
### Automatic Completion Signals
|
|
1104
|
+
|
|
1105
|
+
\`wu:done\` automatically broadcasts completion signals. You do not need to
|
|
1106
|
+
manually signal completion - just run \`wu:done\` and orchestrators will
|
|
1107
|
+
see your signal via \`mem:inbox\`.
|
|
1108
|
+
|
|
1109
|
+
${progressSectionTitle}
|
|
1110
|
+
|
|
1111
|
+
${generateMilestoneTriggers()}
|
|
1112
|
+
|
|
1113
|
+
### Checking Status
|
|
1114
|
+
|
|
1115
|
+
\`\`\`bash
|
|
1116
|
+
pnpm orchestrate:init-status -i INIT-XXX # Initiative progress (compact)
|
|
1117
|
+
pnpm mem:inbox --since 1h # Recent signals from all agents
|
|
1118
|
+
pnpm mem:inbox --lane "Experience: Web" # Lane-specific signals
|
|
1119
|
+
\`\`\``;
|
|
1120
|
+
}
|
|
1121
|
+
function generateQuickFixCommands() {
|
|
1122
|
+
return `## Quick Fix Commands
|
|
1123
|
+
|
|
1124
|
+
If gates fail, try these before investigating:
|
|
1125
|
+
|
|
1126
|
+
\`\`\`bash
|
|
1127
|
+
pnpm format # Auto-fix formatting issues
|
|
1128
|
+
pnpm lint # Check linting (use --fix for auto-fix)
|
|
1129
|
+
pnpm typecheck # Check TypeScript types
|
|
1130
|
+
\`\`\`
|
|
1131
|
+
|
|
1132
|
+
**Use before gates** to catch simple issues early. These are faster than full \`pnpm gates\`.`;
|
|
1133
|
+
}
|
|
1134
|
+
function generateLaneSelectionSection() {
|
|
1135
|
+
return `## Lane Selection
|
|
1136
|
+
|
|
1137
|
+
When creating new WUs, use the correct lane to enable parallelization:
|
|
1138
|
+
|
|
1139
|
+
\`\`\`bash
|
|
1140
|
+
# Get lane suggestion based on code paths and description
|
|
1141
|
+
pnpm wu:infer-lane --id WU-XXX
|
|
1142
|
+
|
|
1143
|
+
# Or infer from manual inputs
|
|
1144
|
+
pnpm wu:infer-lane --paths "tools/**" --desc "CLI improvements"
|
|
1145
|
+
\`\`\`
|
|
1146
|
+
|
|
1147
|
+
**Lane source of truth**: See \`workspace.yaml\` \`software_delivery.lanes.definitions\`.
|
|
1148
|
+
Inference reads configured lane names, descriptions, and \`code_paths\` from that file.
|
|
1149
|
+
|
|
1150
|
+
**Why lanes matter**: WIP=1 per lane means correct lane selection enables parallel work across lanes.`;
|
|
1151
|
+
}
|
|
1152
|
+
function generateWorktreePathGuidance(worktreePath) {
|
|
1153
|
+
if (!worktreePath) {
|
|
1154
|
+
return "";
|
|
1155
|
+
}
|
|
1156
|
+
return `## Worktree Path Guidance (WU-2362)
|
|
1157
|
+
|
|
1158
|
+
**Your worktree:** \`${worktreePath}\`
|
|
1159
|
+
|
|
1160
|
+
### Finding the Worktree Root
|
|
1161
|
+
|
|
1162
|
+
Sub-agents may inherit the parent's cwd (main checkout). To find the actual worktree root:
|
|
1163
|
+
|
|
1164
|
+
\`\`\`bash
|
|
1165
|
+
# Get the worktree root (not main checkout)
|
|
1166
|
+
git rev-parse --show-toplevel
|
|
1167
|
+
\`\`\`
|
|
1168
|
+
|
|
1169
|
+
### Stamp Creation
|
|
1170
|
+
|
|
1171
|
+
When creating \`.lumenflow/\` stamps or other artifacts:
|
|
1172
|
+
|
|
1173
|
+
1. **ALWAYS** create stamps in the **worktree**, not main
|
|
1174
|
+
2. Use \`git rev-parse --show-toplevel\` to get the correct base path
|
|
1175
|
+
3. Stamps created on main will be lost when the worktree merges
|
|
1176
|
+
|
|
1177
|
+
\`\`\`bash
|
|
1178
|
+
# CORRECT: Create stamp in worktree
|
|
1179
|
+
WORKTREE_ROOT=$(git rev-parse --show-toplevel)
|
|
1180
|
+
mkdir -p "$WORKTREE_ROOT/.lumenflow/agent-runs"
|
|
1181
|
+
touch "$WORKTREE_ROOT/.lumenflow/agent-runs/code-reviewer.stamp"
|
|
1182
|
+
|
|
1183
|
+
# WRONG: Hardcoded path to main
|
|
1184
|
+
# touch /path/to/main/.lumenflow/agent-runs/code-reviewer.stamp
|
|
1185
|
+
\`\`\`
|
|
1186
|
+
|
|
1187
|
+
### Why This Matters
|
|
1188
|
+
|
|
1189
|
+
- Stamps on main get overwritten by worktree merge
|
|
1190
|
+
- \`wu:done\` validates stamps exist in the worktree branch
|
|
1191
|
+
- Parallel WUs in other lanes won't see your stamps if on main`;
|
|
1192
|
+
}
|
|
1193
|
+
function generateBugDiscoverySection(id) {
|
|
1194
|
+
return `## Bug Discovery (Mid-WU Issue Capture)
|
|
1195
|
+
|
|
1196
|
+
If you discover a bug or issue **outside the scope of this WU**:
|
|
1197
|
+
|
|
1198
|
+
1. **Capture it immediately** using:
|
|
1199
|
+
\`\`\`bash
|
|
1200
|
+
pnpm mem:create 'Bug: <description>' --type discovery --tags bug,scope-creep --wu ${id}
|
|
1201
|
+
\`\`\`
|
|
1202
|
+
|
|
1203
|
+
2. **Continue with your WU** \u2014 do not fix bugs outside your scope
|
|
1204
|
+
3. **Reference in notes** \u2014 mention the mem node ID in your completion notes
|
|
1205
|
+
|
|
1206
|
+
### NEVER use wu:create for discovered issues
|
|
1207
|
+
|
|
1208
|
+
**Do NOT use \`wu:create\` directly for bugs discovered mid-WU.**
|
|
1209
|
+
|
|
1210
|
+
- \`mem:create\` = **capture** (immediate, no human approval needed)
|
|
1211
|
+
- \`wu:create\` = **planned work** (requires human triage and approval)
|
|
1212
|
+
|
|
1213
|
+
Discovered issues MUST go through human triage before becoming WUs.
|
|
1214
|
+
Using \`wu:create\` directly bypasses the triage workflow and creates
|
|
1215
|
+
unreviewed work items.
|
|
1216
|
+
|
|
1217
|
+
### When to Capture
|
|
1218
|
+
|
|
1219
|
+
- Found a bug in code NOT in your \`code_paths\`
|
|
1220
|
+
- Discovered an issue that would require >10 lines to fix
|
|
1221
|
+
- Encountered broken behaviour unrelated to your acceptance criteria
|
|
1222
|
+
|
|
1223
|
+
### Triage Workflow
|
|
1224
|
+
|
|
1225
|
+
After WU completion, bugs can be promoted to Bug WUs by humans:
|
|
1226
|
+
\`\`\`bash
|
|
1227
|
+
pnpm mem:triage --wu ${id} # List discoveries for this WU
|
|
1228
|
+
pnpm mem:triage --promote <node-id> --lane "<lane>" # Create Bug WU (human action)
|
|
1229
|
+
\`\`\`
|
|
1230
|
+
|
|
1231
|
+
See: https://lumenflow.dev/reference/agent-invocation-guide/ \xA7Bug Discovery`;
|
|
1232
|
+
}
|
|
1233
|
+
function generateLaneGuidance(lane) {
|
|
1234
|
+
if (!lane)
|
|
1235
|
+
return "";
|
|
1236
|
+
const laneParent = (lane.split(":")[0] ?? "").trim();
|
|
1237
|
+
const guidance = {
|
|
1238
|
+
Operations: `## Lane-Specific: Tooling
|
|
1239
|
+
|
|
1240
|
+
- Update tool documentation in tools/README.md or relevant docs if adding new CLI commands`,
|
|
1241
|
+
Intelligence: `## Lane-Specific: Intelligence
|
|
1242
|
+
|
|
1243
|
+
- For prompt/classification work, run the project-specific evaluation command defined by your local templates/config
|
|
1244
|
+
- Follow prompt versioning guidelines documented for this project`,
|
|
1245
|
+
Experience: `## Lane-Specific: Experience
|
|
1246
|
+
|
|
1247
|
+
- Follow design system tokens defined in the project
|
|
1248
|
+
- Ensure accessibility compliance (WCAG 2.1 AA)`,
|
|
1249
|
+
Core: `## Lane-Specific: Core
|
|
1250
|
+
|
|
1251
|
+
- Maintain hexagonal architecture boundaries
|
|
1252
|
+
- Update domain model documentation if changing entities`
|
|
1253
|
+
};
|
|
1254
|
+
return guidance[laneParent] || "";
|
|
1255
|
+
}
|
|
1256
|
+
function generateActionSection(doc, id, config) {
|
|
1257
|
+
const isAlreadyClaimed = doc.claimed_at && doc.worktree_path;
|
|
1258
|
+
const resolvedConfig = config ?? getConfig();
|
|
1259
|
+
const worktreesDirSegment = resolveWorktreesDirSegment(resolvedConfig);
|
|
1260
|
+
if (isAlreadyClaimed) {
|
|
1261
|
+
return `This WU is already claimed. Continue implementation in worktree following all standards above.
|
|
1262
|
+
|
|
1263
|
+
cd ${doc.worktree_path}`;
|
|
1264
|
+
}
|
|
1265
|
+
const laneSlug = (doc.lane || "unknown").toLowerCase().replace(/[:\s]+/g, "-").replace(/-+/g, "-");
|
|
1266
|
+
return `**FIRST: Claim this WU before starting work:**
|
|
1267
|
+
|
|
1268
|
+
\`\`\`bash
|
|
1269
|
+
pnpm wu:claim --id ${id} --lane "${doc.lane}"
|
|
1270
|
+
cd ${worktreesDirSegment}/${laneSlug}-${id.toLowerCase()}
|
|
1271
|
+
\`\`\`
|
|
1272
|
+
|
|
1273
|
+
Then implement following all standards above.
|
|
1274
|
+
|
|
1275
|
+
**CRITICAL:** Never use \`git worktree add\` directly. Always use \`pnpm wu:claim\` to ensure:
|
|
1276
|
+
- Event tracking in ${LUMENFLOW_PATHS.WU_EVENTS}
|
|
1277
|
+
- Lane lock acquisition (WIP=1 enforcement)
|
|
1278
|
+
- Session tracking for context recovery`;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
// ../core/dist/spawn-prompt-helpers.js
|
|
1282
|
+
import path2 from "path";
|
|
1283
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1284
|
+
import { minimatch } from "minimatch";
|
|
1285
|
+
var MANDATORY_TRIGGERS = {
|
|
1286
|
+
// No mandatory triggers for LumenFlow framework development.
|
|
1287
|
+
};
|
|
1288
|
+
function formatAcceptance(acceptance) {
|
|
1289
|
+
if (!acceptance || acceptance.length === 0) {
|
|
1290
|
+
return "- No acceptance criteria defined";
|
|
1291
|
+
}
|
|
1292
|
+
return acceptance.map((item) => `- [ ] ${item}`).join("\n");
|
|
1293
|
+
}
|
|
1294
|
+
function formatSpecRefs(specRefs) {
|
|
1295
|
+
if (!specRefs || specRefs.length === 0) {
|
|
1296
|
+
return "";
|
|
1297
|
+
}
|
|
1298
|
+
const formattedRefs = specRefs.map((ref) => {
|
|
1299
|
+
if (ref.startsWith("lumenflow://") || ref.startsWith("~/") || ref.startsWith("$LUMENFLOW_HOME") || ref.startsWith("/") && ref.includes(".lumenflow")) {
|
|
1300
|
+
return `- ${ref} (external - read with filesystem access)`;
|
|
1301
|
+
}
|
|
1302
|
+
return `- ${ref}`;
|
|
1303
|
+
});
|
|
1304
|
+
return formattedRefs.join("\n");
|
|
1305
|
+
}
|
|
1306
|
+
function formatRisks(risks) {
|
|
1307
|
+
if (!risks || risks.length === 0) {
|
|
1308
|
+
return "";
|
|
1309
|
+
}
|
|
1310
|
+
return risks.map((risk) => `- ${risk}`).join("\n");
|
|
1311
|
+
}
|
|
1312
|
+
function formatManualTests(manualTests) {
|
|
1313
|
+
if (!manualTests || manualTests.length === 0) {
|
|
1314
|
+
return "";
|
|
1315
|
+
}
|
|
1316
|
+
return manualTests.map((test) => `- [ ] ${test}`).join("\n");
|
|
1317
|
+
}
|
|
1318
|
+
function generateImplementationContext(doc) {
|
|
1319
|
+
const sections = [];
|
|
1320
|
+
const refs = formatSpecRefs(doc.spec_refs);
|
|
1321
|
+
if (refs) {
|
|
1322
|
+
sections.push(`## References
|
|
1323
|
+
|
|
1324
|
+
${refs}`);
|
|
1325
|
+
}
|
|
1326
|
+
if (doc.notes && doc.notes.trim()) {
|
|
1327
|
+
sections.push(`## Implementation Notes
|
|
1328
|
+
|
|
1329
|
+
${doc.notes.trim()}`);
|
|
1330
|
+
}
|
|
1331
|
+
const risks = formatRisks(doc.risks);
|
|
1332
|
+
if (risks) {
|
|
1333
|
+
sections.push(`## Risks
|
|
1334
|
+
|
|
1335
|
+
${risks}`);
|
|
1336
|
+
}
|
|
1337
|
+
const manualTests = formatManualTests(doc.tests?.manual);
|
|
1338
|
+
if (manualTests) {
|
|
1339
|
+
sections.push(`## Manual Verification
|
|
1340
|
+
|
|
1341
|
+
${manualTests}`);
|
|
1342
|
+
}
|
|
1343
|
+
if (sections.length === 0) {
|
|
1344
|
+
return "";
|
|
1345
|
+
}
|
|
1346
|
+
return sections.join("\n\n---\n\n");
|
|
1347
|
+
}
|
|
1348
|
+
function detectMandatoryAgents(codePaths) {
|
|
1349
|
+
if (!codePaths || codePaths.length === 0) {
|
|
1350
|
+
return [];
|
|
1351
|
+
}
|
|
1352
|
+
const triggeredAgents = /* @__PURE__ */ new Set();
|
|
1353
|
+
for (const [agentName, patterns] of Object.entries(MANDATORY_TRIGGERS)) {
|
|
1354
|
+
const isTriggered = codePaths.some((filePath) => patterns.some((pattern) => minimatch(filePath, pattern)));
|
|
1355
|
+
if (isTriggered) {
|
|
1356
|
+
triggeredAgents.add(agentName);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
return Array.from(triggeredAgents);
|
|
1360
|
+
}
|
|
1361
|
+
function generateMandatoryAgentSection(mandatoryAgents, _id) {
|
|
1362
|
+
if (mandatoryAgents.length === 0) {
|
|
1363
|
+
return "";
|
|
1364
|
+
}
|
|
1365
|
+
const agentList = mandatoryAgents.map((agent) => ` - ${agent}`).join("\n");
|
|
1366
|
+
return `
|
|
1367
|
+
## Mandatory Agents (MUST invoke before wu:done)
|
|
1368
|
+
|
|
1369
|
+
Based on code_paths, the following agents MUST be invoked:
|
|
1370
|
+
|
|
1371
|
+
${agentList}
|
|
1372
|
+
|
|
1373
|
+
Run: pnpm orchestrate:monitor to check agent status
|
|
1374
|
+
`;
|
|
1375
|
+
}
|
|
1376
|
+
function generatePreamble(id, strategy) {
|
|
1377
|
+
return strategy.getPreamble(id);
|
|
1378
|
+
}
|
|
1379
|
+
function generateClientBlocksSection(clientContext) {
|
|
1380
|
+
if (!clientContext?.config?.blocks?.length)
|
|
1381
|
+
return "";
|
|
1382
|
+
const blocks = clientContext.config.blocks.map((block) => `### ${block.title}
|
|
1383
|
+
|
|
1384
|
+
${block.content}`).join("\n\n");
|
|
1385
|
+
return `## Client Guidance (${clientContext.name})
|
|
1386
|
+
|
|
1387
|
+
${blocks}`;
|
|
1388
|
+
}
|
|
1389
|
+
function codePathMatchesInvariant(invariant, codePaths) {
|
|
1390
|
+
switch (invariant.type) {
|
|
1391
|
+
case INVARIANT_TYPES.FORBIDDEN_FILE:
|
|
1392
|
+
case INVARIANT_TYPES.REQUIRED_FILE: {
|
|
1393
|
+
const invariantPath = invariant.path;
|
|
1394
|
+
if (!invariantPath)
|
|
1395
|
+
return false;
|
|
1396
|
+
return codePaths.some((p) => p === invariantPath || minimatch(p, invariantPath) || minimatch(invariantPath, p));
|
|
1397
|
+
}
|
|
1398
|
+
case INVARIANT_TYPES.MUTUAL_EXCLUSIVITY: {
|
|
1399
|
+
const invariantPaths = invariant.paths ?? [];
|
|
1400
|
+
return codePaths.some((p) => invariantPaths.some((invPath) => p === invPath || minimatch(p, invPath)));
|
|
1401
|
+
}
|
|
1402
|
+
case INVARIANT_TYPES.FORBIDDEN_PATTERN:
|
|
1403
|
+
case INVARIANT_TYPES.REQUIRED_PATTERN:
|
|
1404
|
+
return invariant.scope?.some((scopePattern) => codePaths.some((p) => minimatch(p, scopePattern))) ?? false;
|
|
1405
|
+
case INVARIANT_TYPES.FORBIDDEN_IMPORT: {
|
|
1406
|
+
const fromPattern = invariant.from;
|
|
1407
|
+
if (!fromPattern)
|
|
1408
|
+
return false;
|
|
1409
|
+
return codePaths.some((p) => minimatch(p, fromPattern));
|
|
1410
|
+
}
|
|
1411
|
+
default:
|
|
1412
|
+
return false;
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
function formatInvariantForOutput(inv) {
|
|
1416
|
+
const lines = [`### ${inv.id} (${inv.type})`, "", inv.description, ""];
|
|
1417
|
+
if (inv.message) {
|
|
1418
|
+
lines.push(`**Action:** ${inv.message}`, "");
|
|
1419
|
+
}
|
|
1420
|
+
if (inv.path) {
|
|
1421
|
+
lines.push(`**Path:** \`${inv.path}\``);
|
|
1422
|
+
}
|
|
1423
|
+
if (inv.paths) {
|
|
1424
|
+
const formattedPaths = inv.paths.map((p) => `\`${p}\``).join(", ");
|
|
1425
|
+
lines.push(`**Paths:** ${formattedPaths}`);
|
|
1426
|
+
}
|
|
1427
|
+
if (inv.from) {
|
|
1428
|
+
lines.push(`**From:** \`${inv.from}\``);
|
|
1429
|
+
}
|
|
1430
|
+
if (inv.cannot_import && Array.isArray(inv.cannot_import)) {
|
|
1431
|
+
const formattedImports = inv.cannot_import.map((m) => `\`${m}\``).join(", ");
|
|
1432
|
+
lines.push(`**Cannot Import:** ${formattedImports}`);
|
|
1433
|
+
}
|
|
1434
|
+
if (inv.pattern && (inv.type === INVARIANT_TYPES.REQUIRED_PATTERN || inv.type === INVARIANT_TYPES.FORBIDDEN_PATTERN)) {
|
|
1435
|
+
lines.push(`**Pattern:** \`${inv.pattern}\``);
|
|
1436
|
+
}
|
|
1437
|
+
if (inv.scope && Array.isArray(inv.scope)) {
|
|
1438
|
+
const formattedScope = inv.scope.map((s) => `\`${s}\``).join(", ");
|
|
1439
|
+
lines.push(`**Scope:** ${formattedScope}`);
|
|
1440
|
+
}
|
|
1441
|
+
lines.push("");
|
|
1442
|
+
return lines;
|
|
1443
|
+
}
|
|
1444
|
+
function generateInvariantsPriorArtSection(codePaths) {
|
|
1445
|
+
if (!codePaths || codePaths.length === 0) {
|
|
1446
|
+
return "";
|
|
1447
|
+
}
|
|
1448
|
+
const invariantsPath = path2.resolve("tools/invariants.yml");
|
|
1449
|
+
if (!existsSync3(invariantsPath)) {
|
|
1450
|
+
return "";
|
|
1451
|
+
}
|
|
1452
|
+
let invariants;
|
|
1453
|
+
try {
|
|
1454
|
+
invariants = loadInvariants(invariantsPath);
|
|
1455
|
+
} catch {
|
|
1456
|
+
return "";
|
|
1457
|
+
}
|
|
1458
|
+
if (!invariants || invariants.length === 0) {
|
|
1459
|
+
return "";
|
|
1460
|
+
}
|
|
1461
|
+
const relevantInvariants = invariants.filter((inv) => codePathMatchesInvariant(inv, codePaths));
|
|
1462
|
+
if (relevantInvariants.length === 0) {
|
|
1463
|
+
return "";
|
|
1464
|
+
}
|
|
1465
|
+
const lines = [
|
|
1466
|
+
"## Invariants/Prior-Art (WU-2252)",
|
|
1467
|
+
"",
|
|
1468
|
+
"The following repo invariants are relevant to your code_paths:",
|
|
1469
|
+
"",
|
|
1470
|
+
...relevantInvariants.flatMap(formatInvariantForOutput),
|
|
1471
|
+
"**IMPORTANT:** Do not create specs or acceptance criteria that conflict with these invariants."
|
|
1472
|
+
];
|
|
1473
|
+
return lines.join("\n");
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
// ../core/dist/spawn-task-builder.js
|
|
1477
|
+
var DEFAULT_WORKTREES_DIR_SEGMENT3 = DIRECTORIES.WORKTREES.replace(/\/+$/g, "");
|
|
1478
|
+
function normalizeDirectorySegment2(value) {
|
|
1479
|
+
return value.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
|
|
1480
|
+
}
|
|
1481
|
+
function resolveWorktreesDirSegment2(config) {
|
|
1482
|
+
const normalized = normalizeDirectorySegment2(config.directories.worktrees);
|
|
1483
|
+
return normalized.length > 0 ? normalized : DEFAULT_WORKTREES_DIR_SEGMENT3;
|
|
1484
|
+
}
|
|
1485
|
+
function resolveWorktreePathHint(doc, id, config) {
|
|
1486
|
+
if (doc.worktree_path) {
|
|
1487
|
+
return doc.worktree_path;
|
|
1488
|
+
}
|
|
1489
|
+
return `${resolveWorktreesDirSegment2(config)}/<lane>-${id.toLowerCase()}`;
|
|
1490
|
+
}
|
|
1491
|
+
function generateTaskInvocation(doc, id, strategy, options = {}) {
|
|
1492
|
+
const codePaths = doc.code_paths || [];
|
|
1493
|
+
const mandatoryAgents = detectMandatoryAgents(codePaths);
|
|
1494
|
+
const preamble = generatePreamble(id, strategy);
|
|
1495
|
+
const clientContext = options.client;
|
|
1496
|
+
const config = options.config || getConfig();
|
|
1497
|
+
const worktreesDirSegment = resolveWorktreesDirSegment2(config);
|
|
1498
|
+
const worktreePathHint = resolveWorktreePathHint(doc, id, config);
|
|
1499
|
+
const mainRef = `${config.git.defaultRemote}/${config.git.mainBranch}`;
|
|
1500
|
+
const policy = resolvePolicy(config);
|
|
1501
|
+
const classificationConfig = config?.methodology?.work_classification;
|
|
1502
|
+
const classification = classifyWork({
|
|
1503
|
+
code_paths: doc.code_paths,
|
|
1504
|
+
lane: doc.lane,
|
|
1505
|
+
type: doc.type,
|
|
1506
|
+
description: doc.description
|
|
1507
|
+
}, classificationConfig);
|
|
1508
|
+
const testGuidance = generatePolicyBasedTestGuidance(doc.type || "feature", policy, {
|
|
1509
|
+
testMethodologyHint: classification.testMethodologyHint
|
|
1510
|
+
});
|
|
1511
|
+
const enforcementSummary = generateEnforcementSummary(policy, {
|
|
1512
|
+
testMethodologyHint: classification.testMethodologyHint
|
|
1513
|
+
});
|
|
1514
|
+
const mandatoryStandards = generateMandatoryStandards(policy, {
|
|
1515
|
+
testMethodologyHint: classification.testMethodologyHint
|
|
1516
|
+
});
|
|
1517
|
+
const codeCraftGuidance = generateCodeCraftGuidance();
|
|
1518
|
+
const readBeforeWrite = generateReadBeforeWriteDiscipline();
|
|
1519
|
+
const clientSkillsGuidance = generateClientSkillsGuidance(clientContext);
|
|
1520
|
+
const skillsSection = generateSkillsSelectionSection(doc, config, clientContext?.name) + (clientSkillsGuidance ? `
|
|
1521
|
+
${clientSkillsGuidance}` : "");
|
|
1522
|
+
const clientBlocks = generateClientBlocksSection(clientContext);
|
|
1523
|
+
const mandatorySection = generateMandatoryAgentSection(mandatoryAgents, id);
|
|
1524
|
+
const laneGuidance = generateLaneGuidance(doc.lane);
|
|
1525
|
+
const bugDiscoverySection = generateBugDiscoverySection(id);
|
|
1526
|
+
const action = generateActionSection(doc, id, config);
|
|
1527
|
+
const selfReviewDirective = generateSelfReviewDirective(id);
|
|
1528
|
+
const shouldIncludeTddCheckpoint = classification.testMethodologyHint !== TEST_METHODOLOGY_HINTS.SMOKE_TEST && classification.testMethodologyHint !== TEST_METHODOLOGY_HINTS.STRUCTURED_CONTENT && policy.testing !== "none";
|
|
1529
|
+
const constraints = generateConstraints(id, {
|
|
1530
|
+
includeTddCheckpoint: shouldIncludeTddCheckpoint,
|
|
1531
|
+
mainRef,
|
|
1532
|
+
worktreesDirSegment
|
|
1533
|
+
});
|
|
1534
|
+
const designContextSection = generateDesignContextSection(classification);
|
|
1535
|
+
const implementationContext = generateImplementationContext(doc);
|
|
1536
|
+
const invariantsPriorArt = generateInvariantsPriorArtSection(codePaths);
|
|
1537
|
+
const effortScaling = generateEffortScalingRules();
|
|
1538
|
+
const parallelToolCalls = generateParallelToolCallGuidance();
|
|
1539
|
+
const searchHeuristics = generateIterativeSearchHeuristics();
|
|
1540
|
+
const tokenBudget = generateTokenBudgetAwareness(id);
|
|
1541
|
+
const completionFormat = generateCompletionFormat(id);
|
|
1542
|
+
const agentCoordination = generateAgentCoordinationSection(id);
|
|
1543
|
+
const quickFix = generateQuickFixCommands();
|
|
1544
|
+
const laneSelection = generateLaneSelectionSection();
|
|
1545
|
+
const worktreeGuidance = generateWorktreePathGuidance(doc.worktree_path);
|
|
1546
|
+
const shouldIncludeMemoryContext = options.includeMemoryContext && !options.noContext;
|
|
1547
|
+
const memoryContextSection = shouldIncludeMemoryContext ? options.memoryContextContent || "" : "";
|
|
1548
|
+
const executionModeSection = generateExecutionModeSection(options);
|
|
1549
|
+
const thinkToolGuidance = generateThinkToolGuidance(options);
|
|
1550
|
+
const thinkingSections = [executionModeSection, thinkToolGuidance].filter((section) => section.length > 0).join("\n\n---\n\n");
|
|
1551
|
+
const thinkingBlock = thinkingSections ? `${thinkingSections}
|
|
1552
|
+
|
|
1553
|
+
---
|
|
1554
|
+
|
|
1555
|
+
` : "";
|
|
1556
|
+
const taskPrompt = `${TRUNCATION_WARNING_BANNER}<task>
|
|
1557
|
+
${preamble}
|
|
1558
|
+
</task>
|
|
1559
|
+
|
|
1560
|
+
---
|
|
1561
|
+
|
|
1562
|
+
${testGuidance}
|
|
1563
|
+
|
|
1564
|
+
---
|
|
1565
|
+
|
|
1566
|
+
# ${id}: ${doc.title || "Untitled"}
|
|
1567
|
+
|
|
1568
|
+
## WU Details
|
|
1569
|
+
|
|
1570
|
+
- **ID:** ${id}
|
|
1571
|
+
- **Lane:** ${doc.lane || "Unknown"}
|
|
1572
|
+
- **Type:** ${doc.type || "feature"}
|
|
1573
|
+
- **Status:** ${doc.status || "unknown"}
|
|
1574
|
+
- **Worktree:** ${worktreePathHint}
|
|
1575
|
+
|
|
1576
|
+
## Description
|
|
1577
|
+
|
|
1578
|
+
${doc.description || "No description provided."}
|
|
1579
|
+
|
|
1580
|
+
## Acceptance Criteria
|
|
1581
|
+
|
|
1582
|
+
${formatAcceptance(doc.acceptance)}
|
|
1583
|
+
|
|
1584
|
+
## Code Paths
|
|
1585
|
+
|
|
1586
|
+
${codePaths.length > 0 ? codePaths.map((p) => `- ${p}`).join("\n") : "- No code paths defined"}
|
|
1587
|
+
${mandatorySection}${invariantsPriorArt ? `---
|
|
1588
|
+
|
|
1589
|
+
${invariantsPriorArt}
|
|
1590
|
+
|
|
1591
|
+
` : ""}${implementationContext ? `---
|
|
1592
|
+
|
|
1593
|
+
${implementationContext}
|
|
1594
|
+
|
|
1595
|
+
` : ""}---
|
|
1596
|
+
|
|
1597
|
+
${thinkingBlock}${skillsSection}
|
|
1598
|
+
${memoryContextSection ? `---
|
|
1599
|
+
|
|
1600
|
+
${memoryContextSection}
|
|
1601
|
+
|
|
1602
|
+
` : ""}---
|
|
1603
|
+
|
|
1604
|
+
${mandatoryStandards}
|
|
1605
|
+
|
|
1606
|
+
---
|
|
1607
|
+
|
|
1608
|
+
${enforcementSummary}
|
|
1609
|
+
|
|
1610
|
+
---
|
|
1611
|
+
|
|
1612
|
+
${codeCraftGuidance}
|
|
1613
|
+
|
|
1614
|
+
---
|
|
1615
|
+
|
|
1616
|
+
${readBeforeWrite}
|
|
1617
|
+
|
|
1618
|
+
${designContextSection ? `---
|
|
1619
|
+
|
|
1620
|
+
${designContextSection}
|
|
1621
|
+
|
|
1622
|
+
` : ""}${clientBlocks ? `---
|
|
1623
|
+
|
|
1624
|
+
${clientBlocks}
|
|
1625
|
+
|
|
1626
|
+
` : ""}${worktreeGuidance ? `---
|
|
1627
|
+
|
|
1628
|
+
${worktreeGuidance}
|
|
1629
|
+
|
|
1630
|
+
` : ""}---
|
|
1631
|
+
|
|
1632
|
+
${bugDiscoverySection}
|
|
1633
|
+
|
|
1634
|
+
---
|
|
1635
|
+
|
|
1636
|
+
${effortScaling}
|
|
1637
|
+
|
|
1638
|
+
---
|
|
1639
|
+
|
|
1640
|
+
${parallelToolCalls}
|
|
1641
|
+
|
|
1642
|
+
---
|
|
1643
|
+
|
|
1644
|
+
${searchHeuristics}
|
|
1645
|
+
|
|
1646
|
+
---
|
|
1647
|
+
|
|
1648
|
+
${tokenBudget}
|
|
1649
|
+
|
|
1650
|
+
---
|
|
1651
|
+
|
|
1652
|
+
${completionFormat}
|
|
1653
|
+
|
|
1654
|
+
---
|
|
1655
|
+
|
|
1656
|
+
${agentCoordination}
|
|
1657
|
+
|
|
1658
|
+
---
|
|
1659
|
+
|
|
1660
|
+
${quickFix}
|
|
1661
|
+
|
|
1662
|
+
---
|
|
1663
|
+
|
|
1664
|
+
${laneSelection}
|
|
1665
|
+
|
|
1666
|
+
---
|
|
1667
|
+
|
|
1668
|
+
${laneGuidance}${laneGuidance ? "\n\n---\n\n" : ""}## Action
|
|
1669
|
+
|
|
1670
|
+
${action}
|
|
1671
|
+
|
|
1672
|
+
---
|
|
1673
|
+
|
|
1674
|
+
${selfReviewDirective}
|
|
1675
|
+
|
|
1676
|
+
${constraints}`;
|
|
1677
|
+
const escapedPrompt = taskPrompt.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1678
|
+
const openTag = '<antml:invoke name="Task">';
|
|
1679
|
+
const closeTag = "</antml:invoke>";
|
|
1680
|
+
const paramOpen = '<antml:parameter name="';
|
|
1681
|
+
const paramClose = "</antml:parameter>";
|
|
1682
|
+
const invocation = [
|
|
1683
|
+
"<antml:function_calls>",
|
|
1684
|
+
openTag,
|
|
1685
|
+
`${paramOpen}subagent_type">general-purpose${paramClose}`,
|
|
1686
|
+
`${paramOpen}description">Execute ${id}${paramClose}`,
|
|
1687
|
+
`${paramOpen}prompt">${escapedPrompt}${paramClose}`,
|
|
1688
|
+
closeTag,
|
|
1689
|
+
"</antml:function_calls>"
|
|
1690
|
+
].join("\n");
|
|
1691
|
+
return invocation;
|
|
1692
|
+
}
|
|
1693
|
+
function generateCodexPrompt(doc, id, strategy, options = {}) {
|
|
1694
|
+
const codePaths = doc.code_paths || [];
|
|
1695
|
+
const mandatoryAgents = detectMandatoryAgents(codePaths);
|
|
1696
|
+
const preamble = generatePreamble(id, strategy);
|
|
1697
|
+
const mandatorySection = generateMandatoryAgentSection(mandatoryAgents, id);
|
|
1698
|
+
const laneGuidance = generateLaneGuidance(doc.lane);
|
|
1699
|
+
const bugDiscoverySection = generateBugDiscoverySection(id);
|
|
1700
|
+
const implementationContext = generateImplementationContext(doc);
|
|
1701
|
+
const clientContext = options.client;
|
|
1702
|
+
const config = options.config || getConfig();
|
|
1703
|
+
const classificationConfig = config?.methodology?.work_classification;
|
|
1704
|
+
const classification = classifyWork({
|
|
1705
|
+
code_paths: doc.code_paths,
|
|
1706
|
+
lane: doc.lane,
|
|
1707
|
+
type: doc.type,
|
|
1708
|
+
description: doc.description
|
|
1709
|
+
}, classificationConfig);
|
|
1710
|
+
const policy = resolvePolicy(config);
|
|
1711
|
+
const worktreesDirSegment = resolveWorktreesDirSegment2(config);
|
|
1712
|
+
const worktreePathHint = resolveWorktreePathHint(doc, id, config);
|
|
1713
|
+
const mainRef = `${config.git.defaultRemote}/${config.git.mainBranch}`;
|
|
1714
|
+
const action = generateActionSection(doc, id, config);
|
|
1715
|
+
const shouldIncludeTddCheckpoint = classification.testMethodologyHint !== TEST_METHODOLOGY_HINTS.SMOKE_TEST && classification.testMethodologyHint !== TEST_METHODOLOGY_HINTS.STRUCTURED_CONTENT && policy.testing !== "none";
|
|
1716
|
+
const constraints = generateCodexConstraints(id, {
|
|
1717
|
+
includeTddCheckpoint: shouldIncludeTddCheckpoint,
|
|
1718
|
+
mainRef,
|
|
1719
|
+
worktreesDirSegment
|
|
1720
|
+
});
|
|
1721
|
+
const clientSkillsGuidance = generateClientSkillsGuidance(clientContext);
|
|
1722
|
+
const skillsSection = generateSkillsSelectionSection(doc, config, clientContext?.name) + (clientSkillsGuidance ? `
|
|
1723
|
+
${clientSkillsGuidance}` : "");
|
|
1724
|
+
const clientBlocks = generateClientBlocksSection(clientContext);
|
|
1725
|
+
const testGuidance = generatePolicyBasedTestGuidance(doc.type || "feature", policy, {
|
|
1726
|
+
testMethodologyHint: classification.testMethodologyHint
|
|
1727
|
+
});
|
|
1728
|
+
const enforcementSummary = generateEnforcementSummary(policy, {
|
|
1729
|
+
testMethodologyHint: classification.testMethodologyHint
|
|
1730
|
+
});
|
|
1731
|
+
const mandatoryStandards = generateMandatoryStandards(policy, {
|
|
1732
|
+
testMethodologyHint: classification.testMethodologyHint
|
|
1733
|
+
});
|
|
1734
|
+
const codeCraftGuidance = generateCodeCraftGuidance();
|
|
1735
|
+
const readBeforeWrite = generateReadBeforeWriteDiscipline();
|
|
1736
|
+
const selfReviewDirective = generateSelfReviewDirective(id);
|
|
1737
|
+
const designContextSection = generateDesignContextSection(classification);
|
|
1738
|
+
const executionModeSection = generateExecutionModeSection(options);
|
|
1739
|
+
const thinkToolGuidance = generateThinkToolGuidance(options);
|
|
1740
|
+
const thinkingSections = [executionModeSection, thinkToolGuidance].filter((section) => section.length > 0).join("\n\n---\n\n");
|
|
1741
|
+
const thinkingBlock = thinkingSections ? `${thinkingSections}
|
|
1742
|
+
|
|
1743
|
+
---
|
|
1744
|
+
|
|
1745
|
+
` : "";
|
|
1746
|
+
return `# ${id}: ${doc.title || "Untitled"}
|
|
1747
|
+
|
|
1748
|
+
${testGuidance}
|
|
1749
|
+
|
|
1750
|
+
---
|
|
1751
|
+
|
|
1752
|
+
## Context
|
|
1753
|
+
|
|
1754
|
+
${preamble}
|
|
1755
|
+
|
|
1756
|
+
---
|
|
1757
|
+
|
|
1758
|
+
## WU Details
|
|
1759
|
+
|
|
1760
|
+
- **ID:** ${id}
|
|
1761
|
+
- **Lane:** ${doc.lane || "Unknown"}
|
|
1762
|
+
- **Type:** ${doc.type || "feature"}
|
|
1763
|
+
- **Status:** ${doc.status || "unknown"}
|
|
1764
|
+
- **Worktree:** ${worktreePathHint}
|
|
1765
|
+
|
|
1766
|
+
## Description
|
|
1767
|
+
|
|
1768
|
+
${doc.description || "No description provided."}
|
|
1769
|
+
|
|
1770
|
+
## Scope (code_paths)
|
|
1771
|
+
|
|
1772
|
+
Only change files within these paths:
|
|
1773
|
+
|
|
1774
|
+
${codePaths.length > 0 ? codePaths.map((p) => `- ${p}`).join("\n") : "- No code paths defined"}
|
|
1775
|
+
|
|
1776
|
+
## Acceptance Criteria
|
|
1777
|
+
|
|
1778
|
+
${formatAcceptance(doc.acceptance)}
|
|
1779
|
+
|
|
1780
|
+
---
|
|
1781
|
+
|
|
1782
|
+
${mandatoryStandards}
|
|
1783
|
+
|
|
1784
|
+
---
|
|
1785
|
+
|
|
1786
|
+
${enforcementSummary}
|
|
1787
|
+
|
|
1788
|
+
---
|
|
1789
|
+
|
|
1790
|
+
${codeCraftGuidance}
|
|
1791
|
+
|
|
1792
|
+
---
|
|
1793
|
+
|
|
1794
|
+
${readBeforeWrite}
|
|
1795
|
+
|
|
1796
|
+
${designContextSection ? `---
|
|
1797
|
+
|
|
1798
|
+
${designContextSection}
|
|
1799
|
+
|
|
1800
|
+
` : ""}---
|
|
1801
|
+
|
|
1802
|
+
${skillsSection}
|
|
1803
|
+
|
|
1804
|
+
---
|
|
1805
|
+
|
|
1806
|
+
## Action
|
|
1807
|
+
|
|
1808
|
+
${action}
|
|
1809
|
+
|
|
1810
|
+
---
|
|
1811
|
+
|
|
1812
|
+
## Verification
|
|
1813
|
+
|
|
1814
|
+
- Run in worktree: \`pnpm gates\`
|
|
1815
|
+
- From shared checkout: \`node packages/@lumenflow/agent/verification ${id}\`
|
|
1816
|
+
|
|
1817
|
+
---
|
|
1818
|
+
|
|
1819
|
+
${mandatorySection}${implementationContext ? `${implementationContext}
|
|
1820
|
+
|
|
1821
|
+
---
|
|
1822
|
+
|
|
1823
|
+
` : ""}${clientBlocks ? `${clientBlocks}
|
|
1824
|
+
|
|
1825
|
+
---
|
|
1826
|
+
|
|
1827
|
+
` : ""}${thinkingBlock}${bugDiscoverySection}
|
|
1828
|
+
|
|
1829
|
+
---
|
|
1830
|
+
|
|
1831
|
+
${laneGuidance}${laneGuidance ? "\n\n---\n\n" : ""}${selfReviewDirective}
|
|
1832
|
+
|
|
1833
|
+
---
|
|
1834
|
+
|
|
1835
|
+
${constraints}
|
|
1836
|
+
`;
|
|
1837
|
+
}
|
|
1838
|
+
function checkLaneOccupation(lane) {
|
|
1839
|
+
const lockStatus = checkLaneLock(lane);
|
|
1840
|
+
if (lockStatus.locked && lockStatus.metadata) {
|
|
1841
|
+
return lockStatus.metadata;
|
|
1842
|
+
}
|
|
1843
|
+
return null;
|
|
1844
|
+
}
|
|
1845
|
+
function generateLaneOccupationWarning(lockMetadata, targetWuId, options = {}) {
|
|
1846
|
+
const { isStale = false } = options;
|
|
1847
|
+
let warning = `\u26A0\uFE0F Lane "${lockMetadata.lane}" is occupied by ${lockMetadata.wuId}
|
|
1848
|
+
`;
|
|
1849
|
+
warning += ` This violates WIP=1 (Work In Progress limit of 1 per lane).
|
|
1850
|
+
|
|
1851
|
+
`;
|
|
1852
|
+
if (isStale) {
|
|
1853
|
+
warning += ` \u23F0 This lock is STALE (>24 hours old) - the WU may be abandoned.
|
|
1854
|
+
`;
|
|
1855
|
+
warning += ` Consider using pnpm wu:block --id ${lockMetadata.wuId} if work is stalled.
|
|
1856
|
+
|
|
1857
|
+
`;
|
|
1858
|
+
}
|
|
1859
|
+
warning += ` Options:
|
|
1860
|
+
`;
|
|
1861
|
+
warning += ` 1. Wait for ${lockMetadata.wuId} to complete or block
|
|
1862
|
+
`;
|
|
1863
|
+
warning += ` 2. Choose a different lane for ${targetWuId}
|
|
1864
|
+
`;
|
|
1865
|
+
warning += ` 3. Block ${lockMetadata.wuId} if work is stalled: pnpm wu:block --id ${lockMetadata.wuId}`;
|
|
1866
|
+
return warning;
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
export {
|
|
1870
|
+
DelegationStatus,
|
|
1871
|
+
DelegationRegistryStore,
|
|
1872
|
+
loadTemplatesWithOverrides,
|
|
1873
|
+
replaceTokens,
|
|
1874
|
+
evaluateCondition,
|
|
1875
|
+
validateSpawnArgs,
|
|
1876
|
+
generateExecutionModeSection,
|
|
1877
|
+
generateThinkToolGuidance,
|
|
1878
|
+
recordSpawnToRegistry,
|
|
1879
|
+
formatSpawnRecordedMessage,
|
|
1880
|
+
resolveClientConfig,
|
|
1881
|
+
generateClientSkillsGuidance,
|
|
1882
|
+
generateSkillsSelectionSection,
|
|
1883
|
+
TRUNCATION_WARNING_BANNER,
|
|
1884
|
+
SPAWN_END_SENTINEL,
|
|
1885
|
+
generateAgentCoordinationSection,
|
|
1886
|
+
generateTaskInvocation,
|
|
1887
|
+
generateCodexPrompt,
|
|
1888
|
+
checkLaneOccupation,
|
|
1889
|
+
generateLaneOccupationWarning
|
|
1890
|
+
};
|