@yemi33/minions 0.1.1969 → 0.1.1971

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/engine/ado.js CHANGED
@@ -1103,8 +1103,21 @@ async function pollPrHumanComments(config) {
1103
1103
  const authorName = (comment.author?.displayName || '').toLowerCase();
1104
1104
  if (ignoredAuthors.some(a => authorName.includes(a))) continue;
1105
1105
  if (/^#{1,3}\s*(Coverage|Build|Test|Deploy|Pipeline)\s*(Report|Status|Result|Summary)/i.test(content)) continue;
1106
- // Detect agent comments (included in context, but don't trigger fix)
1107
- const isAgent = /\bMinions\s*\(/i.test(content) || /\bby\s+Minions\b/i.test(content) || /\[minions\]/i.test(content);
1106
+ // Detect agent comments (included in context, but don't trigger fix).
1107
+ // W-mpbhnaim000q9ed3: also match the VERDICT-prefixed review body. The
1108
+ // `Review by Minions (...)` sign-off can be skipped by a review agent
1109
+ // (Ripley on PR ado:office/iss/constellation#5056697, 2026-05-18: posted
1110
+ // a 1100-char APPROVE body with no sign-off → engine treated it as
1111
+ // human feedback → fix-dispatch loop spammed "Skipping human-feedback
1112
+ // fix ... noop on the same head" once a minute for 9+ hours). The
1113
+ // VERDICT prefix is itself an engine-owned structured-output contract
1114
+ // (parseReviewVerdict, playbooks/review.md:69-72) — humans don't write
1115
+ // it as the first line of a comment. The `^` + /m anchor keeps quoted
1116
+ // verdict text inside a human reply from matching.
1117
+ const isAgent = /\bMinions\s*\(/i.test(content)
1118
+ || /\bby\s+Minions\b/i.test(content)
1119
+ || /\[minions\]/i.test(content)
1120
+ || /^\s*VERDICT:\s*(APPROVE|REQUEST_CHANGES)\b/im.test(content);
1108
1121
 
1109
1122
  const entry = {
1110
1123
  threadId: thread.id,
@@ -1369,11 +1382,24 @@ async function checkLiveReviewStatus(pr, project) {
1369
1382
 
1370
1383
  /**
1371
1384
  * W-mp7b1g8q000fea45 — Reviewer vote reconciliation on verdict flip.
1385
+ * W-mpbhnaim000q9ed3 — Bug A: also cast +10 when current vote is 0 (not just <0).
1372
1386
  *
1373
- * Resets the *authenticated reviewer's own* prior negative vote (-5 wait-for-author
1374
- * or -10 rejected) to +10 (approved) when an agent flips their verdict from
1375
- * request_changes approved on a re-review. Mirrors the existing target-branch
1376
- * re-approval pattern at engine/ado.js:837-852 (PUT reviewers/{myId} {vote:10}).
1387
+ * Ensures the *authenticated reviewer's own* vote is positive (+10) when an agent
1388
+ * flips their verdict from request_changes approved on a re-review. Mirrors the
1389
+ * existing target-branch re-approval pattern at engine/ado.js:837-852 (PUT
1390
+ * reviewers/{myId} {vote:10}). Covers both:
1391
+ * - prior negative vote (-5 wait-for-author, -10 rejected) → cleared to +10, and
1392
+ * - prior neutral vote (0 — never cast, or reset by another path and never
1393
+ * re-cast) → upgraded to +10 so the platform reflects the approved verdict.
1394
+ *
1395
+ * Without the neutral-vote upgrade, an agent flip to approved leaves the engine's
1396
+ * `reviewStatus` stuck at `changes-requested` because checkLiveReviewStatus keeps
1397
+ * seeing votes that don't carry an approval, and the next tick's reconciler
1398
+ * dispatches a redundant human-feedback fix (Ripley on PR 5056697, 2026-05-18:
1399
+ * `"Skipping human-feedback fix ... noop on the same head"` once a minute for 9+ hours).
1400
+ *
1401
+ * The function name remains `resetReviewerNegativeVote` for export-stability —
1402
+ * it is referenced in playbooks/review.md and called from lifecycle.js.
1377
1403
  *
1378
1404
  * IMPORTANT: only operates on the authenticated reviewer's own vote. Other
1379
1405
  * reviewers' negative votes (humans, other minions on a different account) are
@@ -1385,7 +1411,7 @@ async function checkLiveReviewStatus(pr, project) {
1385
1411
  * Otherwise returns:
1386
1412
  * {
1387
1413
  * attempted: boolean, // true if we found our reviewer entry and tried to PUT
1388
- * changed: boolean, // true if the reviewer's vote was actually flipped from <0 → 10
1414
+ * changed: boolean, // true if the reviewer's vote was actually flipped to +10
1389
1415
  * fromVote: number|null,// the reviewer's prior vote (null if not a reviewer on the PR)
1390
1416
  * toVote: number|null,// the vote after PUT (10 if changed, prior value otherwise)
1391
1417
  * }
@@ -1421,16 +1447,19 @@ async function resetReviewerNegativeVote(pr, project) {
1421
1447
  // is the normal GH path; on ADO we generally do vote, so this is rare.)
1422
1448
  return { attempted: false, changed: false, fromVote: null, toVote: null };
1423
1449
  }
1424
- if (myVote >= 0) {
1425
- // Already neutral or positive — nothing to reset.
1450
+ if (myVote > 0) {
1451
+ // Already positive — platform already matches the approved verdict.
1426
1452
  return { attempted: false, changed: false, fromVote: myVote, toVote: myVote };
1427
1453
  }
1454
+ // W-mpbhnaim000q9ed3: cover myVote === 0 too. A neutral vote on verdict flip
1455
+ // means the agent never cast (or someone reset to 0), so the platform doesn't
1456
+ // reflect approval yet; PUT +10 so reviewStatus reconciler stops looping.
1428
1457
  await adoFetch(`${repoBase}/pullrequests/${prNum}/reviewers/${myId}?api-version=7.1`, token, {
1429
1458
  method: 'PUT',
1430
1459
  body: JSON.stringify({ vote: 10 }),
1431
1460
  timeout: 4000,
1432
1461
  });
1433
- log('info', `PR ${pr.id}: reset reviewer vote ${myVote} → 10 (verdict flipped to approved)`);
1462
+ log('info', `PR ${pr.id}: reset reviewer vote ${myVote} → 10 on verdict flip`);
1434
1463
  return { attempted: true, changed: true, fromVote: myVote, toVote: 10 };
1435
1464
  } catch (e) {
1436
1465
  log('warn', `Reviewer vote reset for ${pr?.id || 'unknown PR'}: ${e.message}`);
package/engine/shared.js CHANGED
@@ -1959,9 +1959,15 @@ const WORK_TYPE = {
1959
1959
  // and /api/work-items/retry) refuses to create or re-spawn a project-less WI
1960
1960
  // of any type in this set when PROJECTS.length !== 1.
1961
1961
  //
1962
- // Complement of engine.js READ_ONLY_ROOT_TASK_TYPES; `docs` is intentionally
1963
- // also exempt because docs edits run at the Minions root, not in a project
1964
- // worktree.
1962
+ // Complement of engine.js READ_ONLY_ROOT_TASK_TYPES. `docs` is included
1963
+ // (W-mpbgo1wv000gf75b): docs edits run as worktree-requiring dispatches
1964
+ // against the minions repo, so without a project the engine's
1965
+ // resolveProjectRootDir falls back to MINIONS_DIR's parent and can collapse
1966
+ // to a drive root on installs where MINIONS_DIR sits one level below the
1967
+ // filesystem root (e.g. `D:\squad` → `D:\`). Failing fast at HTTP-validate
1968
+ // time is preferable to letting a project-less docs WI land and loop on
1969
+ // WORKTREE_PREFLIGHT. Callers doing minions-repo docs work must pass
1970
+ // `project: "minions"` explicitly.
1965
1971
  const WORKTREE_REQUIRING_TYPES = new Set([
1966
1972
  WORK_TYPE.FIX,
1967
1973
  WORK_TYPE.IMPLEMENT,
@@ -1970,6 +1976,7 @@ const WORKTREE_REQUIRING_TYPES = new Set([
1970
1976
  WORK_TYPE.VERIFY,
1971
1977
  WORK_TYPE.REVIEW,
1972
1978
  WORK_TYPE.DECOMPOSE,
1979
+ WORK_TYPE.DOCS,
1973
1980
  ]);
1974
1981
 
1975
1982
  const PLAN_STATUS = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1969",
3
+ "version": "0.1.1971",
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"
@@ -88,6 +88,8 @@ Minimum diff to ship: <one-line description of the smallest change that would fl
88
88
 
89
89
  Non-blocking observations: None
90
90
  - (default to `None`; if you list any, each must be tied to an existing diff line in the form `path:line — observation`)
91
+
92
+ Review by Minions ({{agent_name}} — {{agent_role}})
91
93
  ```
92
94
 
93
95
  After running the command, confirm it succeeded (check the command output for errors). If it fails, retry once.
@@ -100,6 +102,10 @@ If you encounter merge conflicts (e.g., the PR shows conflicts):
100
102
 
101
103
  ## When to Stop
102
104
 
103
- Your task is complete when your review comment (with `VERDICT: APPROVE` or `VERDICT: REQUEST_CHANGES` on the first line) has been posted successfully and the completion report has `verdict: "approved"` or `verdict: "changes-requested"`.
105
+ Your task is complete when your review comment has been posted successfully with BOTH:
106
+ - `VERDICT: APPROVE` or `VERDICT: REQUEST_CHANGES` on the first line, AND
107
+ - the `Review by Minions ({{agent_name}} — {{agent_role}})` sign-off on the last line of the body
108
+
109
+ and the completion report has `verdict: "approved"` or `verdict: "changes-requested"`. The sign-off is what lets the engine distinguish agent reviews from human PR comments on ADO; without it the engine will queue a redundant fix-dispatch loop (W-mpbhnaim000q9ed3, Ripley on PR 5056697).
104
110
 
105
111
  Do NOT stop before posting the review. Do NOT continue reading unrelated files after posting.
@@ -146,7 +146,7 @@ curl -s http://localhost:{{dashboard_port}}/api/status
146
146
 
147
147
  **Required fields per endpoint** — the server returns `{ error: "..." }` if missing. Common cases:
148
148
  - `POST /api/work-items`: `title` REQUIRED. `description` recommended. `project` REQUIRED when multiple projects are configured (server returns the list of known names if you guess wrong). `type` defaults to `implement`; valid values: `fix`, `implement`, `implement:large`, `explore`, `ask`, `review`, `test`, `verify`. Agent hint via `agent` (string) or `agents` (array).
149
- - Exempt from the `project` requirement (these run rootless or via central paths): `ask`, `explore`, `plan`, `plan-to-prd`, `meeting`, `docs`. Every other type needs a project worktree, so the server rejects project-less creates with `400 { error, knownProjects }` when ≠1 project is configured.
149
+ - Exempt from the `project` requirement (these run rootless or via central paths): `ask`, `explore`, `plan`, `plan-to-prd`, `meeting`. (`docs` is intentionally NOT exempt — it's write-capable and lands in `WORKTREE_REQUIRING_TYPES`, so it needs a real project worktree. For minions-repo docs work, pass `project: "minions"` explicitly.) Every other type needs a project worktree, so the server rejects project-less creates with `400 { error, knownProjects }` when ≠1 project is configured.
150
150
  - **`meta.keep_processes: true`** — opt-in flag that lets the agent leave specific descendant PIDs running after it exits (default: engine reaps EVERYTHING the agent spawned). **Set this whenever the user's intent is to leave a process alive after the agent finishes** — e.g. "spin up the dev server and exit", "start the watcher and leave it running", "set up my dev env", "keep the emulator open", "launch the daemon for me", "boot the constellation host and disconnect". Don't set it for normal build/test/run-once tasks (`npm test`, `npm run build`, one-shot scripts) — those should be reaped. Also accepts optional `meta.keep_processes_ttl_minutes` (default 60, hard-cap 1440 = 24h). When you set this flag, also make the WI title/description say something like "leave the dev server running" so the agent knows to write `agents/<id>/keep-pids.json` before exiting (the playbook injects the contract automatically when the flag is on). Example: `-d '{"title":"Spin up Constellation dev env and leave server running","type":"implement","project":"constellation","description":"Run bun install + bun run dev. Leave the dev server (port 5173) and Constellation host (port 3001) running after you exit so the user can iterate.","meta":{"keep_processes":true,"keep_processes_ttl_minutes":240}}'`. Inspect / kill kept PIDs anytime via `GET /api/keep-processes` and `POST /api/keep-processes/kill`.
151
151
  - **`skipPr: true`** — opt-in flag that tells the engine NOT to enforce the PR-attachment contract for this work item, so the WI can complete `done` without the missing-PR hard-fail. **Set this when the dispatch mutates state OUTSIDE any tracked git repo and therefore cannot produce a PR** — e.g. cleaning `~/.claude/skills/`, editing runtime config under `~/.config/`, resetting the dashboard cache, mutating engine JSON state files (`engine/*.json`) the engine itself owns, or local tooling installs. **Do NOT set it for any task that touches a tracked repo's source** — even one-line diffs in a real repo should produce a PR. Type-selection rule of thumb: prefer `type: "explore"` for genuinely read-only tasks (rootless, no worktree, no PR contract); use `skipPr: true` when the task is write-side mutation but the writes don't land in a git repo. Example: `-d '{"title":"Clean up duplicate skills in ~/.claude/skills","type":"implement","description":"Audit ~/.claude/skills/ and delete the 3 obsolete entries identified in NOTE-mp7gt4iw0004b879. Pure user-machine state outside any git repo, so no PR will be produced.","skipPr":true}'`.
152
152
  - **`oneShot: true`** — opt-in flag for one-off human-initiated dispatches that should NOT enroll the discovered PR into the engine's automatic review/fix loop. The PR is still tracked (status + comments are polled normally) but `discoverFromPrs` skips it for review/fix dispatch. **Set this when the user's intent is "do this single action against an existing PR, then stop"** — e.g. "review PR #2533 once", "rebase PR #2540 once and exit", "post a fix-summary comment on PR #2519". Don't set it for normal feature/fix work where the PR should keep cycling through review/fix until merged. Example: `-d '{"title":"One-off review of PR #2533","type":"review","project":"minions","description":"Single review pass on github:yemi33/minions#2533. Do not re-dispatch on subsequent comments.","oneShot":true}'`.