@yemi33/minions 0.1.2282 → 0.1.2284

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.
@@ -749,6 +749,12 @@ function updateWorkItemStatus(meta, status, reason) {
749
749
  const _VALID_PRD_STATUSES = new Set([...Object.values(WI_STATUS), 'missing']);
750
750
  // (#984) PRD statuses that are stale when the work item is actually done
751
751
  const _STALE_PRD_STATUSES = new Set([WI_STATUS.DISPATCHED, WI_STATUS.FAILED, WI_STATUS.PENDING]);
752
+ // (W-mquoejfa000xd6b6) PRD top-level statuses that are frozen — reconcilePrdStatuses must skip
753
+ // these entirely to avoid spurious status transitions or WI dispatches on paused/rejected/
754
+ // awaiting-approval PRDs. Previously only COMPLETED was skipped.
755
+ const _PRD_FROZEN_STATUSES = new Set([
756
+ PLAN_STATUS.COMPLETED, PLAN_STATUS.PAUSED, PLAN_STATUS.REJECTED, PLAN_STATUS.AWAITING_APPROVAL,
757
+ ]);
752
758
  function syncPrdItemStatus(itemId, status, sourcePlan) {
753
759
  if (!itemId) return;
754
760
  if (!_VALID_PRD_STATUSES.has(status)) return;
@@ -826,8 +832,8 @@ function reconcilePrdStatuses(config) {
826
832
  const logMessages = [];
827
833
  mutateJsonFileLocked(fpath, (plan) => {
828
834
  if (!plan?.missing_features) return plan;
829
- // Skip completed/archived PRDs — no reconciliation needed
830
- if (plan.status === PLAN_STATUS.COMPLETED) return plan;
835
+ // Skip frozen PRDs — paused/rejected/awaiting-approval/completed require no reconciliation
836
+ if (_PRD_FROZEN_STATUSES.has(plan.status)) return plan;
831
837
 
832
838
  for (const feature of plan.missing_features) {
833
839
  if (feature.status === PRD_ITEM_STATUS.MISSING && doneWiById.has(feature.id)) {
@@ -522,15 +522,28 @@ function _findExistingPlanForMeeting(meetingIds, plansDir) {
522
522
  return slugMatch;
523
523
  }
524
524
 
525
- // Check if a PRD already exists for a given plan file (plan already converted)
525
+ // Canonicalize a plan filename by stripping a trailing collision suffix
526
+ // (`-2`, `-3`, …) that `uniquePath` appends AFTER the `-YYYY-MM-DD` date when a
527
+ // same-name plan already exists. A pipeline run whose plan got collision-bumped
528
+ // to `<name>-<date>-2.md` must still link to the PRD written for the canonical
529
+ // `<name>-<date>.md` (a duplicate plan otherwise strands the PRD permanently
530
+ // `awaiting-approval`, defeating the stage's autoApprove). Only strips the
531
+ // suffix when the remainder ends in a full date, so real names are untouched.
532
+ function _canonicalPlanName(planFile) {
533
+ return String(planFile || '').replace(/^(.*-\d{4}-\d{2}-\d{2})-\d+(\.md)$/i, '$1$2');
534
+ }
535
+
536
+ // Check if a PRD already exists for a given plan file (plan already converted).
537
+ // Matches by canonical name so a collision-bumped plan still finds its PRD.
526
538
  function _findExistingPrdForPlan(planFile, prdDir) {
527
539
  if (!fs.existsSync(prdDir)) return null;
528
540
  const prdFiles = safeReadDir(prdDir).filter(f => f.endsWith('.json'));
541
+ const planKey = _canonicalPlanName(planFile);
529
542
  for (const pf of prdFiles) {
530
543
  // safeJsonNoRestore: PRDs are terminal artifacts — never restore archived
531
544
  // PRDs from a stale .backup sidecar (W-mouptdh1000h9f39).
532
545
  const prd = safeJsonNoRestore(path.join(prdDir, pf));
533
- if (prd?.source_plan && path.basename(String(prd.source_plan)) === planFile) return pf;
546
+ if (prd?.source_plan && _canonicalPlanName(path.basename(String(prd.source_plan))) === planKey) return pf;
534
547
  }
535
548
  return null;
536
549
  }
@@ -925,10 +938,14 @@ function isStageComplete(stage, stageState, run, config) {
925
938
  const discoveredWiIds = [];
926
939
  const prdFiles = fs.existsSync(prdDir) ? safeReadDir(prdDir).filter(f => f.endsWith('.json')) : [];
927
940
  for (const planFile of plans) {
941
+ const planKey = _canonicalPlanName(planFile);
928
942
  for (const pf of prdFiles) {
929
943
  // safeJsonNoRestore — see _findExistingPrdForPlan above (W-mouptdh1000h9f39).
930
944
  const prd = safeJsonNoRestore(path.join(prdDir, pf));
931
- if (prd?.source_plan && path.basename(String(prd.source_plan)) === planFile && !(artifacts.prds || []).includes(pf) && !discoveredPrds.includes(pf)) {
945
+ // Canonical match: a collision-bumped run plan (<name>-<date>-2.md)
946
+ // still links to the PRD written for <name>-<date>.md, so the stage's
947
+ // autoApprove reaches the PRD instead of stranding it awaiting-approval.
948
+ if (prd?.source_plan && _canonicalPlanName(path.basename(String(prd.source_plan))) === planKey && !(artifacts.prds || []).includes(pf) && !discoveredPrds.includes(pf)) {
932
949
  discoveredPrds.push(pf);
933
950
  }
934
951
  }
@@ -1244,5 +1261,5 @@ module.exports = {
1244
1261
  evaluateCondition, // exported for testing
1245
1262
  truncatePipelineContext, executeTaskStage, executePlanStage, executeScheduleStage, executeApiStage, executeMeetingStage, executeMergePrsStage, isStageComplete, resolveTemplate, // exported for testing
1246
1263
  _resolvePipelineProjects, // exported for testing
1247
- _findMeetingsInRun, _findExistingPlanForMeeting, _findExistingPrdForPlan, // exported for testing
1264
+ _findMeetingsInRun, _findExistingPlanForMeeting, _findExistingPrdForPlan, _canonicalPlanName, // exported for testing
1248
1265
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2282",
3
+ "version": "0.1.2284",
4
4
  "description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
5
5
  "bin": {
6
6
  "minions": "bin/minions.js"