@yemi33/minions 0.1.702 → 0.1.704

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.704 (2026-04-09)
4
+
5
+ ### Other
6
+ - docs: add PR review & fix loop documentation
7
+ - refactor: move buildFixGracePeriod to ENGINE_DEFAULTS (no magic numbers)
8
+
3
9
  ## 0.1.702 (2026-04-09)
4
10
 
5
11
  ### Fixes
@@ -0,0 +1,110 @@
1
+ # PR Review & Fix Loop
2
+
3
+ How the engine manages the lifecycle of a PR from creation through review, fix, and re-review.
4
+
5
+ ## 1. Implement agent creates PR
6
+
7
+ - Agent pushes code, output contains PR URL
8
+ - `syncPrsFromOutput()` (lifecycle.js) extracts URL via regex, creates `pull-requests.json` entry, links to work item via `addPrLink()`
9
+
10
+ ## 2. Engine discovers PR needs review
11
+
12
+ - `discoverFromPrs()` (engine.js) runs each tick (~60s)
13
+ - Gates: `status === 'active'` + `reviewStatus === 'pending'` + not reviewed since last push + not dispatched + not on cooldown
14
+ - Pre-dispatch: `checkLiveReviewStatus()` hits GitHub/ADO API to catch stale cached status
15
+ - Routes to reviewer via `resolveAgent('review')`, dispatches with `review.md` playbook
16
+
17
+ ## 3. Review completes
18
+
19
+ - `updatePrAfterReview()` (lifecycle.js) re-checks live vote from platform (may have changed during execution)
20
+ - Sets `reviewStatus` to `approved` / `changes-requested` / `waiting`
21
+ - Stores `minionsReview: { reviewer, reviewedAt, note }`
22
+ - Creates feedback file for author agent
23
+
24
+ ## 4. Fix dispatch (3 independent triggers, at most one per tick)
25
+
26
+ ### A. Review feedback (`changes-requested`)
27
+
28
+ - Gate: `reviewStatus === 'changes-requested'` + `!awaitingReReview` + not dispatched + not on cooldown
29
+ - Routes to PR author via `_author_` routing token
30
+ - `review_note` = reviewer's feedback
31
+ - Sets `fixDispatched = true` — prevents trigger B from also firing this tick
32
+
33
+ ### B. Human comments (`humanFeedback.pendingFix`)
34
+
35
+ - Gate: `pendingFix || coalescedFeedback` + `!awaitingReReview` + `!fixDispatched`
36
+ - Agent comments filtered out via `/\bMinions\s*\(/i` regex on comment body
37
+ - Coalesces multiple comments arriving during cooldown into single fix
38
+ - Routes to author
39
+
40
+ ### C. Build failures (`buildStatus === 'failing'`)
41
+
42
+ - Gate: `buildFixAttempts < maxBuildFixAttempts` (default 3) + grace period expired
43
+ - **Grace period** (`_buildFixPushedAt`): after fix dispatches, waits `buildFixGracePeriod` (default 10min, configurable in `ENGINE_DEFAULTS`) for CI to run before re-dispatching. Cleared when poller detects build status transition (CI actually ran).
44
+ - **Error logs**: GitHub fetches annotations (failures only, not warnings) + Actions job log (always). ADO fetches build timeline + failed task logs. Both fetch up to 3 failing pipelines.
45
+ - **Escalation**: after 3 failed attempts, writes inbox alert, sets `buildFixEscalated = true`, stops auto-dispatch. Counter resets when build recovers.
46
+
47
+ ## 5. Fix completes
48
+
49
+ - `updatePrAfterFix()` (lifecycle.js) sets `reviewStatus = 'waiting'` + `fixedAt = ts()`
50
+ - Clears `humanFeedback.pendingFix`
51
+ - `awaitingReReview` gate (`waiting` + `fixedAt`) blocks all fix dispatch until reviewer acts
52
+
53
+ ## 6. Re-review cycle
54
+
55
+ - Poller (~3min): detects new commit (`head.sha` changed) → sets `lastPushedAt`
56
+ - Platform review state drives next action:
57
+ - Reviewer approves → `approved` → done
58
+ - Reviewer re-requests changes → `changes-requested` → triggers another fix
59
+ - No reviewer action yet → stays `waiting` → engine waits
60
+
61
+ ## 7. Build fix cycle after fix push
62
+
63
+ - Fix agent pushes → `_buildFixPushedAt` stamped
64
+ - Poller detects new commit → CI starts → `buildStatus` transitions (`failing` → `running`)
65
+ - `_buildFixPushedAt` cleared on any transition
66
+ - If CI passes → `buildFixAttempts` reset, `buildErrorLog` cleared → done
67
+ - If CI fails again → fresh error logs fetched → new fix dispatches immediately (grace already cleared by transition)
68
+
69
+ ## Race prevention
70
+
71
+ | Scenario | Guard |
72
+ |---|---|
73
+ | Simultaneous review + fix | `activePrIds` — skip PR if any dispatch in-flight |
74
+ | Duplicate fix (review + human) | `fixDispatched` flag — only one fix per PR per tick |
75
+ | Branch write conflict | `isBranchActive()` mutex |
76
+ | Fix while awaiting re-review | `awaitingReReview` (waiting + fixedAt) |
77
+ | Build fix before CI runs | `_buildFixPushedAt` grace period (10min) |
78
+ | Duplicate dispatch | `dispatchKey` dedup + cooldown |
79
+ | Stale review status | Pre-dispatch live API check |
80
+ | Orphan detection | Heartbeat timeout + output scan |
81
+
82
+ ## Key files
83
+
84
+ | File | Functions |
85
+ |---|---|
86
+ | `engine.js` | `discoverFromPrs()` — discovery + dispatch logic |
87
+ | `engine/lifecycle.js` | `syncPrsFromOutput()`, `updatePrAfterReview()`, `updatePrAfterFix()` |
88
+ | `engine/github.js` | `pollPrStatus()`, `pollPrHumanComments()`, `fetchGhBuildErrorLog()` |
89
+ | `engine/ado.js` | `pollPrStatus()`, `pollPrHumanComments()`, `fetchAdoBuildErrorLog()` |
90
+ | `engine/dispatch.js` | `addToDispatch()` — dedup by work item ID and dispatchKey |
91
+ | `engine/cooldown.js` | `isBranchActive()`, cooldown management |
92
+ | `playbooks/review.md` | Reviewer playbook |
93
+ | `playbooks/fix.md` | Fix agent playbook |
94
+
95
+ ## PR state fields
96
+
97
+ | Field | Set by | Purpose |
98
+ |---|---|---|
99
+ | `status` | Poller | `active` / `merged` / `abandoned` |
100
+ | `reviewStatus` | Poller + post-completion | `pending` / `approved` / `changes-requested` / `waiting` |
101
+ | `buildStatus` | Poller | `none` / `passing` / `failing` / `running` |
102
+ | `buildErrorLog` | Poller | Actual CI error output for fix agents |
103
+ | `buildFixAttempts` | Discovery (on dispatch) | Counter for escalation cap |
104
+ | `buildFixEscalated` | Discovery (on cap) | Stops auto-dispatch |
105
+ | `_buildFixPushedAt` | Discovery (on dispatch) | Grace period timestamp |
106
+ | `_buildFailNotified` | Discovery | Dedup for inbox alert |
107
+ | `lastPushedAt` | Poller (new commit) | Tracks latest push for re-review logic |
108
+ | `lastReviewedAt` | `updatePrAfterReview()` | Prevents re-dispatch if reviewed |
109
+ | `minionsReview` | Post-completion hooks | `{ reviewer, reviewedAt, note, fixedAt }` |
110
+ | `humanFeedback` | `pollPrHumanComments()` | `{ pendingFix, feedbackContent, lastProcessedCommentDate }` |
package/engine/shared.js CHANGED
@@ -544,6 +544,7 @@ const ENGINE_DEFAULTS = {
544
544
  lockRetries: 2, // retry lock acquisition this many times after initial timeout (total attempts = 1 + lockRetries)
545
545
  lockRetryBackoffMs: 500, // base backoff between lock retries (doubles each attempt: 500ms, 1s, 2s, ...)
546
546
  maxBuildFixAttempts: 3, // max consecutive auto-fix dispatch cycles per PR before escalation to human
547
+ buildFixGracePeriod: 600000, // 10min — wait for CI to run after build fix before re-dispatching
547
548
  ccModel: 'sonnet', // model for Command Center and doc-chat (sonnet, haiku, opus)
548
549
  ccEffort: null, // effort level for CC/doc-chat (null, 'low', 'medium', 'high')
549
550
  };
package/engine.js CHANGED
@@ -1604,7 +1604,7 @@ async function discoverFromPrs(config, project) {
1604
1604
  // Grace period: after a build fix push, wait for CI to run before re-dispatching
1605
1605
  // Skip if build hasn't transitioned since last fix (still showing the old failure)
1606
1606
  if (pr._buildFixPushedAt && pr.buildStatus === 'failing') {
1607
- const gracePeriodMs = config.engine?.buildFixGracePeriod ?? 600000; // 10 min default
1607
+ const gracePeriodMs = config.engine?.buildFixGracePeriod ?? ENGINE_DEFAULTS.buildFixGracePeriod;
1608
1608
  if (Date.now() - new Date(pr._buildFixPushedAt).getTime() < gracePeriodMs) continue;
1609
1609
  }
1610
1610
  if (pr.status === PR_STATUS.ACTIVE && pr.buildStatus === 'failing') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.702",
3
+ "version": "0.1.704",
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"