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.
Files changed (40) hide show
  1. package/dist/MANIFEST.json +13 -13
  2. package/dist/{chunk-HZPJ5QX4.js → chunk-EG6YGT2O.js} +315 -33
  3. package/dist/chunk-EG6YGT2O.js.map +1 -0
  4. package/dist/cli.cjs +612 -289
  5. package/dist/cli.cjs.map +1 -1
  6. package/dist/cli.js +73 -37
  7. package/dist/cli.js.map +1 -1
  8. package/dist/lib/lifecycle-reconcile.cjs +318 -34
  9. package/dist/lib/lifecycle-reconcile.cjs.map +1 -1
  10. package/dist/lib/lifecycle-reconcile.d.cts +55 -4
  11. package/dist/lib/lifecycle-reconcile.d.ts +55 -4
  12. package/dist/lib/lifecycle-reconcile.js +7 -3
  13. package/dist/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +1 -1
  14. package/dist/templates/cleargate-planning/.claude/agents/developer.md +8 -4
  15. package/dist/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +2 -0
  16. package/dist/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +73 -0
  17. package/dist/templates/cleargate-planning/.cleargate/templates/Bug.md +1 -1
  18. package/dist/templates/cleargate-planning/.cleargate/templates/CR.md +1 -1
  19. package/dist/templates/cleargate-planning/.cleargate/templates/epic.md +1 -1
  20. package/dist/templates/cleargate-planning/.cleargate/templates/hotfix.md +1 -1
  21. package/dist/templates/cleargate-planning/.cleargate/templates/initiative.md +1 -1
  22. package/dist/templates/cleargate-planning/.cleargate/templates/sprint_report.md +1 -1
  23. package/dist/templates/cleargate-planning/.cleargate/templates/story.md +1 -1
  24. package/dist/templates/cleargate-planning/CLAUDE.md +2 -0
  25. package/dist/templates/cleargate-planning/MANIFEST.json +13 -13
  26. package/package.json +8 -9
  27. package/templates/cleargate-planning/.claude/agents/cleargate-wiki-lint.md +1 -1
  28. package/templates/cleargate-planning/.claude/agents/developer.md +8 -4
  29. package/templates/cleargate-planning/.claude/hooks/pre-commit-surface-gate.sh +2 -0
  30. package/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +73 -0
  31. package/templates/cleargate-planning/.cleargate/templates/Bug.md +1 -1
  32. package/templates/cleargate-planning/.cleargate/templates/CR.md +1 -1
  33. package/templates/cleargate-planning/.cleargate/templates/epic.md +1 -1
  34. package/templates/cleargate-planning/.cleargate/templates/hotfix.md +1 -1
  35. package/templates/cleargate-planning/.cleargate/templates/initiative.md +1 -1
  36. package/templates/cleargate-planning/.cleargate/templates/sprint_report.md +1 -1
  37. package/templates/cleargate-planning/.cleargate/templates/story.md +1 -1
  38. package/templates/cleargate-planning/CLAUDE.md +2 -0
  39. package/templates/cleargate-planning/MANIFEST.json +13 -13
  40. package/dist/chunk-HZPJ5QX4.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  {
2
- "cleargate_version": "0.12.0",
3
- "generated_at": "2026-05-15T05:10:35.575Z",
2
+ "cleargate_version": "0.13.0",
3
+ "generated_at": "2026-05-18T17:11:10.027Z",
4
4
  "files": [
5
5
  {
6
6
  "path": ".claude/agents/architect.md",
@@ -25,7 +25,7 @@
25
25
  },
26
26
  {
27
27
  "path": ".claude/agents/cleargate-wiki-lint.md",
28
- "sha256": "42067fd728e64ac1ff1ace5822b8f8c3ddf1ba70fd0fbddf0e8cb19d5d699e77",
28
+ "sha256": "2a9212d81df9a68e167ec7a18093a73c6da0208f13685c6887d5bd832b56fb3d",
29
29
  "tier": "agent",
30
30
  "overwrite_policy": "always",
31
31
  "preserve_on_uninstall": false
@@ -39,7 +39,7 @@
39
39
  },
40
40
  {
41
41
  "path": ".claude/agents/developer.md",
42
- "sha256": "36f75fe9f8dc8412eb22706dfd021f5db5694c81d73e0c938e701a83c0149126",
42
+ "sha256": "db7963778d68654f2dc96658d60433fa79305a10eec937807f2f0ed0cd05ce89",
43
43
  "tier": "agent",
44
44
  "overwrite_policy": "always",
45
45
  "preserve_on_uninstall": false
@@ -74,7 +74,7 @@
74
74
  },
75
75
  {
76
76
  "path": ".claude/hooks/pre-commit-surface-gate.sh",
77
- "sha256": "6741422cd8ca75b45e26bd0e9cb54126ad9e933f763c9094ceba6b84794b761a",
77
+ "sha256": "8dd817fbe75ee53753e2fe1fc2ccd2b6efdb2b3c0b6ad3191bebd9640afdd6f8",
78
78
  "tier": "hook",
79
79
  "overwrite_policy": "always",
80
80
  "preserve_on_uninstall": false
@@ -200,7 +200,7 @@
200
200
  },
201
201
  {
202
202
  "path": ".cleargate/scripts/close_sprint.mjs",
203
- "sha256": "b42018973ec6f02fc5d0ddf2a7d302aa0ee6d127cb5140474a67cb1fb0af6b28",
203
+ "sha256": "b14b65f15c5ad57a845df89c069b5417195651b7d3aa7f7f5736416d0db0b868",
204
204
  "tier": "script",
205
205
  "overwrite_policy": "always",
206
206
  "preserve_on_uninstall": false
@@ -396,35 +396,35 @@
396
396
  },
397
397
  {
398
398
  "path": ".cleargate/templates/Bug.md",
399
- "sha256": "570112e3a60856c891652837307fc6f296cc39acc20451f8bde62da0fe033026",
399
+ "sha256": "cebca344b6b820525c603444cf52626e120ebaa1ac28da099dc19c9cff39302c",
400
400
  "tier": "template",
401
401
  "overwrite_policy": "merge-3way",
402
402
  "preserve_on_uninstall": false
403
403
  },
404
404
  {
405
405
  "path": ".cleargate/templates/CR.md",
406
- "sha256": "ab111792dc79fd5181b44a63c76ac696fa4e456161ac44e2c4d0dc609e8dd904",
406
+ "sha256": "ea5acf2087808e0d52806a87a6a7f1ced7473b591857f322fab5285adc80d25a",
407
407
  "tier": "template",
408
408
  "overwrite_policy": "merge-3way",
409
409
  "preserve_on_uninstall": false
410
410
  },
411
411
  {
412
412
  "path": ".cleargate/templates/epic.md",
413
- "sha256": "fd3b54de89befe7c5bcef65e2fa5550fc7f371d55c8f15143ae73d84f3ae6d36",
413
+ "sha256": "f9cf44db19288f0756b76bc8b13a075d4089990324db6febd072f5cf93d59cd0",
414
414
  "tier": "template",
415
415
  "overwrite_policy": "merge-3way",
416
416
  "preserve_on_uninstall": false
417
417
  },
418
418
  {
419
419
  "path": ".cleargate/templates/hotfix.md",
420
- "sha256": "b75dd154c156e68a44ae01b85eff7ec16f54050e4fe7eb71f59e17d87c03a298",
420
+ "sha256": "de788497b4d224500036773f854801c056d73b08c3c0d66fdb641f17a1610bca",
421
421
  "tier": "template",
422
422
  "overwrite_policy": "merge-3way",
423
423
  "preserve_on_uninstall": false
424
424
  },
425
425
  {
426
426
  "path": ".cleargate/templates/initiative.md",
427
- "sha256": "28680917228e5d9887c8820193cc21bfecc6aad3bc0658e8a59087adf63551fd",
427
+ "sha256": "1170e595f5813c62f86212d6ca1955d84465f396c81aaf34498b8e2d4595d681",
428
428
  "tier": "template",
429
429
  "overwrite_policy": "merge-3way",
430
430
  "preserve_on_uninstall": false
@@ -445,14 +445,14 @@
445
445
  },
446
446
  {
447
447
  "path": ".cleargate/templates/sprint_report.md",
448
- "sha256": "84e32e956eb1e8a9c97be1c346b695df316a9e47e17402f3d6581e3ffdbc4d2d",
448
+ "sha256": "5914d54080f6110be5a5e905e3312811f3d0f80978121b1e15707d5be05cc5b1",
449
449
  "tier": "template",
450
450
  "overwrite_policy": "merge-3way",
451
451
  "preserve_on_uninstall": false
452
452
  },
453
453
  {
454
454
  "path": ".cleargate/templates/story.md",
455
- "sha256": "56d793983614d832e84193d99ee5318c061d2f3ea4f019cefb861c026513cb31",
455
+ "sha256": "0badf01a080bca552a06fb64becd9b8c88fbf104e30f32667263522c3ff81051",
456
456
  "tier": "template",
457
457
  "overwrite_policy": "merge-3way",
458
458
  "preserve_on_uninstall": false
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/lib/lifecycle-reconcile.ts
4
- import * as fs from "fs";
5
- import * as path from "path";
4
+ import * as fs2 from "fs";
5
+ import * as path2 from "path";
6
6
  import { spawnSync } from "child_process";
7
7
 
8
8
  // src/wiki/parse-frontmatter.ts
@@ -44,25 +44,305 @@ function parseFrontmatter(raw) {
44
44
  return { fm: parsed, body };
45
45
  }
46
46
 
47
+ // src/lib/parent-rollup.ts
48
+ import * as fs from "fs";
49
+ import * as path from "path";
50
+ function readFm(filePath) {
51
+ try {
52
+ const raw = fs.readFileSync(filePath, "utf8");
53
+ const { fm } = parseFrontmatter(raw);
54
+ return fm;
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+ function extractId(fm, filePath) {
60
+ for (const key of [
61
+ "story_id",
62
+ "epic_id",
63
+ "sprint_id",
64
+ "bug_id",
65
+ "cr_id",
66
+ "initiative_id",
67
+ "hotfix_id"
68
+ ]) {
69
+ const val = fm[key];
70
+ if (typeof val === "string" && val.trim() !== "") return val.trim();
71
+ }
72
+ const stem = path.basename(filePath, ".md");
73
+ return stem.split("_")[0] ?? stem;
74
+ }
75
+ function enumerateChildren(parentId, deliveryRoot, archiveRoot, fmCache) {
76
+ const pendingSyncDir = path.join(deliveryRoot, "pending-sync");
77
+ const results = [];
78
+ const pools = [];
79
+ if (fs.existsSync(archiveRoot)) pools.push(archiveRoot);
80
+ if (fs.existsSync(pendingSyncDir)) pools.push(pendingSyncDir);
81
+ for (const dir of pools) {
82
+ let entries;
83
+ try {
84
+ entries = fs.readdirSync(dir);
85
+ } catch {
86
+ entries = [];
87
+ }
88
+ for (const entry of entries) {
89
+ if (!entry.endsWith(".md")) continue;
90
+ const absPath = path.join(dir, entry);
91
+ let fm = fmCache.get(absPath);
92
+ if (fm === void 0) {
93
+ const parsed = readFm(absPath);
94
+ if (parsed === null) continue;
95
+ fm = parsed;
96
+ fmCache.set(absPath, fm);
97
+ }
98
+ const parentCleargateId = fm["parent_cleargate_id"];
99
+ const parentEpicRef = fm["parent_epic_ref"];
100
+ const isChild = typeof parentCleargateId === "string" && parentCleargateId.trim() === parentId || typeof parentEpicRef === "string" && parentEpicRef.trim() === parentId;
101
+ if (!isChild) continue;
102
+ const childId = extractId(fm, absPath);
103
+ const status = typeof fm["status"] === "string" ? fm["status"].trim() : "";
104
+ results.push({ id: childId, status });
105
+ }
106
+ }
107
+ return results;
108
+ }
109
+ async function rollUpParentStatusInternal(parentFilePath, opts, visited, fmCache) {
110
+ const { deliveryRoot, archiveRoot } = opts;
111
+ let fm = fmCache.get(parentFilePath);
112
+ if (fm === void 0) {
113
+ const raw = readFm(parentFilePath);
114
+ if (raw === null) {
115
+ throw new Error(`parent-rollup: cannot read frontmatter from ${parentFilePath}`);
116
+ }
117
+ fm = raw;
118
+ fmCache.set(parentFilePath, fm);
119
+ }
120
+ const parentId = extractId(fm, parentFilePath);
121
+ const currentStatus = typeof fm["status"] === "string" ? fm["status"].trim() : "";
122
+ if (ARTIFACT_TERMINAL_STATUSES.has(currentStatus)) {
123
+ return {
124
+ parent_id: parentId,
125
+ parent_path: parentFilePath,
126
+ current_status: currentStatus,
127
+ proposed_status: null,
128
+ coverage: "full",
129
+ terminal_children: [],
130
+ pending_children: [],
131
+ verdict: "no-op"
132
+ };
133
+ }
134
+ if (visited.has(parentId)) {
135
+ throw new Error(`parent-rollup: sub_epics cycle detected at ${parentId}`);
136
+ }
137
+ visited.add(parentId);
138
+ const subEpicsField = fm["sub_epics"];
139
+ const subEpics = Array.isArray(subEpicsField) && subEpicsField.length > 0 ? subEpicsField.filter((s) => typeof s === "string") : [];
140
+ if (subEpics.length > 0) {
141
+ const pendingSyncDir = path.join(deliveryRoot, "pending-sync");
142
+ const terminalSubEpics = [];
143
+ const pendingSubEpics = [];
144
+ for (const subEpicId of subEpics) {
145
+ let subEpicPath = null;
146
+ const candidateDirs = [pendingSyncDir, archiveRoot];
147
+ for (const dir of candidateDirs) {
148
+ if (!fs.existsSync(dir)) continue;
149
+ let entries;
150
+ try {
151
+ entries = fs.readdirSync(dir);
152
+ } catch {
153
+ entries = [];
154
+ }
155
+ for (const entry of entries) {
156
+ if (!entry.endsWith(".md")) continue;
157
+ const absPath = path.join(dir, entry);
158
+ let subFm2 = fmCache.get(absPath);
159
+ if (subFm2 === void 0) {
160
+ const parsed = readFm(absPath);
161
+ if (parsed === null) continue;
162
+ subFm2 = parsed;
163
+ fmCache.set(absPath, subFm2);
164
+ }
165
+ const entryId = extractId(subFm2, absPath);
166
+ if (entryId === subEpicId) {
167
+ subEpicPath = absPath;
168
+ break;
169
+ }
170
+ }
171
+ if (subEpicPath !== null) break;
172
+ }
173
+ if (subEpicPath === null) {
174
+ pendingSubEpics.push(subEpicId);
175
+ continue;
176
+ }
177
+ let subFm = fmCache.get(subEpicPath);
178
+ if (subFm === void 0) {
179
+ const parsed = readFm(subEpicPath);
180
+ if (parsed === null) {
181
+ pendingSubEpics.push(subEpicId);
182
+ continue;
183
+ }
184
+ subFm = parsed;
185
+ fmCache.set(subEpicPath, subFm);
186
+ }
187
+ const subStatus = typeof subFm["status"] === "string" ? subFm["status"].trim() : "";
188
+ if (subStatus === "DEFERRED") {
189
+ continue;
190
+ }
191
+ if (ARTIFACT_TERMINAL_STATUSES.has(subStatus)) {
192
+ terminalSubEpics.push(subEpicId);
193
+ continue;
194
+ }
195
+ const visitedSnapshot = new Set(visited);
196
+ const subResult = await rollUpParentStatusInternal(
197
+ subEpicPath,
198
+ opts,
199
+ visitedSnapshot,
200
+ fmCache
201
+ );
202
+ if (subResult.verdict === "auto-flip" || subResult.verdict === "no-op") {
203
+ terminalSubEpics.push(subEpicId);
204
+ } else {
205
+ pendingSubEpics.push(subEpicId);
206
+ }
207
+ }
208
+ visited.delete(parentId);
209
+ const total2 = terminalSubEpics.length + pendingSubEpics.length;
210
+ if (total2 === 0) {
211
+ return {
212
+ parent_id: parentId,
213
+ parent_path: parentFilePath,
214
+ current_status: currentStatus,
215
+ proposed_status: null,
216
+ coverage: "zero",
217
+ terminal_children: [],
218
+ pending_children: [],
219
+ verdict: "halt-zero-children",
220
+ halt_reason: `${parentId}: 0 children drafted; not reconcilable \u2014 decompose or abandon`
221
+ };
222
+ }
223
+ if (pendingSubEpics.length === 0) {
224
+ return {
225
+ parent_id: parentId,
226
+ parent_path: parentFilePath,
227
+ current_status: currentStatus,
228
+ proposed_status: "Completed",
229
+ coverage: "full",
230
+ terminal_children: terminalSubEpics,
231
+ pending_children: [],
232
+ verdict: "auto-flip"
233
+ };
234
+ }
235
+ return {
236
+ parent_id: parentId,
237
+ parent_path: parentFilePath,
238
+ current_status: currentStatus,
239
+ proposed_status: null,
240
+ coverage: "sub-epic-partial",
241
+ terminal_children: terminalSubEpics,
242
+ pending_children: pendingSubEpics,
243
+ verdict: "halt-partial",
244
+ halt_reason: `${parentId}: ${terminalSubEpics.length}/${total2} sub-epics terminal \u2014 pending: ${pendingSubEpics.join(", ")}`
245
+ };
246
+ }
247
+ const children = enumerateChildren(parentId, deliveryRoot, archiveRoot, fmCache);
248
+ visited.delete(parentId);
249
+ if (children.length === 0) {
250
+ return {
251
+ parent_id: parentId,
252
+ parent_path: parentFilePath,
253
+ current_status: currentStatus,
254
+ proposed_status: null,
255
+ coverage: "zero",
256
+ terminal_children: [],
257
+ pending_children: [],
258
+ verdict: "halt-zero-children",
259
+ halt_reason: `${parentId}: 0 children drafted; not reconcilable \u2014 decompose or abandon`
260
+ };
261
+ }
262
+ const terminalChildren = [];
263
+ const pendingChildren = [];
264
+ for (const child of children) {
265
+ if (ARTIFACT_TERMINAL_STATUSES.has(child.status)) {
266
+ terminalChildren.push(child.id);
267
+ } else {
268
+ pendingChildren.push(child.id);
269
+ }
270
+ }
271
+ const total = terminalChildren.length + pendingChildren.length;
272
+ if (pendingChildren.length === 0) {
273
+ return {
274
+ parent_id: parentId,
275
+ parent_path: parentFilePath,
276
+ current_status: currentStatus,
277
+ proposed_status: "Completed",
278
+ coverage: "full",
279
+ terminal_children: terminalChildren,
280
+ pending_children: [],
281
+ verdict: "auto-flip"
282
+ };
283
+ }
284
+ return {
285
+ parent_id: parentId,
286
+ parent_path: parentFilePath,
287
+ current_status: currentStatus,
288
+ proposed_status: null,
289
+ coverage: "partial",
290
+ terminal_children: terminalChildren,
291
+ pending_children: pendingChildren,
292
+ verdict: "halt-partial",
293
+ halt_reason: `${parentId}: ${terminalChildren.length}/${total} children terminal \u2014 pending: ${pendingChildren.join(", ")}`
294
+ };
295
+ }
296
+ async function rollUpParentStatus(parentFilePath, opts) {
297
+ const visited = /* @__PURE__ */ new Set();
298
+ const fmCache = /* @__PURE__ */ new Map();
299
+ return rollUpParentStatusInternal(parentFilePath, opts, visited, fmCache);
300
+ }
301
+ async function walkActiveParents(opts) {
302
+ const { deliveryRoot } = opts;
303
+ const pendingSyncDir = path.join(deliveryRoot, "pending-sync");
304
+ let entries;
305
+ try {
306
+ entries = fs.readdirSync(pendingSyncDir);
307
+ } catch {
308
+ return [];
309
+ }
310
+ const parentFiles = entries.filter(
311
+ (e) => e.endsWith(".md") && (e.startsWith("EPIC-") || e.startsWith("SPRINT-"))
312
+ );
313
+ const results = [];
314
+ const fmCache = /* @__PURE__ */ new Map();
315
+ for (const entry of parentFiles) {
316
+ const absPath = path.join(pendingSyncDir, entry);
317
+ try {
318
+ const visited = /* @__PURE__ */ new Set();
319
+ const result = await rollUpParentStatusInternal(absPath, opts, visited, fmCache);
320
+ results.push(result);
321
+ } catch (err) {
322
+ if (err instanceof Error && err.message.includes("sub_epics cycle detected")) {
323
+ throw err;
324
+ }
325
+ }
326
+ }
327
+ return results;
328
+ }
329
+
47
330
  // src/lib/lifecycle-reconcile.ts
48
331
  var ARTIFACT_TERMINAL_STATUSES = /* @__PURE__ */ new Set([
49
- "Done",
50
332
  "Completed",
51
- "Verified",
52
333
  "Abandoned",
53
334
  "Closed",
54
- "Resolved",
55
- "Escalated",
56
- "Parking Lot"
335
+ "Resolved"
57
336
  ]);
337
+ var ARTIFACT_GATE_EXPECTED = ["Completed"];
58
338
  var VERB_STATUS_MAP = {
59
339
  feat: {
60
340
  types: ["STORY", "EPIC", "CR"],
61
- expected: ["Done", "Completed"]
341
+ expected: [...ARTIFACT_GATE_EXPECTED]
62
342
  },
63
343
  fix: {
64
344
  types: ["BUG", "HOTFIX"],
65
- expected: ["Verified", "Done", "Completed"]
345
+ expected: [...ARTIFACT_GATE_EXPECTED]
66
346
  }
67
347
  };
68
348
  var ID_PATTERN = /\b(STORY-\d{3}-\d{2}|(CR|BUG|EPIC|HOTFIX)-\d{3}|(PROPOSAL|PROP)-\d{3})\b/g;
@@ -113,10 +393,10 @@ function findArtifactFile(deliveryRoot, id) {
113
393
  { rel: "archive", inArchive: true }
114
394
  ];
115
395
  for (const { rel, inArchive } of dirs) {
116
- const dir = path.join(deliveryRoot, rel);
396
+ const dir = path2.join(deliveryRoot, rel);
117
397
  let entries;
118
398
  try {
119
- entries = fs.readdirSync(dir);
399
+ entries = fs2.readdirSync(dir);
120
400
  } catch {
121
401
  continue;
122
402
  }
@@ -124,7 +404,7 @@ function findArtifactFile(deliveryRoot, id) {
124
404
  (e) => (e.startsWith(prefix) || e === `${id}.md`) && e.endsWith(".md")
125
405
  );
126
406
  if (match) {
127
- const absPath = path.join(dir, match);
407
+ const absPath = path2.join(dir, match);
128
408
  return { absPath, inArchive, relPath: `${rel}/${match}` };
129
409
  }
130
410
  }
@@ -133,7 +413,7 @@ function findArtifactFile(deliveryRoot, id) {
133
413
  function readArtifactStatus(absPath) {
134
414
  let raw;
135
415
  try {
136
- raw = fs.readFileSync(absPath, "utf8");
416
+ raw = fs2.readFileSync(absPath, "utf8");
137
417
  } catch {
138
418
  return { status: null, carryOver: false };
139
419
  }
@@ -188,7 +468,7 @@ function reconcileLifecycle(opts) {
188
468
  if (carryOver) continue;
189
469
  let expectedStatuses;
190
470
  if (verb === "feat" && type === "BUG") {
191
- expectedStatuses = ["Verified", "Done", "Completed"];
471
+ expectedStatuses = [...ARTIFACT_GATE_EXPECTED];
192
472
  } else if (!verbConfig.types.includes(type)) {
193
473
  continue;
194
474
  } else {
@@ -200,7 +480,7 @@ function reconcileLifecycle(opts) {
200
480
  cleanIds.add(id);
201
481
  idToItem.delete(id);
202
482
  } else if (!idToItem.has(id)) {
203
- const expectedStr = expectedStatuses[0] ?? "Done";
483
+ const expectedStr = expectedStatuses[0] ?? "Completed";
204
484
  idToItem.set(id, {
205
485
  id,
206
486
  type,
@@ -231,13 +511,13 @@ function reconcileCrossSprintOrphans(opts) {
231
511
  const TERMINAL_STATE_JSON = /* @__PURE__ */ new Set(["Done", "Escalated", "Parking Lot"]);
232
512
  let activeSprintId = null;
233
513
  try {
234
- activeSprintId = fs.readFileSync(path.join(sprintRunsRoot, ".active"), "utf8").trim();
514
+ activeSprintId = fs2.readFileSync(path2.join(sprintRunsRoot, ".active"), "utf8").trim();
235
515
  } catch {
236
516
  }
237
- const pendingDir = path.join(deliveryRoot, "pending-sync");
517
+ const pendingDir = path2.join(deliveryRoot, "pending-sync");
238
518
  let pendingFiles;
239
519
  try {
240
- pendingFiles = fs.readdirSync(pendingDir).filter(
520
+ pendingFiles = fs2.readdirSync(pendingDir).filter(
241
521
  (f) => f.endsWith(".md") && !f.startsWith(".")
242
522
  );
243
523
  } catch {
@@ -245,7 +525,7 @@ function reconcileCrossSprintOrphans(opts) {
245
525
  }
246
526
  const pendingMap = /* @__PURE__ */ new Map();
247
527
  for (const fileName of pendingFiles) {
248
- const absPath = path.join(pendingDir, fileName);
528
+ const absPath = path2.join(pendingDir, fileName);
249
529
  const { status } = readArtifactStatus(absPath);
250
530
  if (status === null) continue;
251
531
  if (ARTIFACT_TERMINAL_STATUSES.has(status)) continue;
@@ -257,7 +537,7 @@ function reconcileCrossSprintOrphans(opts) {
257
537
  if (!type || type === "PROPOSAL") continue;
258
538
  pendingMap.set(id, {
259
539
  status,
260
- filePath: path.join("pending-sync", fileName),
540
+ filePath: path2.join("pending-sync", fileName),
261
541
  type
262
542
  });
263
543
  }
@@ -266,10 +546,10 @@ function reconcileCrossSprintOrphans(opts) {
266
546
  }
267
547
  let sprintDirs;
268
548
  try {
269
- sprintDirs = fs.readdirSync(sprintRunsRoot).filter((entry) => {
549
+ sprintDirs = fs2.readdirSync(sprintRunsRoot).filter((entry) => {
270
550
  if (entry.startsWith(".")) return false;
271
551
  try {
272
- return fs.statSync(path.join(sprintRunsRoot, entry)).isDirectory();
552
+ return fs2.statSync(path2.join(sprintRunsRoot, entry)).isDirectory();
273
553
  } catch {
274
554
  return false;
275
555
  }
@@ -282,10 +562,10 @@ function reconcileCrossSprintOrphans(opts) {
282
562
  let clean = 0;
283
563
  for (const sprintDir of sprintDirs) {
284
564
  if (activeSprintId && sprintDir === activeSprintId) continue;
285
- const stateFile = path.join(sprintRunsRoot, sprintDir, "state.json");
565
+ const stateFile = path2.join(sprintRunsRoot, sprintDir, "state.json");
286
566
  let stateJson;
287
567
  try {
288
- const raw = fs.readFileSync(stateFile, "utf8");
568
+ const raw = fs2.readFileSync(stateFile, "utf8");
289
569
  stateJson = JSON.parse(raw);
290
570
  } catch {
291
571
  continue;
@@ -318,7 +598,7 @@ function reconcileDecomposition(opts) {
318
598
  const { sprintPlanPath, deliveryRoot } = opts;
319
599
  let raw;
320
600
  try {
321
- raw = fs.readFileSync(sprintPlanPath, "utf8");
601
+ raw = fs2.readFileSync(sprintPlanPath, "utf8");
322
602
  } catch {
323
603
  return { missing: [], clean: 0 };
324
604
  }
@@ -330,11 +610,11 @@ function reconcileDecomposition(opts) {
330
610
  }
331
611
  const epics = Array.isArray(fm["epics"]) ? fm["epics"].map(String) : [];
332
612
  const proposals = Array.isArray(fm["proposals"]) ? fm["proposals"].map(String) : [];
333
- const pendingDir = path.join(deliveryRoot, "pending-sync");
334
- const archiveDir = path.join(deliveryRoot, "archive");
613
+ const pendingDir = path2.join(deliveryRoot, "pending-sync");
614
+ const archiveDir = path2.join(deliveryRoot, "archive");
335
615
  function listMdFiles(dir) {
336
616
  try {
337
- return fs.readdirSync(dir).filter((f) => f.endsWith(".md"));
617
+ return fs2.readdirSync(dir).filter((f) => f.endsWith(".md"));
338
618
  } catch {
339
619
  return [];
340
620
  }
@@ -406,9 +686,9 @@ function findChildStories(epicId, pendingDir, pendingFiles, archiveDir, archiveF
406
686
  for (const f of files) {
407
687
  if (!f.startsWith(storyPrefix) && !f.startsWith("STORY-")) continue;
408
688
  if (!f.includes(storyPrefix)) continue;
409
- const absPath = path.join(dir, f);
689
+ const absPath = path2.join(dir, f);
410
690
  try {
411
- const raw = fs.readFileSync(absPath, "utf8");
691
+ const raw = fs2.readFileSync(absPath, "utf8");
412
692
  const { fm } = parseFrontmatter(raw);
413
693
  const parentRef = fm["parent_epic_ref"];
414
694
  if (parentRef === epicId) {
@@ -423,9 +703,9 @@ function findChildStories(epicId, pendingDir, pendingFiles, archiveDir, archiveF
423
703
  function findDecomposedEpic(proposalId, pendingDir, pendingFiles) {
424
704
  for (const f of pendingFiles) {
425
705
  if (!f.startsWith("EPIC-")) continue;
426
- const absPath = path.join(pendingDir, f);
706
+ const absPath = path2.join(pendingDir, f);
427
707
  try {
428
- const raw = fs.readFileSync(absPath, "utf8");
708
+ const raw = fs2.readFileSync(absPath, "utf8");
429
709
  const { fm } = parseFrontmatter(raw);
430
710
  const contextSource = fm["context_source"];
431
711
  if (typeof contextSource === "string" && contextSource.includes(proposalId)) {
@@ -448,6 +728,8 @@ function checkVerbMismatch(verb, type) {
448
728
 
449
729
  export {
450
730
  parseFrontmatter,
731
+ rollUpParentStatus,
732
+ walkActiveParents,
451
733
  ARTIFACT_TERMINAL_STATUSES,
452
734
  VERB_STATUS_MAP,
453
735
  parseCommitMessage,
@@ -456,4 +738,4 @@ export {
456
738
  reconcileDecomposition,
457
739
  checkVerbMismatch
458
740
  };
459
- //# sourceMappingURL=chunk-HZPJ5QX4.js.map
741
+ //# sourceMappingURL=chunk-EG6YGT2O.js.map