@yemi33/minions 0.1.1998 → 0.1.2000

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.
@@ -3360,6 +3360,74 @@ function extractDecompositionJson(stdout, runtimeName) {
3360
3360
  return null;
3361
3361
  }
3362
3362
 
3363
+ /**
3364
+ * PR follow-up audit (W-mpej3cox00099466).
3365
+ *
3366
+ * Parses the optional `followups` array on the agent's completion report,
3367
+ * logs each entry at `info` so operators can trace fan-out, and warns when
3368
+ * a claimed `wi_id` doesn't match any record in central or per-project
3369
+ * work-items.json. The mismatch warning catches dispatches that were reverted
3370
+ * (HTTP 409 from /api/work-items dedup), fingerprinted away by the standard
3371
+ * dedup window, or otherwise didn't actually land — situations where the
3372
+ * agent claimed a follow-up exists but the dashboard/engine has no record.
3373
+ *
3374
+ * Returns the array of well-formed followup entries; malformed entries (non-
3375
+ * object, missing `wi_id`) are logged and skipped. Failures inside the
3376
+ * existence check are non-fatal — they degrade to a warn.
3377
+ */
3378
+ function processCompletionFollowups(completion, agentId, dispatchItem, config) {
3379
+ if (!completion || typeof completion !== 'object') return [];
3380
+ const raw = completion.followups;
3381
+ if (raw === undefined || raw === null) return [];
3382
+ if (!Array.isArray(raw)) {
3383
+ log('warn', `Followup audit: completion.followups must be an array; got ${typeof raw}`);
3384
+ return [];
3385
+ }
3386
+ if (raw.length === 0) return [];
3387
+ const wiId = dispatchItem?.meta?.item?.id || 'N/A';
3388
+ let existingIds = null;
3389
+ function loadExistingIds() {
3390
+ if (existingIds !== null) return existingIds;
3391
+ try {
3392
+ existingIds = new Set(queries.getWorkItems(config).map(w => w && w.id).filter(Boolean));
3393
+ } catch (err) {
3394
+ log('warn', `Followup audit: getWorkItems failed (${err.message}); skipping existence checks`);
3395
+ existingIds = new Set();
3396
+ }
3397
+ return existingIds;
3398
+ }
3399
+ const accepted = [];
3400
+ for (const entry of raw) {
3401
+ if (!entry || typeof entry !== 'object') {
3402
+ log('warn', `Followup audit (${agentId || 'unknown'} wi=${wiId}): skipping malformed entry (not an object)`);
3403
+ continue;
3404
+ }
3405
+ const followupWiId = typeof entry.wi_id === 'string' ? entry.wi_id.trim()
3406
+ : (typeof entry.wiId === 'string' ? entry.wiId.trim() : '');
3407
+ if (!followupWiId) {
3408
+ log('warn', `Followup audit (${agentId || 'unknown'} wi=${wiId}): skipping entry without wi_id`);
3409
+ continue;
3410
+ }
3411
+ const title = typeof entry.title === 'string' ? entry.title.trim() : '';
3412
+ const reason = typeof entry.reason === 'string' ? entry.reason.trim() : '';
3413
+ const parentCommentId = typeof entry.parent_comment_id === 'string'
3414
+ ? entry.parent_comment_id.trim()
3415
+ : (typeof entry.parentCommentId === 'string' ? entry.parentCommentId.trim() : '');
3416
+ log('info', `Followup audit (${agentId || 'unknown'} wi=${wiId}): dispatched ${followupWiId}${title ? ` — ${title}` : ''}${parentCommentId ? ` (comment=${parentCommentId})` : ''}${reason ? ` — ${reason}` : ''}`);
3417
+ const allIds = loadExistingIds();
3418
+ if (allIds.size > 0 && !allIds.has(followupWiId)) {
3419
+ log('warn', `Followup audit (${agentId || 'unknown'} wi=${wiId}): claimed followup ${followupWiId} not found in any work-items.json — dispatch may have been deduped, reverted, or never landed`);
3420
+ }
3421
+ accepted.push({
3422
+ wi_id: followupWiId,
3423
+ title,
3424
+ reason,
3425
+ parent_comment_id: parentCommentId,
3426
+ });
3427
+ }
3428
+ return accepted;
3429
+ }
3430
+
3363
3431
  /**
3364
3432
  * Handle decomposition result — parse sub-items from agent output and create child work items.
3365
3433
  * Called from runPostCompletionHooks when type === 'decompose'.
@@ -3538,6 +3606,17 @@ async function runPostCompletionHooks(dispatchItem, agentId, code, stdout, confi
3538
3606
  if (structuredCompletion.summary) resultSummary = String(structuredCompletion.summary);
3539
3607
  log('info', `Structured completion from ${agentId}: status=${structuredCompletion.status}, pr=${structuredCompletion.pr || 'N/A'}${structuredCompletion._source ? ` (${structuredCompletion._source})` : ''}`);
3540
3608
  }
3609
+ // PR follow-up audit (W-mpej3cox00099466) — when the agent declares
3610
+ // `followups: [{wi_id, title, reason, parent_comment_id}]` in its completion
3611
+ // report, log each entry and verify the claimed WI ids actually landed in
3612
+ // some project's work-items.json. A mismatch (claimed wi_id with no matching
3613
+ // record) is logged at warn so operators can spot dispatches that were
3614
+ // reverted, fingerprinted-deduped, or otherwise didn't take.
3615
+ if (structuredCompletion) {
3616
+ try {
3617
+ processCompletionFollowups(structuredCompletion, agentId, dispatchItem, config);
3618
+ } catch (err) { log('warn', `Followup audit: ${err.message}`); }
3619
+ }
3541
3620
  // F5 (W-mpeklod3000we69c): if the agent flagged an injection attempt in the
3542
3621
  // structured completion, force the dispatch into a non-retryable failure
3543
3622
  // with `FAILURE_CLASS.INJECTION_FLAGGED`. Inbox note + WI stamp are written
@@ -4303,4 +4382,5 @@ module.exports = {
4303
4382
  isPrAttachmentRequired,
4304
4383
  extractDecompositionJson,
4305
4384
  handleDecompositionResult,
4385
+ processCompletionFollowups,
4306
4386
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1998",
3
+ "version": "0.1.2000",
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
@@ -35,6 +35,7 @@ Before editing, split the feedback into:
35
35
 
36
36
  - **Blocking findings to fix:** verified correctness, safety, build/test failure, missing requested behavior, broken compatibility, or approval-blocking comments whose claim is valid on the current branch.
37
37
  - **Findings to answer with rationale:** comments where the current approach is intentionally correct, the reviewer misunderstood the code, the issue is stale/already addressed, or the requested change would broaden the PR beyond its purpose.
38
+ - **Out-of-scope but legitimate follow-up requests:** asks for genuinely useful work that does **not** belong in this PR (a separate bug, a related-but-distinct feature, a refactor the reviewer explicitly said to "track separately"). Spin off a new Minions work item — see `## Follow-up Dispatch` below — and reply on the originating thread with the new WI id. This is a third option alongside "fix it" and "rebut it"; it does **not** loosen the don't-broaden-the-PR rule for in-PR work.
38
39
  - **Non-blocking suggestions:** style, optional refactors, extra docs, or enhancements that are not required for approval. Do not implement these unless they are necessary to resolve a verified blocking issue.
39
40
 
40
41
  ## Health Check
@@ -55,7 +56,7 @@ Handle this like the PR author responding directly from a CLI:
55
56
  - Fix it if the feedback is valid and improves correctness, safety, maintainability, or test coverage.
56
57
  - If the current approach is intentionally correct, stale, already fixed, out of scope, or the requested change would be harmful, reply with specific rationale instead of silently changing code or ignoring the thread.
57
58
  - Handle merge conflicts when needed, preserving the PR's intended changes while keeping the branch reviewable.
58
- - Do not add unrelated cleanups or broaden the PR beyond the review feedback unless that is necessary to make the fix correct.
59
+ - Do not add unrelated cleanups or broaden the PR beyond the review feedback unless that is necessary to make the fix correct. For legitimate-but-out-of-scope asks, see `## Follow-up Dispatch` below — spin off a separate WI instead of broadening this PR.
59
60
 
60
61
  ## Validation
61
62
 
@@ -94,6 +95,20 @@ After pushing, respond to each review comment/thread:
94
95
  - **GitHub**: Reply to each review comment, resolve conversations you've fixed
95
96
  - **ADO**: Use `az` CLI first to reply to each thread and update status when supported; use ADO MCP only as a fallback when `az` is unavailable or insufficient. Set status to `fixed` or `closed` for fixes; leave `active` for rationale replies
96
97
 
98
+ ## Follow-up Dispatch
99
+
100
+ When a comment asks for legitimate work that is **clearly out of scope** for this PR (separate bug, different feature, refactor the reviewer explicitly asked to "track separately"), spin off a new Minions work item via the dashboard API instead of broadening the PR or silently declining. Read the full contract — decision tree, exact curl shape, required `meta.pr_followup` fields, dedup behavior, and the mandatory comment-thread reply — at:
101
+
102
+ `{{team_root}}/playbooks/templates/followup-dispatch.md`
103
+
104
+ Key rules (full detail in the template):
105
+
106
+ - The follow-up is **additive**. You still fix the in-scope feedback in this PR and post the normal fix comment.
107
+ - Reply on the originating comment thread with the new WI id (e.g. `Tracked as follow-up: W-…`) and resolve that sub-thread.
108
+ - Always include `meta.pr_followup.parent_comment_id` — without it the server cannot dedup retries.
109
+ - Do not pick `agent`; let `routing.md` decide.
110
+ - Record every follow-up you dispatched in the `followups` array of your completion JSON.
111
+
97
112
  ## When to Stop
98
113
 
99
114
  Your task is complete when each review finding has either been fixed or answered with rationale, the validation story is truthful and sufficient for review, the fix is pushed if code changed, the PR is commented, and addressed threads are resolved. Do NOT continue into unrelated improvements.
@@ -98,6 +98,20 @@ After running the command, confirm it succeeded (check the command output for er
98
98
  If you encounter merge conflicts (e.g., the PR shows conflicts):
99
99
  1. Note the conflict in your review comment. Do NOT attempt to resolve — flag it for the author.
100
100
 
101
+ ## Follow-up Dispatch
102
+
103
+ If, while reviewing, you spot substantial work that should be tracked separately rather than blocking this PR (a related-but-distinct bug, a feature gap that didn't ship in this diff, a refactor that's worth doing later), you may dispatch a follow-up Minions work item instead of pinning it as a blocking finding. Read the full contract — decision tree, exact curl shape, required `meta.pr_followup` fields, dedup behavior, and the mandatory comment-thread reply — at:
104
+
105
+ `{{team_root}}/playbooks/templates/followup-dispatch.md`
106
+
107
+ Key rules (full detail in the template):
108
+
109
+ - Use this for **legitimate out-of-scope follow-ups**, not for findings that should block the PR.
110
+ - The review verdict (`APPROVE` / `REQUEST_CHANGES`) is unchanged by the follow-up — pick the verdict based on the PR's own contents.
111
+ - Reply on the originating thread with the new WI id and resolve that sub-thread (do not leave it open).
112
+ - Always include `meta.pr_followup.parent_comment_id` so the server can dedup if you (or another agent) retries.
113
+ - Record every follow-up you dispatched in the `followups` array of your completion JSON.
114
+
101
115
  ## Do not run git checkout on the main working tree. Use `git diff` and `git show` only.
102
116
 
103
117
  ## When to Stop
@@ -92,6 +92,27 @@ The engine provides a completion report path in the prompt and in `MINIONS_COMPL
92
92
 
93
93
  For the canonical schema — every field, the `failure_class` enum, `noop:true` semantics, `retryable` / `needs_rerun` shape, and the artifacts array — see `docs/completion-reports.md`. The JSON report is the primary signal; fenced `completion` blocks in stdout are accepted only as a fallback.
94
94
 
95
+ ## Minions API access
96
+
97
+ The Minions dashboard runs at `http://localhost:7331` whenever the engine is up. Agents may call its HTTP endpoints when (and only when) the playbook explicitly authorizes it for a particular task — most dispatches do not need any API access, and uninvited writes to engine-managed state are still prohibited (see "Do NOT write to `agents/*/status.json`" above).
98
+
99
+ When a playbook authorizes an API call (e.g. follow-up work-item dispatch from `playbooks/templates/followup-dispatch.md`), use the standard `curl` shape:
100
+
101
+ ```bash
102
+ curl -sS -X POST http://localhost:7331/api/<route> \
103
+ -H 'Content-Type: application/json' \
104
+ -H 'X-Minions-Agent: <your-agent-id>' \
105
+ -H 'X-Minions-Origin-Wi: <current-work-item-id>' \
106
+ -d '<json-payload>'
107
+ ```
108
+
109
+ Conventions:
110
+
111
+ - The full live route surface is available at `GET http://localhost:7331/api/routes` (the route registry includes method, path, and a one-line description for every handler).
112
+ - **Identification headers (for traceability, not auth).** Set `X-Minions-Agent` to your agent id and `X-Minions-Origin-Wi` to the work-item id you were dispatched against. Endpoints that persist these (e.g. `POST /api/work-items`) store them as `_originAgent` / `_originWi` for the dashboard timeline. Agents do **not** have a `X-CC-Turn-Id` — that header is reserved for Command Center turns.
113
+ - The dashboard binds to `127.0.0.1` only; no auth token is required for localhost calls.
114
+ - Treat HTTP 4xx as a contract violation: surface the response body in your completion summary instead of retrying blindly.
115
+
95
116
  ## Long-Running Commands
