@yemi33/minions 0.1.2103 → 0.1.2105

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/dashboard.js CHANGED
@@ -3329,8 +3329,12 @@ function normalizeMeetingParticipants(participants) {
3329
3329
 
3330
3330
 
3331
3331
  function getWorkItemPrRef(input) {
3332
- if (!input || typeof input !== 'object') return null;
3333
- return input.targetPr || input.pr || input.prId || input.prNumber || input.pullRequest || input.sourcePr || input.prUrl || null;
3332
+ // Delegates to shared.extractWorkItemPrRef so the dashboard's WI-create
3333
+ // path detects PR pointers in references[*].url / meta.pr_followup /
3334
+ // description text — not just the canonical structured fields — and routes
3335
+ // the dispatch to the existing PR branch instead of a fresh `work/<wi-id>`
3336
+ // parallel branch (issue #2999 / W-mpx6i5kh000ac040).
3337
+ return shared.extractWorkItemPrRef(input);
3334
3338
  }
3335
3339
 
3336
3340
  function isPrTargetedWorkType(workType) {
@@ -3341,17 +3345,11 @@ function trimTrailingPrRefPunctuation(value) {
3341
3345
  return String(value || '').replace(/[),.;:]+$/g, '');
3342
3346
  }
3343
3347
 
3348
+ // Thin wrapper over shared.extractPrRefFromText so callers in dashboard.js
3349
+ // keep their existing import shape while the canonical regex + extraction
3350
+ // logic lives in engine/shared.js (issue #2999 / W-mpx6i5kh000ac040).
3344
3351
  function extractPrRefFromText(value) {
3345
- const text = String(value || '');
3346
- if (!text.trim()) return null;
3347
- const urlMatch = text.match(/https?:\/\/[^\s<>()]+(?:\/pull\/\d+|\/pullrequest\/\d+)[^\s<>()]*/i);
3348
- if (urlMatch) return trimTrailingPrRefPunctuation(urlMatch[0]);
3349
- const canonicalMatch = text.match(/\b(?:github|ado):[^\s#]+#\d+\b/i);
3350
- if (canonicalMatch) return canonicalMatch[0];
3351
- const legacyMatch = text.match(/\bPR-(\d+)\b/i);
3352
- if (legacyMatch) return legacyMatch[1];
3353
- const numberMatch = text.match(/\b(?:pr|pull\s+request|pullrequest)\s*#?\s*(\d+)\b/i);
3354
- return numberMatch ? numberMatch[1] : null;
3352
+ return shared.extractPrRefFromText(value);
3355
3353
  }
3356
3354
 
3357
3355
 
package/engine/shared.js CHANGED
@@ -4300,6 +4300,70 @@ function parsePrUrl(url) {
4300
4300
  return parseGitHubPrUrl(url) || parseAdoPrUrl(url);
4301
4301
  }
4302
4302
 
4303
+ // ─── PR reference extraction from work-item bodies (W-mpx6i5kh000ac040) ─────
4304
+ //
4305
+ // `extractPrRefFromText` scans free-form text (description, comment bodies,
4306
+ // release notes) for the first PR reference it can recognise: a GitHub or ADO
4307
+ // PR URL, a canonical `github:owner/repo#N` / `ado:org/proj/repo#N` id, or a
4308
+ // legacy `PR-<n>` token. Returns null when nothing parseable is found.
4309
+ //
4310
+ // `extractWorkItemPrRef` is the engine + dashboard's single source of truth
4311
+ // for "does this work-item target an existing PR?". It checks the canonical
4312
+ // structured fields first (current behaviour preserved), then falls back to
4313
+ // `references[*].url`, `meta.pr_followup.parent_pr_url`, and finally a text
4314
+ // scan of the title/description. Returning a truthy ref keeps `linkedPr` /
4315
+ // `isPrTargeted` paths intact and prevents PR-feedback fix WIs from being
4316
+ // dispatched on a fresh `work/<wi-id>` branch when only the description /
4317
+ // references[] carried the PR pointer (issue #2999).
4318
+ function _trimTrailingPrRefPunctuation(value) {
4319
+ return String(value || '').replace(/[),.;:]+$/g, '');
4320
+ }
4321
+
4322
+ function extractPrRefFromText(value) {
4323
+ const text = String(value || '');
4324
+ if (!text.trim()) return null;
4325
+ const urlMatch = text.match(/https?:\/\/[^\s<>()]+(?:\/pull\/\d+|\/pullrequest\/\d+)[^\s<>()]*/i);
4326
+ if (urlMatch) return _trimTrailingPrRefPunctuation(urlMatch[0]);
4327
+ const canonicalMatch = text.match(/\b(?:github|ado):[^\s#]+#\d+\b/i);
4328
+ if (canonicalMatch) return canonicalMatch[0];
4329
+ const legacyMatch = text.match(/\bPR-(\d+)\b/i);
4330
+ if (legacyMatch) return legacyMatch[1];
4331
+ const numberMatch = text.match(/\b(?:pr|pull\s+request|pullrequest)\s*#?\s*(\d+)\b/i);
4332
+ return numberMatch ? numberMatch[1] : null;
4333
+ }
4334
+
4335
+ function extractWorkItemPrRef(item) {
4336
+ if (!item || typeof item !== 'object') return null;
4337
+ const structured = item.targetPr
4338
+ || item.pr
4339
+ || item.pr_id
4340
+ || item.prId
4341
+ || item.sourcePr
4342
+ || item.pullRequest
4343
+ || item.prUrl
4344
+ || item.prNumber;
4345
+ if (structured) return structured;
4346
+ // references[*].url — manual /api/work-items callers often supply the PR
4347
+ // pointer here when no structured prId/targetPr is known up front.
4348
+ if (Array.isArray(item.references)) {
4349
+ for (const ref of item.references) {
4350
+ const url = ref && typeof ref === 'object' ? ref.url : null;
4351
+ const fromUrl = extractPrRefFromText(url);
4352
+ if (fromUrl) return fromUrl;
4353
+ }
4354
+ }
4355
+ // meta.pr_followup.parent_pr_url — set by playbooks/templates/followup-dispatch.md.
4356
+ const followupUrl = item?.meta?.pr_followup?.parent_pr_url;
4357
+ if (followupUrl) {
4358
+ const fromFollowup = extractPrRefFromText(followupUrl);
4359
+ if (fromFollowup) return fromFollowup;
4360
+ }
4361
+ // Last resort: scan description + title for a PR URL / canonical id.
4362
+ const fromDescription = extractPrRefFromText(item.description);
4363
+ if (fromDescription) return fromDescription;
4364
+ return extractPrRefFromText(item.title) || null;
4365
+ }
4366
+
4303
4367
  function getProjectPrScope(project) {
4304
4368
  if (!project) return '';
4305
4369
  const host = String(project.repoHost || '').toLowerCase();
@@ -5632,6 +5696,8 @@ module.exports = {
5632
5696
  parseGitHubPrUrl, // exported for testing
5633
5697
  parseAdoPrUrl, // exported for testing
5634
5698
  parsePrUrl, // exported for testing
5699
+ extractPrRefFromText,
5700
+ extractWorkItemPrRef,
5635
5701
  getProjectPrScope,
5636
5702
  getPrNumber,
5637
5703
  getPrDisplayId,
package/engine.js CHANGED
@@ -5583,8 +5583,13 @@ function renderProjectWorkItemPromptForAgent(item, workType, agentId, config, pr
5583
5583
  }
5584
5584
 
5585
5585
  function getWorkItemPrRef(item) {
5586
- if (!item || typeof item !== 'object') return null;
5587
- return item.targetPr || item.pr || item.pr_id || item.sourcePr || item.pullRequest || item.prUrl || item.prNumber || null;
5586
+ // Delegates to shared.extractWorkItemPrRef so engine + dashboard agree on
5587
+ // which work-item fields count as PR pointers covers structured fields,
5588
+ // references[*].url, meta.pr_followup.parent_pr_url, and description text
5589
+ // (issue #2999 / W-mpx6i5kh000ac040). Defence in depth: even if a manual
5590
+ // /api/work-items create slipped through without structured PR fields, the
5591
+ // engine still detects the PR pointer here at dispatch time.
5592
+ return shared.extractWorkItemPrRef(item);
5588
5593
  }
5589
5594
 
5590
5595
  function resolveWorkItemPrRecord(item, project) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.2103",
3
+ "version": "0.1.2105",
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"
package/playbooks/fix.md CHANGED
@@ -46,6 +46,10 @@ Before editing, split the feedback into:
46
46
 
47
47
  Before starting work, run `git status` and verify the worktree is clean and on the expected branch (`{{pr_branch}}`). If the worktree is dirty or on the wrong branch, report the issue and stop.
48
48
 
49
+ ### Branch-mismatch guard (issue #2999)
50
+
51
+ If your task description, `references[]`, or the PR thread clearly identifies an existing PR but your checkout branch does **not** match the PR's source branch — for example you are on `work/W-…` while the task references PR `!5284819` (source branch `yemishin/task-hub-symphony-gate`) — **stop immediately**. Do not branch off master, do not replay the PR's commits onto a fresh branch, and do not open a duplicate PR. Emit a non-retryable completion (`failure_class: "branch-mismatch"`, `retryable: false`) with the referenced PR id and the actual branch you found, so the engine and the operator can re-dispatch on the correct branch. The canonical bad incident is in issue #2999 — manual fix WI `W-mpwxgd4v00078c06` opened duplicate ADO PR `!5288540` instead of patching `!5284819`.
52
+
49
53
  ## Working Style
50
54
 
51
55
  Use subagents only for genuinely parallel, independent tasks. For sequential work, single-file edits, searches, and file reads, work directly — do not spawn subagents.
@@ -64,6 +64,17 @@ Application:
64
64
  - When you create a work item programmatically (API, plan-to-prd, scripts), set the WI's `branch` (or PRD `feature_branch`) to the conventional name so the engine creates the worktree on the right branch from the start. `dashboard.js` derives this automatically when callers omit `branch`.
65
65
  - The legacy `feat/<id>-<slug>` and bare `work/<id>` formats are deprecated; the engine no longer falls back to them.
66
66
 
67
+ ### PR-fix exception (DO NOT branch off master)
68
+
69
+ For a `type: "fix"` work item that targets an existing PR, the engine reuses the PR's **source branch** (e.g. `yemishin/task-hub-symphony-gate`) instead of derivingthe `user/<loginname>/<wi-id>-<slug>` form. PR pointers are detected from any of:
70
+
71
+ - Structured fields: `targetPr`, `pr_id`, `prUrl`, `prNumber`, `pullRequest`, `sourcePr`
72
+ - `references[*].url` (manual `/api/work-items` callers usually pass the PR pointer here)
73
+ - `meta.pr_followup.parent_pr_url` (follow-up dispatch template)
74
+ - A PR URL or canonical `github:owner/repo#N` / `ado:org/proj/repo#N` id in the `description` text
75
+
76
+ If you are running a fix task and `{{pr_branch}}` is populated, your worktree is already on that branch — push the fix commit there and do **not** create a new PR. If `{{pr_branch}}` is empty but the task description / references clearly point at an existing PR, **stop** and emit a non-retryable completion (`failure_class: 'branch-mismatch'`) instead of branching off master and opening a duplicate PR. See issue #2999 for the canonical bad incident (ADO PR `!5284819` → duplicate `!5288540`).
77
+
67
78
  ## Engine Rules (apply to all tasks)
68
79
 
69
80
  **Context compaction:** Your context window may be compacted mid-task by Claude's infrastructure. If you notice your earlier conversation history appears truncated or summarized, this is normal and expected. Do not interpret compaction as a signal to stop early or wrap up. Continue working toward your task objective — all relevant instructions and state remain available.