cleargate 0.12.0 → 0.13.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/MANIFEST.json +13 -13
- package/dist/{chunk-HZPJ5QX4.js → chunk-EG6YGT2O.js} +315 -33
- package/dist/chunk-EG6YGT2O.js.map +1 -0
- package/dist/cli.cjs +612 -289
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +73 -37
- package/dist/cli.js.map +1 -1
- package/dist/lib/lifecycle-reconcile.cjs +318 -34
- package/dist/lib/lifecycle-reconcile.cjs.map +1 -1
- package/dist/lib/lifecycle-reconcile.d.cts +55 -4
- package/dist/lib/lifecycle-reconcile.d.ts +55 -4
- package/dist/lib/lifecycle-reconcile.js +7 -3
- package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +1 -1
- package/dist/templates/cleargate-planning/.claude/agents/developer.md +8 -4
- package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +2 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +73 -0
- package/dist/templates/cleargate-planning/.cleargate/templates/Bug.md +1 -1
- package/dist/templates/cleargate-planning/.cleargate/templates/CR.md +1 -1
- package/dist/templates/cleargate-planning/.cleargate/templates/epic.md +1 -1
- package/dist/templates/cleargate-planning/.cleargate/templates/hotfix.md +1 -1
- package/dist/templates/cleargate-planning/.cleargate/templates/initiative.md +1 -1
- package/dist/templates/cleargate-planning/.cleargate/templates/sprint_report.md +1 -1
- package/dist/templates/cleargate-planning/.cleargate/templates/story.md +1 -1
- package/dist/templates/cleargate-planning/CLAUDE.md +2 -0
- package/dist/templates/cleargate-planning/MANIFEST.json +13 -13
- package/package.json +8 -9
- package/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +1 -1
- package/templates/cleargate-planning/.claude/agents/developer.md +8 -4
- package/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +2 -0
- package/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +73 -0
- package/templates/cleargate-planning/.cleargate/templates/Bug.md +1 -1
- package/templates/cleargate-planning/.cleargate/templates/CR.md +1 -1
- package/templates/cleargate-planning/.cleargate/templates/epic.md +1 -1
- package/templates/cleargate-planning/.cleargate/templates/hotfix.md +1 -1
- package/templates/cleargate-planning/.cleargate/templates/initiative.md +1 -1
- package/templates/cleargate-planning/.cleargate/templates/sprint_report.md +1 -1
- package/templates/cleargate-planning/.cleargate/templates/story.md +1 -1
- package/templates/cleargate-planning/CLAUDE.md +2 -0
- package/templates/cleargate-planning/MANIFEST.json +13 -13
- package/dist/chunk-HZPJ5QX4.js.map +0 -1
|
@@ -37,11 +37,13 @@ __export(lifecycle_reconcile_exports, {
|
|
|
37
37
|
parseCommitMessage: () => parseCommitMessage,
|
|
38
38
|
reconcileCrossSprintOrphans: () => reconcileCrossSprintOrphans,
|
|
39
39
|
reconcileDecomposition: () => reconcileDecomposition,
|
|
40
|
-
reconcileLifecycle: () => reconcileLifecycle
|
|
40
|
+
reconcileLifecycle: () => reconcileLifecycle,
|
|
41
|
+
rollUpParentStatus: () => rollUpParentStatus,
|
|
42
|
+
walkActiveParents: () => walkActiveParents
|
|
41
43
|
});
|
|
42
44
|
module.exports = __toCommonJS(lifecycle_reconcile_exports);
|
|
43
|
-
var
|
|
44
|
-
var
|
|
45
|
+
var fs2 = __toESM(require("fs"), 1);
|
|
46
|
+
var path2 = __toESM(require("path"), 1);
|
|
45
47
|
var import_node_child_process = require("child_process");
|
|
46
48
|
|
|
47
49
|
// src/wiki/parse-frontmatter.ts
|
|
@@ -83,25 +85,305 @@ function parseFrontmatter(raw) {
|
|
|
83
85
|
return { fm: parsed, body };
|
|
84
86
|
}
|
|
85
87
|
|
|
88
|
+
// src/lib/parent-rollup.ts
|
|
89
|
+
var fs = __toESM(require("fs"), 1);
|
|
90
|
+
var path = __toESM(require("path"), 1);
|
|
91
|
+
function readFm(filePath) {
|
|
92
|
+
try {
|
|
93
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
94
|
+
const { fm } = parseFrontmatter(raw);
|
|
95
|
+
return fm;
|
|
96
|
+
} catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function extractId(fm, filePath) {
|
|
101
|
+
for (const key of [
|
|
102
|
+
"story_id",
|
|
103
|
+
"epic_id",
|
|
104
|
+
"sprint_id",
|
|
105
|
+
"bug_id",
|
|
106
|
+
"cr_id",
|
|
107
|
+
"initiative_id",
|
|
108
|
+
"hotfix_id"
|
|
109
|
+
]) {
|
|
110
|
+
const val = fm[key];
|
|
111
|
+
if (typeof val === "string" && val.trim() !== "") return val.trim();
|
|
112
|
+
}
|
|
113
|
+
const stem = path.basename(filePath, ".md");
|
|
114
|
+
return stem.split("_")[0] ?? stem;
|
|
115
|
+
}
|
|
116
|
+
function enumerateChildren(parentId, deliveryRoot, archiveRoot, fmCache) {
|
|
117
|
+
const pendingSyncDir = path.join(deliveryRoot, "pending-sync");
|
|
118
|
+
const results = [];
|
|
119
|
+
const pools = [];
|
|
120
|
+
if (fs.existsSync(archiveRoot)) pools.push(archiveRoot);
|
|
121
|
+
if (fs.existsSync(pendingSyncDir)) pools.push(pendingSyncDir);
|
|
122
|
+
for (const dir of pools) {
|
|
123
|
+
let entries;
|
|
124
|
+
try {
|
|
125
|
+
entries = fs.readdirSync(dir);
|
|
126
|
+
} catch {
|
|
127
|
+
entries = [];
|
|
128
|
+
}
|
|
129
|
+
for (const entry of entries) {
|
|
130
|
+
if (!entry.endsWith(".md")) continue;
|
|
131
|
+
const absPath = path.join(dir, entry);
|
|
132
|
+
let fm = fmCache.get(absPath);
|
|
133
|
+
if (fm === void 0) {
|
|
134
|
+
const parsed = readFm(absPath);
|
|
135
|
+
if (parsed === null) continue;
|
|
136
|
+
fm = parsed;
|
|
137
|
+
fmCache.set(absPath, fm);
|
|
138
|
+
}
|
|
139
|
+
const parentCleargateId = fm["parent_cleargate_id"];
|
|
140
|
+
const parentEpicRef = fm["parent_epic_ref"];
|
|
141
|
+
const isChild = typeof parentCleargateId === "string" && parentCleargateId.trim() === parentId || typeof parentEpicRef === "string" && parentEpicRef.trim() === parentId;
|
|
142
|
+
if (!isChild) continue;
|
|
143
|
+
const childId = extractId(fm, absPath);
|
|
144
|
+
const status = typeof fm["status"] === "string" ? fm["status"].trim() : "";
|
|
145
|
+
results.push({ id: childId, status });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return results;
|
|
149
|
+
}
|
|
150
|
+
async function rollUpParentStatusInternal(parentFilePath, opts, visited, fmCache) {
|
|
151
|
+
const { deliveryRoot, archiveRoot } = opts;
|
|
152
|
+
let fm = fmCache.get(parentFilePath);
|
|
153
|
+
if (fm === void 0) {
|
|
154
|
+
const raw = readFm(parentFilePath);
|
|
155
|
+
if (raw === null) {
|
|
156
|
+
throw new Error(`parent-rollup: cannot read frontmatter from ${parentFilePath}`);
|
|
157
|
+
}
|
|
158
|
+
fm = raw;
|
|
159
|
+
fmCache.set(parentFilePath, fm);
|
|
160
|
+
}
|
|
161
|
+
const parentId = extractId(fm, parentFilePath);
|
|
162
|
+
const currentStatus = typeof fm["status"] === "string" ? fm["status"].trim() : "";
|
|
163
|
+
if (ARTIFACT_TERMINAL_STATUSES.has(currentStatus)) {
|
|
164
|
+
return {
|
|
165
|
+
parent_id: parentId,
|
|
166
|
+
parent_path: parentFilePath,
|
|
167
|
+
current_status: currentStatus,
|
|
168
|
+
proposed_status: null,
|
|
169
|
+
coverage: "full",
|
|
170
|
+
terminal_children: [],
|
|
171
|
+
pending_children: [],
|
|
172
|
+
verdict: "no-op"
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
if (visited.has(parentId)) {
|
|
176
|
+
throw new Error(`parent-rollup: sub_epics cycle detected at ${parentId}`);
|
|
177
|
+
}
|
|
178
|
+
visited.add(parentId);
|
|
179
|
+
const subEpicsField = fm["sub_epics"];
|
|
180
|
+
const subEpics = Array.isArray(subEpicsField) && subEpicsField.length > 0 ? subEpicsField.filter((s) => typeof s === "string") : [];
|
|
181
|
+
if (subEpics.length > 0) {
|
|
182
|
+
const pendingSyncDir = path.join(deliveryRoot, "pending-sync");
|
|
183
|
+
const terminalSubEpics = [];
|
|
184
|
+
const pendingSubEpics = [];
|
|
185
|
+
for (const subEpicId of subEpics) {
|
|
186
|
+
let subEpicPath = null;
|
|
187
|
+
const candidateDirs = [pendingSyncDir, archiveRoot];
|
|
188
|
+
for (const dir of candidateDirs) {
|
|
189
|
+
if (!fs.existsSync(dir)) continue;
|
|
190
|
+
let entries;
|
|
191
|
+
try {
|
|
192
|
+
entries = fs.readdirSync(dir);
|
|
193
|
+
} catch {
|
|
194
|
+
entries = [];
|
|
195
|
+
}
|
|
196
|
+
for (const entry of entries) {
|
|
197
|
+
if (!entry.endsWith(".md")) continue;
|
|
198
|
+
const absPath = path.join(dir, entry);
|
|
199
|
+
let subFm2 = fmCache.get(absPath);
|
|
200
|
+
if (subFm2 === void 0) {
|
|
201
|
+
const parsed = readFm(absPath);
|
|
202
|
+
if (parsed === null) continue;
|
|
203
|
+
subFm2 = parsed;
|
|
204
|
+
fmCache.set(absPath, subFm2);
|
|
205
|
+
}
|
|
206
|
+
const entryId = extractId(subFm2, absPath);
|
|
207
|
+
if (entryId === subEpicId) {
|
|
208
|
+
subEpicPath = absPath;
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (subEpicPath !== null) break;
|
|
213
|
+
}
|
|
214
|
+
if (subEpicPath === null) {
|
|
215
|
+
pendingSubEpics.push(subEpicId);
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
let subFm = fmCache.get(subEpicPath);
|
|
219
|
+
if (subFm === void 0) {
|
|
220
|
+
const parsed = readFm(subEpicPath);
|
|
221
|
+
if (parsed === null) {
|
|
222
|
+
pendingSubEpics.push(subEpicId);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
subFm = parsed;
|
|
226
|
+
fmCache.set(subEpicPath, subFm);
|
|
227
|
+
}
|
|
228
|
+
const subStatus = typeof subFm["status"] === "string" ? subFm["status"].trim() : "";
|
|
229
|
+
if (subStatus === "DEFERRED") {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
if (ARTIFACT_TERMINAL_STATUSES.has(subStatus)) {
|
|
233
|
+
terminalSubEpics.push(subEpicId);
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
const visitedSnapshot = new Set(visited);
|
|
237
|
+
const subResult = await rollUpParentStatusInternal(
|
|
238
|
+
subEpicPath,
|
|
239
|
+
opts,
|
|
240
|
+
visitedSnapshot,
|
|
241
|
+
fmCache
|
|
242
|
+
);
|
|
243
|
+
if (subResult.verdict === "auto-flip" || subResult.verdict === "no-op") {
|
|
244
|
+
terminalSubEpics.push(subEpicId);
|
|
245
|
+
} else {
|
|
246
|
+
pendingSubEpics.push(subEpicId);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
visited.delete(parentId);
|
|
250
|
+
const total2 = terminalSubEpics.length + pendingSubEpics.length;
|
|
251
|
+
if (total2 === 0) {
|
|
252
|
+
return {
|
|
253
|
+
parent_id: parentId,
|
|
254
|
+
parent_path: parentFilePath,
|
|
255
|
+
current_status: currentStatus,
|
|
256
|
+
proposed_status: null,
|
|
257
|
+
coverage: "zero",
|
|
258
|
+
terminal_children: [],
|
|
259
|
+
pending_children: [],
|
|
260
|
+
verdict: "halt-zero-children",
|
|
261
|
+
halt_reason: `${parentId}: 0 children drafted; not reconcilable \u2014 decompose or abandon`
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
if (pendingSubEpics.length === 0) {
|
|
265
|
+
return {
|
|
266
|
+
parent_id: parentId,
|
|
267
|
+
parent_path: parentFilePath,
|
|
268
|
+
current_status: currentStatus,
|
|
269
|
+
proposed_status: "Completed",
|
|
270
|
+
coverage: "full",
|
|
271
|
+
terminal_children: terminalSubEpics,
|
|
272
|
+
pending_children: [],
|
|
273
|
+
verdict: "auto-flip"
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
return {
|
|
277
|
+
parent_id: parentId,
|
|
278
|
+
parent_path: parentFilePath,
|
|
279
|
+
current_status: currentStatus,
|
|
280
|
+
proposed_status: null,
|
|
281
|
+
coverage: "sub-epic-partial",
|
|
282
|
+
terminal_children: terminalSubEpics,
|
|
283
|
+
pending_children: pendingSubEpics,
|
|
284
|
+
verdict: "halt-partial",
|
|
285
|
+
halt_reason: `${parentId}: ${terminalSubEpics.length}/${total2} sub-epics terminal \u2014 pending: ${pendingSubEpics.join(", ")}`
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
const children = enumerateChildren(parentId, deliveryRoot, archiveRoot, fmCache);
|
|
289
|
+
visited.delete(parentId);
|
|
290
|
+
if (children.length === 0) {
|
|
291
|
+
return {
|
|
292
|
+
parent_id: parentId,
|
|
293
|
+
parent_path: parentFilePath,
|
|
294
|
+
current_status: currentStatus,
|
|
295
|
+
proposed_status: null,
|
|
296
|
+
coverage: "zero",
|
|
297
|
+
terminal_children: [],
|
|
298
|
+
pending_children: [],
|
|
299
|
+
verdict: "halt-zero-children",
|
|
300
|
+
halt_reason: `${parentId}: 0 children drafted; not reconcilable \u2014 decompose or abandon`
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
const terminalChildren = [];
|
|
304
|
+
const pendingChildren = [];
|
|
305
|
+
for (const child of children) {
|
|
306
|
+
if (ARTIFACT_TERMINAL_STATUSES.has(child.status)) {
|
|
307
|
+
terminalChildren.push(child.id);
|
|
308
|
+
} else {
|
|
309
|
+
pendingChildren.push(child.id);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
const total = terminalChildren.length + pendingChildren.length;
|
|
313
|
+
if (pendingChildren.length === 0) {
|
|
314
|
+
return {
|
|
315
|
+
parent_id: parentId,
|
|
316
|
+
parent_path: parentFilePath,
|
|
317
|
+
current_status: currentStatus,
|
|
318
|
+
proposed_status: "Completed",
|
|
319
|
+
coverage: "full",
|
|
320
|
+
terminal_children: terminalChildren,
|
|
321
|
+
pending_children: [],
|
|
322
|
+
verdict: "auto-flip"
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
return {
|
|
326
|
+
parent_id: parentId,
|
|
327
|
+
parent_path: parentFilePath,
|
|
328
|
+
current_status: currentStatus,
|
|
329
|
+
proposed_status: null,
|
|
330
|
+
coverage: "partial",
|
|
331
|
+
terminal_children: terminalChildren,
|
|
332
|
+
pending_children: pendingChildren,
|
|
333
|
+
verdict: "halt-partial",
|
|
334
|
+
halt_reason: `${parentId}: ${terminalChildren.length}/${total} children terminal \u2014 pending: ${pendingChildren.join(", ")}`
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
async function rollUpParentStatus(parentFilePath, opts) {
|
|
338
|
+
const visited = /* @__PURE__ */ new Set();
|
|
339
|
+
const fmCache = /* @__PURE__ */ new Map();
|
|
340
|
+
return rollUpParentStatusInternal(parentFilePath, opts, visited, fmCache);
|
|
341
|
+
}
|
|
342
|
+
async function walkActiveParents(opts) {
|
|
343
|
+
const { deliveryRoot } = opts;
|
|
344
|
+
const pendingSyncDir = path.join(deliveryRoot, "pending-sync");
|
|
345
|
+
let entries;
|
|
346
|
+
try {
|
|
347
|
+
entries = fs.readdirSync(pendingSyncDir);
|
|
348
|
+
} catch {
|
|
349
|
+
return [];
|
|
350
|
+
}
|
|
351
|
+
const parentFiles = entries.filter(
|
|
352
|
+
(e) => e.endsWith(".md") && (e.startsWith("EPIC-") || e.startsWith("SPRINT-"))
|
|
353
|
+
);
|
|
354
|
+
const results = [];
|
|
355
|
+
const fmCache = /* @__PURE__ */ new Map();
|
|
356
|
+
for (const entry of parentFiles) {
|
|
357
|
+
const absPath = path.join(pendingSyncDir, entry);
|
|
358
|
+
try {
|
|
359
|
+
const visited = /* @__PURE__ */ new Set();
|
|
360
|
+
const result = await rollUpParentStatusInternal(absPath, opts, visited, fmCache);
|
|
361
|
+
results.push(result);
|
|
362
|
+
} catch (err) {
|
|
363
|
+
if (err instanceof Error && err.message.includes("sub_epics cycle detected")) {
|
|
364
|
+
throw err;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return results;
|
|
369
|
+
}
|
|
370
|
+
|
|
86
371
|
// src/lib/lifecycle-reconcile.ts
|
|
87
372
|
var ARTIFACT_TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
88
|
-
"Done",
|
|
89
373
|
"Completed",
|
|
90
|
-
"Verified",
|
|
91
374
|
"Abandoned",
|
|
92
375
|
"Closed",
|
|
93
|
-
"Resolved"
|
|
94
|
-
"Escalated",
|
|
95
|
-
"Parking Lot"
|
|
376
|
+
"Resolved"
|
|
96
377
|
]);
|
|
378
|
+
var ARTIFACT_GATE_EXPECTED = ["Completed"];
|
|
97
379
|
var VERB_STATUS_MAP = {
|
|
98
380
|
feat: {
|
|
99
381
|
types: ["STORY", "EPIC", "CR"],
|
|
100
|
-
expected: [
|
|
382
|
+
expected: [...ARTIFACT_GATE_EXPECTED]
|
|
101
383
|
},
|
|
102
384
|
fix: {
|
|
103
385
|
types: ["BUG", "HOTFIX"],
|
|
104
|
-
expected: [
|
|
386
|
+
expected: [...ARTIFACT_GATE_EXPECTED]
|
|
105
387
|
}
|
|
106
388
|
};
|
|
107
389
|
var ID_PATTERN = /\b(STORY-\d{3}-\d{2}|(CR|BUG|EPIC|HOTFIX)-\d{3}|(PROPOSAL|PROP)-\d{3})\b/g;
|
|
@@ -152,10 +434,10 @@ function findArtifactFile(deliveryRoot, id) {
|
|
|
152
434
|
{ rel: "archive", inArchive: true }
|
|
153
435
|
];
|
|
154
436
|
for (const { rel, inArchive } of dirs) {
|
|
155
|
-
const dir =
|
|
437
|
+
const dir = path2.join(deliveryRoot, rel);
|
|
156
438
|
let entries;
|
|
157
439
|
try {
|
|
158
|
-
entries =
|
|
440
|
+
entries = fs2.readdirSync(dir);
|
|
159
441
|
} catch {
|
|
160
442
|
continue;
|
|
161
443
|
}
|
|
@@ -163,7 +445,7 @@ function findArtifactFile(deliveryRoot, id) {
|
|
|
163
445
|
(e) => (e.startsWith(prefix) || e === `${id}.md`) && e.endsWith(".md")
|
|
164
446
|
);
|
|
165
447
|
if (match) {
|
|
166
|
-
const absPath =
|
|
448
|
+
const absPath = path2.join(dir, match);
|
|
167
449
|
return { absPath, inArchive, relPath: `${rel}/${match}` };
|
|
168
450
|
}
|
|
169
451
|
}
|
|
@@ -172,7 +454,7 @@ function findArtifactFile(deliveryRoot, id) {
|
|
|
172
454
|
function readArtifactStatus(absPath) {
|
|
173
455
|
let raw;
|
|
174
456
|
try {
|
|
175
|
-
raw =
|
|
457
|
+
raw = fs2.readFileSync(absPath, "utf8");
|
|
176
458
|
} catch {
|
|
177
459
|
return { status: null, carryOver: false };
|
|
178
460
|
}
|
|
@@ -227,7 +509,7 @@ function reconcileLifecycle(opts) {
|
|
|
227
509
|
if (carryOver) continue;
|
|
228
510
|
let expectedStatuses;
|
|
229
511
|
if (verb === "feat" && type === "BUG") {
|
|
230
|
-
expectedStatuses = [
|
|
512
|
+
expectedStatuses = [...ARTIFACT_GATE_EXPECTED];
|
|
231
513
|
} else if (!verbConfig.types.includes(type)) {
|
|
232
514
|
continue;
|
|
233
515
|
} else {
|
|
@@ -239,7 +521,7 @@ function reconcileLifecycle(opts) {
|
|
|
239
521
|
cleanIds.add(id);
|
|
240
522
|
idToItem.delete(id);
|
|
241
523
|
} else if (!idToItem.has(id)) {
|
|
242
|
-
const expectedStr = expectedStatuses[0] ?? "
|
|
524
|
+
const expectedStr = expectedStatuses[0] ?? "Completed";
|
|
243
525
|
idToItem.set(id, {
|
|
244
526
|
id,
|
|
245
527
|
type,
|
|
@@ -270,13 +552,13 @@ function reconcileCrossSprintOrphans(opts) {
|
|
|
270
552
|
const TERMINAL_STATE_JSON = /* @__PURE__ */ new Set(["Done", "Escalated", "Parking Lot"]);
|
|
271
553
|
let activeSprintId = null;
|
|
272
554
|
try {
|
|
273
|
-
activeSprintId =
|
|
555
|
+
activeSprintId = fs2.readFileSync(path2.join(sprintRunsRoot, ".active"), "utf8").trim();
|
|
274
556
|
} catch {
|
|
275
557
|
}
|
|
276
|
-
const pendingDir =
|
|
558
|
+
const pendingDir = path2.join(deliveryRoot, "pending-sync");
|
|
277
559
|
let pendingFiles;
|
|
278
560
|
try {
|
|
279
|
-
pendingFiles =
|
|
561
|
+
pendingFiles = fs2.readdirSync(pendingDir).filter(
|
|
280
562
|
(f) => f.endsWith(".md") && !f.startsWith(".")
|
|
281
563
|
);
|
|
282
564
|
} catch {
|
|
@@ -284,7 +566,7 @@ function reconcileCrossSprintOrphans(opts) {
|
|
|
284
566
|
}
|
|
285
567
|
const pendingMap = /* @__PURE__ */ new Map();
|
|
286
568
|
for (const fileName of pendingFiles) {
|
|
287
|
-
const absPath =
|
|
569
|
+
const absPath = path2.join(pendingDir, fileName);
|
|
288
570
|
const { status } = readArtifactStatus(absPath);
|
|
289
571
|
if (status === null) continue;
|
|
290
572
|
if (ARTIFACT_TERMINAL_STATUSES.has(status)) continue;
|
|
@@ -296,7 +578,7 @@ function reconcileCrossSprintOrphans(opts) {
|
|
|
296
578
|
if (!type || type === "PROPOSAL") continue;
|
|
297
579
|
pendingMap.set(id, {
|
|
298
580
|
status,
|
|
299
|
-
filePath:
|
|
581
|
+
filePath: path2.join("pending-sync", fileName),
|
|
300
582
|
type
|
|
301
583
|
});
|
|
302
584
|
}
|
|
@@ -305,10 +587,10 @@ function reconcileCrossSprintOrphans(opts) {
|
|
|
305
587
|
}
|
|
306
588
|
let sprintDirs;
|
|
307
589
|
try {
|
|
308
|
-
sprintDirs =
|
|
590
|
+
sprintDirs = fs2.readdirSync(sprintRunsRoot).filter((entry) => {
|
|
309
591
|
if (entry.startsWith(".")) return false;
|
|
310
592
|
try {
|
|
311
|
-
return
|
|
593
|
+
return fs2.statSync(path2.join(sprintRunsRoot, entry)).isDirectory();
|
|
312
594
|
} catch {
|
|
313
595
|
return false;
|
|
314
596
|
}
|
|
@@ -321,10 +603,10 @@ function reconcileCrossSprintOrphans(opts) {
|
|
|
321
603
|
let clean = 0;
|
|
322
604
|
for (const sprintDir of sprintDirs) {
|
|
323
605
|
if (activeSprintId && sprintDir === activeSprintId) continue;
|
|
324
|
-
const stateFile =
|
|
606
|
+
const stateFile = path2.join(sprintRunsRoot, sprintDir, "state.json");
|
|
325
607
|
let stateJson;
|
|
326
608
|
try {
|
|
327
|
-
const raw =
|
|
609
|
+
const raw = fs2.readFileSync(stateFile, "utf8");
|
|
328
610
|
stateJson = JSON.parse(raw);
|
|
329
611
|
} catch {
|
|
330
612
|
continue;
|
|
@@ -357,7 +639,7 @@ function reconcileDecomposition(opts) {
|
|
|
357
639
|
const { sprintPlanPath, deliveryRoot } = opts;
|
|
358
640
|
let raw;
|
|
359
641
|
try {
|
|
360
|
-
raw =
|
|
642
|
+
raw = fs2.readFileSync(sprintPlanPath, "utf8");
|
|
361
643
|
} catch {
|
|
362
644
|
return { missing: [], clean: 0 };
|
|
363
645
|
}
|
|
@@ -369,11 +651,11 @@ function reconcileDecomposition(opts) {
|
|
|
369
651
|
}
|
|
370
652
|
const epics = Array.isArray(fm["epics"]) ? fm["epics"].map(String) : [];
|
|
371
653
|
const proposals = Array.isArray(fm["proposals"]) ? fm["proposals"].map(String) : [];
|
|
372
|
-
const pendingDir =
|
|
373
|
-
const archiveDir =
|
|
654
|
+
const pendingDir = path2.join(deliveryRoot, "pending-sync");
|
|
655
|
+
const archiveDir = path2.join(deliveryRoot, "archive");
|
|
374
656
|
function listMdFiles(dir) {
|
|
375
657
|
try {
|
|
376
|
-
return
|
|
658
|
+
return fs2.readdirSync(dir).filter((f) => f.endsWith(".md"));
|
|
377
659
|
} catch {
|
|
378
660
|
return [];
|
|
379
661
|
}
|
|
@@ -445,9 +727,9 @@ function findChildStories(epicId, pendingDir, pendingFiles, archiveDir, archiveF
|
|
|
445
727
|
for (const f of files) {
|
|
446
728
|
if (!f.startsWith(storyPrefix) && !f.startsWith("STORY-")) continue;
|
|
447
729
|
if (!f.includes(storyPrefix)) continue;
|
|
448
|
-
const absPath =
|
|
730
|
+
const absPath = path2.join(dir, f);
|
|
449
731
|
try {
|
|
450
|
-
const raw =
|
|
732
|
+
const raw = fs2.readFileSync(absPath, "utf8");
|
|
451
733
|
const { fm } = parseFrontmatter(raw);
|
|
452
734
|
const parentRef = fm["parent_epic_ref"];
|
|
453
735
|
if (parentRef === epicId) {
|
|
@@ -462,9 +744,9 @@ function findChildStories(epicId, pendingDir, pendingFiles, archiveDir, archiveF
|
|
|
462
744
|
function findDecomposedEpic(proposalId, pendingDir, pendingFiles) {
|
|
463
745
|
for (const f of pendingFiles) {
|
|
464
746
|
if (!f.startsWith("EPIC-")) continue;
|
|
465
|
-
const absPath =
|
|
747
|
+
const absPath = path2.join(pendingDir, f);
|
|
466
748
|
try {
|
|
467
|
-
const raw =
|
|
749
|
+
const raw = fs2.readFileSync(absPath, "utf8");
|
|
468
750
|
const { fm } = parseFrontmatter(raw);
|
|
469
751
|
const contextSource = fm["context_source"];
|
|
470
752
|
if (typeof contextSource === "string" && contextSource.includes(proposalId)) {
|
|
@@ -492,6 +774,8 @@ function checkVerbMismatch(verb, type) {
|
|
|
492
774
|
parseCommitMessage,
|
|
493
775
|
reconcileCrossSprintOrphans,
|
|
494
776
|
reconcileDecomposition,
|
|
495
|
-
reconcileLifecycle
|
|
777
|
+
reconcileLifecycle,
|
|
778
|
+
rollUpParentStatus,
|
|
779
|
+
walkActiveParents
|
|
496
780
|
});
|
|
497
781
|
//# sourceMappingURL=lifecycle-reconcile.cjs.map
|