96
117
 
97
118
  Builds, dependency installs, tests, and local servers can be quiet for long periods. Run the repo's normal CLI commands and let them finish; do not add artificial progress output, heartbeat loops, or command-specific workarounds just to keep Minions active.
@@ -0,0 +1,157 @@
1
+ # Follow-up Dispatch (PR-comment → new WI)
2
+
3
+ When a human leaves a PR comment that asks for work that is **legitimate but
4
+ outside the scope of the current PR** (a different bug, a related-but-separate
5
+ feature, a follow-up cleanup that doesn't belong in the current diff), do **not**
6
+ broaden the PR and do **not** silently rebut the comment. Spin off a new Minions
7
+ work item via the dashboard API and reply on the originating comment thread with
8
+ a link to it.
9
+
10
+ This carve-out only enables creating a **separate** work item. It does not
11
+ loosen the rule that the *current* PR should stay focused on the original
12
+ review feedback.
13
+
14
+ ## Decision tree
15
+
16
+ For each human comment that asks for additional work, classify it before
17
+ editing:
18
+
19
+ 1. **Fix in this PR** — the request is in scope (regression of the current
20
+ diff, a flaw in the new code, missing requested behavior the PR was already
21
+ supposed to cover). Fix it on this branch, push, reply on the thread.
22
+ 2. **Answer with rationale** — the request is invalid, stale, already
23
+ addressed, or would harm the code. Post an evidence-backed rebuttal on the
24
+ thread; do **not** create a follow-up WI for it.
25
+ 3. **Follow-up WI** — the request is legitimate but clearly out of scope for
26
+ the current PR (different file/system, different bug, distinct feature, a
27
+ refactor the reviewer explicitly asked you to "track separately" or "do in a
28
+ follow-up"). Create a new work item via the API (see below) and reply on the
29
+ originating thread with the new WI id.
30
+
31
+ If you are unsure between #1 and #3, prefer **#3 (follow-up)** so the current
32
+ PR stays small and reviewable. If you are unsure between #2 and #3, prefer #2
33
+ and explain your reasoning — don't manufacture work.
34
+
35
+ ## The API call
36
+
37
+ The Minions dashboard runs on `http://localhost:7331`. Create a follow-up work
38
+ item with `POST /api/work-items`:
39
+
40
+ ```bash
41
+ curl -sS -X POST http://localhost:7331/api/work-items \
42
+ -H 'Content-Type: application/json' \
43
+ -H 'X-Minions-Agent: <your-agent-id>' \
44
+ -H 'X-Minions-Origin-Wi: <current-wi-id>' \
45
+ -d '{
46
+ "title": "Short imperative summary of the follow-up",
47
+ "type": "implement",
48
+ "priority": "medium",
49
+ "project": "<project-name>",
50
+ "description": "Why this is a follow-up, what should change, acceptance.\n\nOriginated from <reviewer> on PR #<N> (<url>): \"<verbatim quote of the ask>\"",
51
+ "references": [
52
+ {"url": "<originating-pr-url>", "label": "Originated from PR #<N> comment"}
53
+ ],
54
+ "meta": {
55
+ "pr_followup": {
56
+ "parent_pr_url": "<originating-pr-url>",
57
+ "parent_pr_id": "<canonical PR id, e.g. github:owner/repo#123 or PR-123>",
58
+ "parent_comment_id": "<exact comment/thread id from the host API>",
59
+ "parent_comment_author": "<reviewer login>"
60
+ }
61
+ }
62
+ }'
63
+ ```
64
+
65
+ Required fields and their conventions:
66
+
67
+ - **`title`** — short imperative ("Add foo to bar", "Fix off-by-one in baz").
68
+ - **`type`** — pick from the routing types (`implement`, `fix`, `test`,
69
+ `docs`, `ask`, `explore`, …). For new feature/fix work, `implement` is
70
+ almost always correct; use `fix` only when the follow-up is about an existing
71
+ PR's bug. Do **not** set `agent` — let `routing.md` pick.
72
+ - **`project`** — name of the same project the originating PR belongs to.
73
+ Required when more than one project is configured.
74
+ - **`description`** — explain *why* this is a follow-up and quote the exact
75
+ ask. Future readers should understand the scope without re-reading the PR
76
+ thread.
77
+ - **`references`** — at least one entry pointing back at the originating PR.
78
+ - **`meta.pr_followup`** — the four-field shape above. Used by the dashboard
79
+ for traceability chips and by the server for dedup (a second request with the
80
+ same `parent_comment_id` returns the existing WI id rather than creating a
81
+ duplicate).
82
+
83
+ Headers:
84
+
85
+ - **`X-Minions-Agent`** — your agent id (e.g. `lambert`, `ripley`,
86
+ `temp-mp3dop1v…`). Persisted as `_originAgent` on the new WI.
87
+ - **`X-Minions-Origin-Wi`** — the id of the work item you were dispatched
88
+ against (the one whose PR you are responding to). Persisted as `_originWi`.
89
+
90
+ Both headers are optional but strongly recommended — they let the dashboard
91
+ correlate the follow-up to the dispatch that created it.
92
+
93
+ ## Server responses
94
+
95
+ | Status | Body | Meaning |
96
+ |---|---|---|
97
+ | 200 | `{ ok: true, id: "W-…" }` | New follow-up WI created. |
98
+ | 200 | `{ ok: true, id: "W-…", duplicate: true, duplicateOf: "W-…" }` | The standard title/type/project dedup window matched an in-flight WI. Same WI id is returned; reply on the comment thread with that id. |
99
+ | 409 | `{ error: "followup_already_dispatched", existingWiId: "W-…" }` | Another agent (or your previous attempt) already dispatched a follow-up for this exact `parent_comment_id`. Reply on the comment thread linking the existing WI; do not retry. |
100
+ | 400 | `{ error: "<message>" }` | Validation failed. Inspect the message: the most common cause is a malformed `meta.pr_followup` shape. Fix and retry. |
101
+
102
+ Treat 409 as a **success** for your purposes — the follow-up exists, you just
103
+ weren't the one who created it. Still post the reply on the comment thread with
104
+ the returned `existingWiId`.
105
+
106
+ ## Mandatory post-dispatch step
107
+
108
+ After the API call returns a WI id (whether `id` from 200 or `existingWiId`
109
+ from 409), reply on the originating comment thread with a link back:
110
+
111
+ > Tracked as follow-up: **W-…** — see the Minions dashboard. Continuing with
112
+ > the in-place fix on this PR.
113
+
114
+ Then **resolve that sub-thread** (GitHub: "Resolve conversation"; ADO: set
115
+ status to `closed`) even if other review threads on the PR are still open. The
116
+ follow-up WI is the durable artifact; the comment thread should not be left
117
+ hanging.
118
+
119
+ ## Record the follow-up in your completion report
120
+
121
+ Add a `followups` array to your JSON completion report so the engine has an
122
+ auditable record of what you fanned out to:
123
+
124
+ ```json
125
+ {
126
+ "status": "success",
127
+ "summary": "…",
128
+ "followups": [
129
+ {
130
+ "wi_id": "W-mp7abc…",
131
+ "title": "Add foo to bar",
132
+ "reason": "Out-of-scope ask from reviewer on PR #123 comment #4567890",
133
+ "parent_comment_id": "4567890"
134
+ }
135
+ ]
136
+ }
137
+ ```
138
+
139
+ The engine logs each entry at `info` and warns if the array references a WI id
140
+ that doesn't exist in any project's `work-items.json` (mismatch between what
141
+ you claim and what the API actually created — usually a sign that the dispatch
142
+ was reverted or the response was misread).
143
+
144
+ ## Do NOT
145
+
146
+ - Do not pick `agent` for the follow-up; the routing layer assigns one based
147
+ on `type` + `project`.
148
+ - Do not set `X-CC-Turn-Id` (that header is for Command Center turns; agents
149
+ don't have a turn id). Use `X-Minions-Agent`/`X-Minions-Origin-Wi` instead.
150
+ - Do not skip the comment-thread reply — without it, the human has no signal
151
+ that their ask was tracked and the engine will keep re-routing the comment
152
+ as `pendingFix`.
153
+ - Do not spin off follow-ups for nits, style notes, or things you are about
154
+ to fix in this PR anyway. Reserve this for asks that genuinely belong in a
155
+ separate WI.
156
+ - Do not omit `meta.pr_followup.parent_comment_id`; without it the server
157
+ cannot dedup and you will create duplicates on every retry.