@yemi33/minions 0.1.2363 → 0.1.2365
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/command-center.js +3 -0
- package/dashboard.js +50 -24
- package/docs/README.md +19 -7
- package/docs/architecture-review-2026-07-09.md +94 -0
- package/docs/command-center.md +4 -2
- package/docs/constants.md +1 -1
- package/docs/documentation-audit-2026-07-09.md +43 -0
- package/docs/engine-restart.md +2 -2
- package/docs/managed-spawn.md +1 -1
- package/docs/onboarding.md +76 -198
- package/docs/preflight.md +4 -3
- package/docs/tutorials/01-install-and-connect.md +87 -0
- package/docs/tutorials/02-first-task.md +73 -0
- package/docs/tutorials/03-plan-driven-feature.md +73 -0
- package/docs/tutorials/04-runtimes-and-harness.md +77 -0
- package/docs/tutorials/05-schedules-and-watches.md +71 -0
- package/docs/tutorials/06-managed-services.md +85 -0
- package/docs/tutorials/07-cross-repo-plan.md +56 -0
- package/docs/tutorials/08-operations-and-recovery.md +76 -0
- package/docs/tutorials/README.md +29 -0
- package/docs/watches.md +6 -6
- package/engine/lifecycle.js +27 -0
- package/engine/playbook.js +5 -5
- package/engine/process-utils.js +18 -7
- package/engine/queries.js +8 -4
- package/engine/shared.js +23 -11
- package/engine/worktree-gc.js +2 -2
- package/engine.js +8 -3
- package/package.json +1 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Tutorial 5: Automate Recurring Work and Follow-Ups
|
|
2
|
+
|
|
3
|
+
Schedules create work at a time; watches react to state changes. You will create
|
|
4
|
+
one of each and verify them without waiting for the clock.
|
|
5
|
+
|
|
6
|
+
## 1. Explore the live schemas
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
minions schedule --help
|
|
10
|
+
minions watch --help
|
|
11
|
+
minions watch target-types
|
|
12
|
+
minions watch action-types
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Use the dashboard forms for your first records. They validate against the same
|
|
16
|
+
server-side schemas as the CLI JSON commands.
|
|
17
|
+
|
|
18
|
+
## 2. Create a schedule
|
|
19
|
+
|
|
20
|
+
Open **Schedule**, create a disabled schedule named `weekly-doc-check`, select
|
|
21
|
+
your sample project, and use a task such as:
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
Review README.md for commands that no longer match the CLI. Correct only
|
|
25
|
+
verified stale claims and report the source lines used.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Use the natural-language parser or cron builder to choose a weekly time. Save
|
|
29
|
+
the schedule disabled, then use **Run now** to test it.
|
|
30
|
+
|
|
31
|
+
From the CLI:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
minions schedule list
|
|
35
|
+
minions schedule run-now <schedule-id>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Confirm that a work item appears before enabling the recurring schedule.
|
|
39
|
+
|
|
40
|
+
## 3. Create a watch
|
|
41
|
+
|
|
42
|
+
Open **Watches** and create a watch for the test work item:
|
|
43
|
+
|
|
44
|
+
- target type: `work-item`
|
|
45
|
+
- target: the work-item ID
|
|
46
|
+
- condition: `completed`
|
|
47
|
+
- owner: `human`
|
|
48
|
+
- stop after: `0`
|
|
49
|
+
|
|
50
|
+
For an absolute condition such as `completed`, `stopAfter: 0` means fire once
|
|
51
|
+
and expire. For change conditions, zero means continue indefinitely.
|
|
52
|
+
|
|
53
|
+
Choose the default inbox notification first. Follow-up actions can dispatch
|
|
54
|
+
work, call a webhook, trigger a pipeline, run a skill, or invoke CC triage, but
|
|
55
|
+
they should be added only after the condition itself is proven.
|
|
56
|
+
|
|
57
|
+
## 4. Observe the trigger
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
minions watch list
|
|
61
|
+
minions watch show <watch-id>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Watches are evaluated on their polling cadence, not every engine tick. After
|
|
65
|
+
the work item completes, confirm the watch records a trigger and an inbox note.
|
|
66
|
+
|
|
67
|
+
## Checkpoint
|
|
68
|
+
|
|
69
|
+
You have tested time-based creation and condition-based reaction independently.
|
|
70
|
+
See [Watches](../watches.md) for predicates, plugins, templating, guards, and
|
|
71
|
+
action chains.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Tutorial 6: Keep a Development Service Running
|
|
2
|
+
|
|
3
|
+
Use managed spawn when later agents need a dev server, emulator, or daemon to
|
|
4
|
+
survive the creating agent's exit. The engine owns the process, checks health,
|
|
5
|
+
and exposes it to later dispatches.
|
|
6
|
+
|
|
7
|
+
## 1. Prove the service manually
|
|
8
|
+
|
|
9
|
+
In the worktree that will host the service:
|
|
10
|
+
|
|
11
|
+
1. Install its existing dependencies.
|
|
12
|
+
2. Run the real start command for at least five seconds.
|
|
13
|
+
3. Probe the intended health endpoint and verify the expected status.
|
|
14
|
+
4. Stop the test process.
|
|
15
|
+
|
|
16
|
+
Do not derive a managed-spawn spec from package names or guessed monorepo
|
|
17
|
+
filters. A command that exits immediately will not become reliable because the
|
|
18
|
+
engine starts it.
|
|
19
|
+
|
|
20
|
+
## 2. Dispatch a managed-spawn work item
|
|
21
|
+
|
|
22
|
+
Create a project-scoped work item through the API so the opt-in metadata is
|
|
23
|
+
explicit. Save a temporary `managed-work-item.json`:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"title": "Start the sample development service",
|
|
28
|
+
"description": "Start the existing development service and leave it available for downstream validation.",
|
|
29
|
+
"project": "SampleProject",
|
|
30
|
+
"type": "implement",
|
|
31
|
+
"meta": {
|
|
32
|
+
"managed_spawn": true,
|
|
33
|
+
"managed_spawn_ttl_minutes": 120
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Submit it, then delete the temporary file:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
minions api POST /api/work-items --body-file managed-work-item.json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
The rendered playbook tells the agent where to write
|
|
45
|
+
`agents/<id>/managed-spawn.json` and includes the current executable allowlist
|
|
46
|
+
and schema. The declared `cwd` must be an absolute path that exists when the
|
|
47
|
+
agent exits; the agent should derive it from its current worktree.
|
|
48
|
+
|
|
49
|
+
## 3. Require a healthcheck
|
|
50
|
+
|
|
51
|
+
For a web service, use an HTTP healthcheck:
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"type": "http",
|
|
56
|
+
"url": "http://localhost:3000/health",
|
|
57
|
+
"expect_status": 200,
|
|
58
|
+
"interval_s": 1,
|
|
59
|
+
"timeout_s": 60
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
For an emulator or non-HTTP daemon, use a command healthcheck that exits zero
|
|
64
|
+
and matches a stable readiness signal.
|
|
65
|
+
|
|
66
|
+
## 4. Inspect and use the service
|
|
67
|
+
|
|
68
|
+
Open **Engine** and find **Managed Processes**, or query:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
minions api GET /api/managed-processes
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Confirm the process is healthy, its log path is populated, and its expiration
|
|
75
|
+
time is correct. Dispatch a second work item in the same project and verify
|
|
76
|
+
that the live managed-process context is included in its prompt.
|
|
77
|
+
|
|
78
|
+
Use `meta.keep_processes` instead when the agent itself must retain an ad-hoc
|
|
79
|
+
child with no health endpoint. Prefer managed spawn for named services.
|
|
80
|
+
|
|
81
|
+
## Checkpoint
|
|
82
|
+
|
|
83
|
+
The service remains healthy after the creating agent exits and is visible to a
|
|
84
|
+
later dispatch. Read [Managed Spawn](../managed-spawn.md) for the full sidecar,
|
|
85
|
+
allowlist, restart, log-stream, and partial-failure contracts.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Tutorial 7: Coordinate a Cross-Repository Change
|
|
2
|
+
|
|
3
|
+
Cross-repository plans keep one product outcome together while materializing
|
|
4
|
+
each item into the project that owns its code.
|
|
5
|
+
|
|
6
|
+
## 1. Link and name both projects
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
minions add C:\code\sample-api
|
|
10
|
+
minions add C:\code\sample-web
|
|
11
|
+
minions list
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Give each project a distinct responsibility in its description. Cross-repo
|
|
15
|
+
routing is much easier to review when `sample-api` and `sample-web` do not both
|
|
16
|
+
say only "application code."
|
|
17
|
+
|
|
18
|
+
## 2. Request a cross-repo plan
|
|
19
|
+
|
|
20
|
+
In Command Center:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
/plan Add an API capability in sample-api and consume it from sample-web.
|
|
24
|
+
Create separately testable items, assign every item to one project, and make
|
|
25
|
+
the web integration depend on the API contract item.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The plan must declare both target projects, and every PRD item must have a valid
|
|
29
|
+
`project` value matching a configured project name.
|
|
30
|
+
|
|
31
|
+
## 3. Review the dependency boundary
|
|
32
|
+
|
|
33
|
+
Before approval, confirm:
|
|
34
|
+
|
|
35
|
+
- shared contracts are explicit acceptance criteria;
|
|
36
|
+
- each item edits only one repository;
|
|
37
|
+
- cross-repo `depends_on` records sequencing intent;
|
|
38
|
+
- each repository has its own verification responsibility.
|
|
39
|
+
|
|
40
|
+
Cross-repo dependencies are advisory for merge coordination. They do not make
|
|
41
|
+
separate repositories share a Git history or automatically merge one repo's
|
|
42
|
+
branch into another.
|
|
43
|
+
|
|
44
|
+
## 4. Approve and observe fan-out
|
|
45
|
+
|
|
46
|
+
Approval materializes items into each project's work-item store. Minions creates
|
|
47
|
+
one verify work item per touched project after implementation completes.
|
|
48
|
+
Inspect both project filters in **Work** and **Plans** rather than assuming the
|
|
49
|
+
central plan card shows every repository-specific detail.
|
|
50
|
+
|
|
51
|
+
## Checkpoint
|
|
52
|
+
|
|
53
|
+
Both projects have correctly scoped work items and independent verification
|
|
54
|
+
results, while the source plan remains one reviewable artifact.
|
|
55
|
+
|
|
56
|
+
Reference: [Cross-Repo Plans](../cross-repo-plans.md).
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Tutorial 8: Operate and Recover Minions
|
|
2
|
+
|
|
3
|
+
This tutorial builds a repeatable triage order for engine, dispatch, runtime,
|
|
4
|
+
and worktree failures.
|
|
5
|
+
|
|
6
|
+
## 1. Start with supported summaries
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
minions status
|
|
10
|
+
minions queue
|
|
11
|
+
minions sources
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Use the dashboard **Engine** page for dispatch logs, metrics, managed processes,
|
|
15
|
+
and worktree state. Read JSON sidecars only as diagnostics; migrated runtime
|
|
16
|
+
state is canonical in `engine/state.db`.
|
|
17
|
+
|
|
18
|
+
## 2. Classify before acting
|
|
19
|
+
|
|
20
|
+
For a pending item, inspect `_pendingReason`. For a failed item, inspect its
|
|
21
|
+
failure class and final output. Correct non-retryable causes such as
|
|
22
|
+
authentication, invalid manifests, bad managed-spawn schemas, or dirty live
|
|
23
|
+
checkouts before retrying.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
minions wi show <work-item-id>
|
|
27
|
+
minions wi retry <work-item-id>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## 3. Restart safely
|
|
31
|
+
|
|
32
|
+
`minions start` is idempotent. Use `minions restart` after changing engine
|
|
33
|
+
configuration or playbooks:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
minions restart
|
|
37
|
+
minions status
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Agents are independent child processes. A restart loses in-memory process
|
|
41
|
+
handles, so Minions uses persisted dispatch state, PID data, live-output
|
|
42
|
+
activity, and a restart grace period to recover or classify orphans. Do not
|
|
43
|
+
delete worktrees or dispatch rows to force recovery.
|
|
44
|
+
|
|
45
|
+
## 4. Use targeted recovery commands
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
minions dispatch # wake the daemon; does not run a second tick loop
|
|
49
|
+
minions cleanup # run the supported cleanup path
|
|
50
|
+
minions kill # kill active agents and reset dispatches to pending
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Use `kill` only when agents are genuinely wedged. Quiet output is not enough:
|
|
54
|
+
tracked agents are allowed to be silent during long builds and tests.
|
|
55
|
+
|
|
56
|
+
## 5. Protect against process-level outages
|
|
57
|
+
|
|
58
|
+
For unattended installations, inspect and optionally install the external
|
|
59
|
+
watchdog:
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
minions watchdog status
|
|
63
|
+
minions watchdog install --interval=5
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The watchdog complements the in-process supervisor by probing and recovering
|
|
67
|
+
the engine through the operating system scheduler.
|
|
68
|
+
|
|
69
|
+
## Checkpoint
|
|
70
|
+
|
|
71
|
+
You can distinguish pending, failed, orphaned, and stopped states and can choose
|
|
72
|
+
the least destructive supported action for each.
|
|
73
|
+
|
|
74
|
+
References: [Engine Restart](../engine-restart.md),
|
|
75
|
+
[Timeouts and Liveness](../timeouts-and-liveness.md), and
|
|
76
|
+
[Worktree Lifecycle](../worktree-lifecycle.md).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Minions Tutorial Track
|
|
2
|
+
|
|
3
|
+
These tutorials build on one another. Complete the first two before jumping to
|
|
4
|
+
an advanced topic. Each tutorial ends with an observable result rather than
|
|
5
|
+
only describing configuration.
|
|
6
|
+
|
|
7
|
+
| Tutorial | Outcome | Time |
|
|
8
|
+
|---|---|---:|
|
|
9
|
+
| [1. Install and connect a project](01-install-and-connect.md) | A healthy engine, dashboard, and linked repository | 15 min |
|
|
10
|
+
| [2. Ship your first task](02-first-task.md) | One scoped work item dispatched and tracked to completion | 15 min |
|
|
11
|
+
| [3. Build a feature from a plan](03-plan-driven-feature.md) | An approved PRD with dependency-aware implementation and verification | 25 min |
|
|
12
|
+
| [4. Choose runtimes and propagate project tools](04-runtimes-and-harness.md) | A configured fleet with verified skills, commands, and MCP visibility | 20 min |
|
|
13
|
+
| [5. Automate recurring work and follow-ups](05-schedules-and-watches.md) | A schedule and a condition-based watch | 25 min |
|
|
14
|
+
| [6. Keep a development service running](06-managed-services.md) | An engine-owned, health-checked development service | 25 min |
|
|
15
|
+
| [7. Coordinate a cross-repository change](07-cross-repo-plan.md) | One plan fanned out safely across multiple projects | 30 min |
|
|
16
|
+
| [8. Operate and recover Minions](08-operations-and-recovery.md) | A practical status, restart, retry, and diagnostics workflow | 20 min |
|
|
17
|
+
|
|
18
|
+
## How to use the tutorials
|
|
19
|
+
|
|
20
|
+
- Use a disposable repository for the first task if you do not want an agent to
|
|
21
|
+
open a real pull request.
|
|
22
|
+
- Keep the dashboard open while following CLI steps. It is the easiest place to
|
|
23
|
+
see routing, pending reasons, live output, and artifacts together.
|
|
24
|
+
- Treat JSON files under `engine/` and `projects/` as diagnostic mirrors.
|
|
25
|
+
`engine/state.db` is the primary store for migrated runtime state.
|
|
26
|
+
- Use the linked reference docs when you need the full schema or lifecycle.
|
|
27
|
+
Tutorials intentionally focus on a successful path.
|
|
28
|
+
|
|
29
|
+
For a shorter setup-only path, use [the first-30-minutes guide](../onboarding.md).
|
package/docs/watches.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Watches
|
|
2
2
|
|
|
3
|
-
Persistent monitoring jobs that fire inbox notifications and follow-up actions when a target hits a condition. Watches survive engine restarts and are checked every
|
|
3
|
+
Persistent monitoring jobs that fire inbox notifications and follow-up actions when a target hits a condition. Watches survive engine restarts and are checked every 18 ticks (~3 minutes at the default 10-second tick).
|
|
4
4
|
|
|
5
5
|
## What a Watch Is
|
|
6
6
|
|
|
7
|
-
A watch is a
|
|
7
|
+
A watch is a record persisted in `engine/state.db`; `engine/watches.json` is a passive diagnostic mirror. It binds:
|
|
8
8
|
|
|
9
9
|
| Field | Purpose |
|
|
10
10
|
|--------------|-------------------------------------------------------------------------|
|
|
@@ -20,7 +20,7 @@ A watch is a small JSON record persisted to `engine/watches.json`. It binds:
|
|
|
20
20
|
| `requires` | Optional guard: array of predicate objects evaluated against `state` / `entity` / `prevState`; trigger is suppressed when any guard fails (false-or-error). Used to gate a watch on "PR is mergeable AND build passing" etc. |
|
|
21
21
|
| `status` | `WATCH_STATUS.ACTIVE` \| `PAUSED` \| `TRIGGERED` \| `EXPIRED` |
|
|
22
22
|
|
|
23
|
-
`createWatch()` allocates a `watch-<uid>` id, defaults the fields above, and persists
|
|
23
|
+
`createWatch()` allocates a `watch-<uid>` id, defaults the fields above, and persists through the watch store *(source: `engine/watches.js` and `engine/watches-store.js`)*.
|
|
24
24
|
|
|
25
25
|
## Lifecycle (`WATCH_STATUS`)
|
|
26
26
|
|
|
@@ -94,7 +94,7 @@ The eight built-ins are registered at module load *(source: `engine/watches.js`
|
|
|
94
94
|
|
|
95
95
|
| `targetType` | Target value | Conditions | Notes |
|
|
96
96
|
|---------------|--------------------------------------|----------------------------------------------------------------------------|-------|
|
|
97
|
-
| `pr` | PR number, canonical id, or display id | `merged`, `build-fail`, `build-pass`, `status-change`, `any`, `new-comments`, `vote-change`, `head-commit-change`, `mergeable-flipped`, `ready-for-merge`, `behind-master`, `draft-flipped` | Reads
|
|
97
|
+
| `pr` | PR number, canonical id, or display id | `merged`, `build-fail`, `build-pass`, `status-change`, `any`, `new-comments`, `vote-change`, `head-commit-change`, `mergeable-flipped`, `ready-for-merge`, `behind-master`, `draft-flipped` | Reads project PR state; `new-comments` watches `humanFeedback.lastProcessedCommentDate`; `behind-master` requires `engine.watchesIncludeBehindBy: true` so the GH poller populates `pr.behindBy` |
|
|
98
98
|
| `work-item` | Work item id | `completed`, `failed`, `status-change`, `any`, `stalled`, `retry-limit-reached`, `dependency-met` | `completed` matches `DONE_STATUSES`; `failed` matches `WI_STATUS.FAILED`; `stalled` fires after N unchanged captures (default 12 ≈ 60 min); `retry-limit-reached` fires when `_retryCount >= ENGINE_DEFAULTS.maxRetries` |
|
|
99
99
|
| `meeting` | Meeting id | `concluded`, `status-change`, `any` | `concluded` fires on terminal status (`completed`, `archived`) |
|
|
100
100
|
| `plan` | PRD JSON filename or plan id | `approved`, `rejected`, `completed`, `status-change`, `any`, `all-items-done`, `item-failed-n-times` | Looked up by `_source` (PRD file), `_sourcePlan` (.md), or `id`; uses `PLAN_STATUS` |
|
|
@@ -145,7 +145,7 @@ Resolution is `path.join(shared.MINIONS_DIR, 'watches.d')` so it works in both d
|
|
|
145
145
|
}
|
|
146
146
|
```
|
|
147
147
|
|
|
148
|
-
`checkWatches` walks every active watch and
|
|
148
|
+
`checkWatches` walks every active watch and persists updates through the watch store:
|
|
149
149
|
|
|
150
150
|
1. Skips paused/expired watches and any watch checked within its `interval`.
|
|
151
151
|
2. Captures a baseline `_lastState` on first check (so change conditions have something to diff).
|
|
@@ -166,7 +166,7 @@ I/O happens **outside the lock**: notifications via `writeToInbox`, follow-up ac
|
|
|
166
166
|
| Action type | What it does |
|
|
167
167
|
|------------------------|-----------------------------------------------------------------------------------------------|
|
|
168
168
|
| `notify` | Explicit inbox write; lets you customize `owner`/`body` instead of the default trigger string |
|
|
169
|
-
| `dispatch-work-item` |
|
|
169
|
+
| `dispatch-work-item` | Add a new project or central work item with `createdBy: "watch:<id>"` |
|
|
170
170
|
| `run-skill` | Wrapper around `dispatch-work-item` that asks the agent to run a specific `.claude` skill |
|
|
171
171
|
| `webhook` | `http`/`https` request to an arbitrary URL (10s safety timeout, JSON or string body) |
|
|
172
172
|
| `minions-api` | Loopback call to the in-process dashboard at `http://127.0.0.1:${shared.readDashboardPortFile(MINIONS_DIR)?.port \|\| 7331}` (reads the bound-port beacon file, not the `MINIONS_PORT` env, so it still resolves after an `EADDRINUSE` port scan) — `endpoint` must start with `/api/`; sets `X-Minions-Internal: 1`; returns `{ok, status, summary, response}` for chain-step templating |
|
package/engine/lifecycle.js
CHANGED
|
@@ -6874,6 +6874,15 @@ function collapseAllDuplicatePrRecords(config) {
|
|
|
6874
6874
|
// a coding WI (not one of the validation types itself). Skips if the coding WI
|
|
6875
6875
|
// has no PR.
|
|
6876
6876
|
//
|
|
6877
|
+
// W-mrdq22j700056c6c — gated to real code-authoring completions only. Only
|
|
6878
|
+
// item.type in {implement, implement:large, fix} ever spawns a follow-up
|
|
6879
|
+
// validation WI. Every other work-item type (review, test, verify, setup,
|
|
6880
|
+
// explore, ask, docs, decompose, ...) is excluded even when it resolves a PR
|
|
6881
|
+
// ref and autoDispatch is on, because those completions don't author a code
|
|
6882
|
+
// diff worth re-validating — e.g. a `review` dispatch that merely comments on
|
|
6883
|
+
// someone else's PR previously spawned a spurious "Validate: Review opg PR
|
|
6884
|
+
// #792 ..." follow-up. See the ELIGIBLE_TYPES check below.
|
|
6885
|
+
//
|
|
6877
6886
|
// Files exactly ONE validation WI per coding-WI completion (W-mrhybval0001).
|
|
6878
6887
|
// `liveValidation.type` still governs CHECKOUT ROUTING — the set of work-item
|
|
6879
6888
|
// types that run in-place on the live checkout (see resolveCheckoutMode's
|
|
@@ -6893,6 +6902,10 @@ function collapseAllDuplicatePrRecords(config) {
|
|
|
6893
6902
|
// own type is excluded so a validation-type WI can't recursively validate
|
|
6894
6903
|
// itself (anti-loop).
|
|
6895
6904
|
//
|
|
6905
|
+
// Only these item.type values ever trigger this hook (see ELIGIBLE_TYPES
|
|
6906
|
+
// below) — real code-authoring completions, not review/test/verify/setup/
|
|
6907
|
+
// explore/ask/docs/decompose completions.
|
|
6908
|
+
//
|
|
6896
6909
|
// Deduplicates per codingWiId: any non-terminal WI with
|
|
6897
6910
|
// meta.liveValidationFor === codingWiId blocks a second dispatch, so only one
|
|
6898
6911
|
// validation task is ever in flight per coding completion.
|
|
@@ -6907,6 +6920,14 @@ function collapseAllDuplicatePrRecords(config) {
|
|
|
6907
6920
|
// Validate: ..."), repeating indefinitely. Any WI that was itself created
|
|
6908
6921
|
// as a liveValidation follow-up (tagged via meta.liveValidationFor at
|
|
6909
6922
|
// creation, below) must never re-trigger this hook, regardless of its type.
|
|
6923
|
+
//
|
|
6924
|
+
// W-mrdq22j700056c6c — only real code-authoring work-item types ever spawn a
|
|
6925
|
+
// live-validation follow-up. Every other completed type (review, test,
|
|
6926
|
+
// verify, setup, explore, ask, docs, decompose, ...) is excluded even when it
|
|
6927
|
+
// resolves a PR ref, because those completions don't author a code diff
|
|
6928
|
+
// worth re-validating.
|
|
6929
|
+
const ELIGIBLE_LIVE_VALIDATION_TYPES = new Set([WORK_TYPE.IMPLEMENT, WORK_TYPE.IMPLEMENT_LARGE, WORK_TYPE.FIX]);
|
|
6930
|
+
|
|
6910
6931
|
function autoDispatchLiveValidationWi(meta, config) {
|
|
6911
6932
|
const item = meta?.item;
|
|
6912
6933
|
if (!item?.id || !item?.type) return;
|
|
@@ -6916,6 +6937,12 @@ function autoDispatchLiveValidationWi(meta, config) {
|
|
|
6916
6937
|
// "Validate: Validate: ..." cascade (W-mr9u39az000db5c2 / issue #708).
|
|
6917
6938
|
if (item.meta && item.meta.liveValidationFor) return;
|
|
6918
6939
|
|
|
6940
|
+
// W-mrdq22j700056c6c — only implement/implement:large/fix completions ever
|
|
6941
|
+
// author a real code diff worth validating. Review, test, verify, setup,
|
|
6942
|
+
// explore, ask, docs, decompose, etc. completions must never spawn a
|
|
6943
|
+
// "Validate: ..." follow-up, even when they resolve a PR reference.
|
|
6944
|
+
if (!ELIGIBLE_LIVE_VALIDATION_TYPES.has(item.type)) return;
|
|
6945
|
+
|
|
6919
6946
|
// Issue #716 — QA Session infrastructure WIs (SETUP/DRAFT/EXECUTE, tagged
|
|
6920
6947
|
// via meta.sessionId and always dispatched with skipPr:true) never own a
|
|
6921
6948
|
// real code diff, so there is nothing for a live-validation WI to validate.
|
package/engine/playbook.js
CHANGED
|
@@ -1060,12 +1060,12 @@ function renderPlaybook(type, vars) {
|
|
|
1060
1060
|
// ─── Playbook Section Validator ──────────────────────────────────────────────
|
|
1061
1061
|
|
|
1062
1062
|
// Required structural section patterns — warn (do not throw) when absent.
|
|
1063
|
-
//
|
|
1064
|
-
//
|
|
1065
|
-
//
|
|
1066
|
-
//
|
|
1063
|
+
// Playbooks use a few established names for the task-bearing section; accept
|
|
1064
|
+
// all of them rather than generating warn-level noise for valid templates.
|
|
1065
|
+
// '## Tools' / '## Constraints' were removed (#775) because no template
|
|
1066
|
+
// consistently implements those headers.
|
|
1067
1067
|
const _REQUIRED_PROMPT_SECTIONS = [
|
|
1068
|
-
{ pattern: /^## Your Task\b/m,
|
|
1068
|
+
{ pattern: /^## (?:Your Task|Task Description|Mission)\b/m, label: 'task section' },
|
|
1069
1069
|
];
|
|
1070
1070
|
|
|
1071
1071
|
/**
|
package/engine/process-utils.js
CHANGED
|
@@ -566,9 +566,9 @@ function findProcessCwdHolders(worktreePath, opts = {}) {
|
|
|
566
566
|
return _parseCwdHolderLines(raw, resolved);
|
|
567
567
|
}
|
|
568
568
|
|
|
569
|
-
// Cross-check a single PID's command line for a Minions agent invocation
|
|
570
|
-
//
|
|
571
|
-
//
|
|
569
|
+
// Cross-check a single PID's command line for a Minions agent invocation,
|
|
570
|
+
// including the shared spawn wrapper and every bundled runtime CLI. Used by
|
|
571
|
+
// orphan/recycled-PID safety:
|
|
572
572
|
// - engine/cleanup.js: gate before killing a PID found in engine/tmp/pid-*.pid
|
|
573
573
|
// - engine/timeout.js: gate before parking a dispatch as still-alive when
|
|
574
574
|
// the OS PID is alive but may belong to an unrelated recycled-PID process
|
|
@@ -578,9 +578,20 @@ function findProcessCwdHolders(worktreePath, opts = {}) {
|
|
|
578
578
|
// macOS / when /proc isn't available: fallback `ps -p <pid> -o command=`.
|
|
579
579
|
//
|
|
580
580
|
// Returns false when the PID is invalid, the process doesn't exist, the
|
|
581
|
-
// command line can't be read, or the cmdline contains
|
|
582
|
-
//
|
|
581
|
+
// command line can't be read, or the cmdline contains no recognized agent
|
|
582
|
+
// marker. False is the safe default for both call sites: cleanup falls
|
|
583
583
|
// through to "skip kill" and timeout falls through to "treat PID as dead".
|
|
584
|
+
const AGENT_COMMAND_PATTERNS = [
|
|
585
|
+
/(?:^|[\s"'\\/])(?:claude|copilot|codex)(?:\.exe|\.cmd|\.bat|\.js)?(?=$|[\s"])/i,
|
|
586
|
+
/[\\/]claude-code[\\/]cli\.js(?=$|[\s"])/i,
|
|
587
|
+
/[\\/]@github[\\/](?:copilot|copilot-cli)[\\/](?:(?:dist|bin)[\\/])?index\.js(?=$|[\s"])/i,
|
|
588
|
+
];
|
|
589
|
+
|
|
590
|
+
function _commandLineMatchesAgent(cmdline) {
|
|
591
|
+
const value = String(cmdline || '');
|
|
592
|
+
return AGENT_COMMAND_PATTERNS.some(pattern => pattern.test(value));
|
|
593
|
+
}
|
|
594
|
+
|
|
584
595
|
function isProcessCommandLineMatchingAgent(pid) {
|
|
585
596
|
const n = Number(pid);
|
|
586
597
|
if (!Number.isInteger(n) || n <= 0) return false;
|
|
@@ -605,8 +616,7 @@ function isProcessCommandLineMatchingAgent(pid) {
|
|
|
605
616
|
}
|
|
606
617
|
} catch { return false; }
|
|
607
618
|
if (!cmdline) return false;
|
|
608
|
-
|
|
609
|
-
return lower.includes('claude') || lower.includes('copilot');
|
|
619
|
+
return _commandLineMatchesAgent(cmdline);
|
|
610
620
|
}
|
|
611
621
|
|
|
612
622
|
function _buildChildMap(processes) {
|
|
@@ -693,6 +703,7 @@ module.exports = {
|
|
|
693
703
|
_parseCwdHolderLines,
|
|
694
704
|
findProcessCwdHolders,
|
|
695
705
|
isProcessCommandLineMatchingAgent,
|
|
706
|
+
_commandLineMatchesAgent, // exported for testing
|
|
696
707
|
_buildChildMap,
|
|
697
708
|
listProcessDescendants,
|
|
698
709
|
listProcessReachable,
|
package/engine/queries.js
CHANGED
|
@@ -3283,10 +3283,6 @@ function getStatusFastStateMtimePaths(config) {
|
|
|
3283
3283
|
* Claude CLI mutations (mcp add/remove, settings edits), not on every
|
|
3284
3284
|
* prompt, so whole-file tracking is acceptable.
|
|
3285
3285
|
*
|
|
3286
|
-
* Files intentionally NOT tracked here:
|
|
3287
|
-
* - version, autoMode, installId — change only on human/CLI edits, which
|
|
3288
|
-
* already pop the slow-state via reloadConfig + the 60 s TTL.
|
|
3289
|
-
*
|
|
3290
3286
|
* Per-project `.git/logs/HEAD` + `.git/FETCH_HEAD` ARE tracked here in
|
|
3291
3287
|
* addition to the fast-state tracker. The project payload (`projects:`
|
|
3292
3288
|
* with ahead/behind counts) lives in the slow-state slice, and
|
|
@@ -3327,6 +3323,14 @@ function getStatusSlowStateMtimePaths(config) {
|
|
|
3327
3323
|
// in the payload anymore.
|
|
3328
3324
|
const files = [];
|
|
3329
3325
|
|
|
3326
|
+
// Config-derived status fields (projects, autoMode, version settings) can be
|
|
3327
|
+
// changed by the CLI or another process without calling dashboard-local
|
|
3328
|
+
// invalidateStatusCache(). The unified cache has no TTL fallback, so these
|
|
3329
|
+
// source files must participate in the mtime registry.
|
|
3330
|
+
files.push(CONFIG_PATH);
|
|
3331
|
+
files.push(path.join(MINIONS_DIR, '.install-id'));
|
|
3332
|
+
files.push(path.join(MINIONS_DIR, 'package.json'));
|
|
3333
|
+
|
|
3330
3334
|
// Skill discovery roots (surfaced by _buildStatusSlowState → getSkills).
|
|
3331
3335
|
// Mirrors collectSkillFiles' source enumeration so adding a new runtime
|
|
3332
3336
|
// adapter automatically extends the tracker. ENOENT is tolerated by
|
package/engine/shared.js
CHANGED
|
@@ -6327,16 +6327,18 @@ function resolveAgentTempBaseDir(project, engine, minionsDir) {
|
|
|
6327
6327
|
|
|
6328
6328
|
// Seed the agent COPILOT_HOME's mcp-config (copy of `~/.copilot/mcp-config.json`
|
|
6329
6329
|
// MINUS `engine.copilotAgentDisabledMcpServers`) and return the home path. This
|
|
6330
|
-
// is the
|
|
6331
|
-
//
|
|
6332
|
-
// (
|
|
6333
|
-
//
|
|
6334
|
-
//
|
|
6335
|
-
// SPAWNS every server in its config and
|
|
6336
|
-
// only hides tools), so any
|
|
6337
|
-
//
|
|
6338
|
-
//
|
|
6339
|
-
//
|
|
6330
|
+
// is the source of truth for "what MCP servers may an agent-dispatch copilot
|
|
6331
|
+
// load." It is applied to the engine-process copilot spawn paths ONLY — agent
|
|
6332
|
+
// dispatches (`_applyAgentCopilotHome`) and the engine process's own internal
|
|
6333
|
+
// `llm.callLLM` calls — by setting `process.env.COPILOT_HOME` at engine boot
|
|
6334
|
+
// and re-stamping it per dispatch, so those copilot CHILDREN inherit the
|
|
6335
|
+
// filtered config. copilot SPAWNS every server in its config and
|
|
6336
|
+
// authenticates it (`--disable-mcp-server` only hides tools), so any
|
|
6337
|
+
// auth-requiring server pops a Microsoft window per spawn — filtering the
|
|
6338
|
+
// config is the only effective control. Idempotent: writes only when the
|
|
6339
|
+
// content changes (safe under concurrent callers). The operator's real
|
|
6340
|
+
// `~/.copilot` (interactive copilot, and now also Command Center / doc-chat /
|
|
6341
|
+
// the CC worker pool, per W-mre75l6x00024f49) is never touched. Fail-open.
|
|
6340
6342
|
function ensureAgentCopilotHome(minionsDir, engine) {
|
|
6341
6343
|
const home = resolveAgentCopilotHome(minionsDir);
|
|
6342
6344
|
try {
|
|
@@ -7882,7 +7884,17 @@ function upsertPullRequestRecord(prPath, entry, { project = null, itemId = null,
|
|
|
7882
7884
|
...(Array.isArray(itemIds) ? itemIds : [itemId]),
|
|
7883
7885
|
]);
|
|
7884
7886
|
const prNumber = getPrNumber(entry.prNumber ?? entry.id ?? entry.url);
|
|
7885
|
-
|
|
7887
|
+
// Issue #802: when the caller already resolved a scope-qualified canonical
|
|
7888
|
+
// id (e.g. `ado:office/office/office#5370216`, as enrollPrFromCanonicalId
|
|
7889
|
+
// does before calling here), it must win over recomputing from a bare
|
|
7890
|
+
// entry.prNumber + project. Recomputing silently downgrades to a legacy
|
|
7891
|
+
// `PR-<n>` stub whenever project is null/unresolvable (getCanonicalPrId
|
|
7892
|
+
// can't derive a scope), discarding the good id even though it was passed
|
|
7893
|
+
// in. Prefer entry.id whenever it already parses as canonical.
|
|
7894
|
+
const hasCanonicalEntryId = typeof entry.id === 'string' && parseCanonicalPrId(entry.id) != null;
|
|
7895
|
+
const canonicalId = hasCanonicalEntryId
|
|
7896
|
+
? getCanonicalPrId(project, entry.id, entry.url || '')
|
|
7897
|
+
: getCanonicalPrId(project, entry.prNumber ?? entry.id ?? entry.url ?? prNumber, entry.url || '');
|
|
7886
7898
|
if (!canonicalId) throw new Error('PR id required');
|
|
7887
7899
|
const normalizedEntry = {
|
|
7888
7900
|
...entry,
|
package/engine/worktree-gc.js
CHANGED
|
@@ -769,9 +769,9 @@ function pruneOrphanWorktrees(opts) {
|
|
|
769
769
|
const globalLiveDirNames = new Set();
|
|
770
770
|
for (const d of [...(dispatchSnap.active || []), ...(dispatchSnap.pending || [])]) {
|
|
771
771
|
if (!d || !d.id || !d.meta || !d.meta.branch) continue;
|
|
772
|
-
const dispatchProject =
|
|
772
|
+
const dispatchProject = typeof d.meta.project === 'string'
|
|
773
773
|
? d.meta.project
|
|
774
|
-
: 'default';
|
|
774
|
+
: (d.meta.project?.name || 'default');
|
|
775
775
|
try {
|
|
776
776
|
globalLiveDirNames.add(_buildWorktreeDirName({
|
|
777
777
|
dispatchId: d.id,
|
package/engine.js
CHANGED
|
@@ -10141,7 +10141,9 @@ async function discoverWork(config) {
|
|
|
10141
10141
|
const activeMissionIds = new Set();
|
|
10142
10142
|
const activeScheduleIds = new Set();
|
|
10143
10143
|
for (const existing of items) {
|
|
10144
|
-
if (
|
|
10144
|
+
if (DONE_STATUSES.has(existing.status) ||
|
|
10145
|
+
existing.status === WI_STATUS.FAILED ||
|
|
10146
|
+
existing.status === WI_STATUS.CANCELLED) continue;
|
|
10145
10147
|
if (existing._missionId) activeMissionIds.add(existing._missionId);
|
|
10146
10148
|
if (existing._scheduleId) activeScheduleIds.add(existing._scheduleId);
|
|
10147
10149
|
}
|
|
@@ -11809,8 +11811,11 @@ if (require.main === module) {
|
|
|
11809
11811
|
// `llm.callLLM` calls (consolidation, classification, meetings, …) — inherits
|
|
11810
11812
|
// this process's COPILOT_HOME, so point it at the seeded, MCP-filtered home.
|
|
11811
11813
|
// Without this, those `direct:true` copilot calls load the operator's full
|
|
11812
|
-
// `~/.copilot` stack and pop a Microsoft auth window per call.
|
|
11813
|
-
//
|
|
11814
|
+
// `~/.copilot` stack and pop a Microsoft auth window per call. This isolation
|
|
11815
|
+
// is engine-process-only: the dashboard process deliberately does NOT set
|
|
11816
|
+
// COPILOT_HOME at its own boot, so Command Center / doc-chat / the CC worker
|
|
11817
|
+
// pool inherit the operator's real `~/.copilot` home instead (reverted in
|
|
11818
|
+
// W-mre75l6x00024f49; see dashboard.js boot for rationale).
|
|
11814
11819
|
// See shared.ensureAgentCopilotHome. Fail-open.
|
|
11815
11820
|
try { process.env.COPILOT_HOME = shared.ensureAgentCopilotHome(MINIONS_DIR, getConfig()?.engine); } catch {}
|
|
11816
11821
|
const { handleCommand } = require('./engine/cli');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2365",
|
|
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"
|