@yemi33/minions 0.1.1884 → 0.1.1885

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.
@@ -0,0 +1,116 @@
1
+ # Team Memory
2
+
3
+ Minions agents share knowledge through a layered memory system. Before an agent does any independent research — web searches, broad codebase exploration, external doc reading — it is expected to check what the team already knows.
4
+
5
+ This document describes the four-tier read order the engine enforces in dispatch prompts, the rationale, and the constraint that `knowledge/` is **sweep-write-only**.
6
+
7
+ ## Why team memory exists
8
+
9
+ Multiple agents work the same repository in parallel and across days. Without shared memory:
10
+
11
+ - Agents re-research the same questions (cost, time, context churn).
12
+ - Agents re-litigate decisions the team already made (drift, regressions).
13
+ - Human-flagged warnings (e.g., "do not self-approve PRs") get ignored because they live outside the prompt.
14
+
15
+ Team memory closes the loop. The engine injects pinned context and recent team notes into every dispatch prompt automatically, and the playbooks instruct agents to consult `knowledge/` and `notes/inbox/` on disk before going wider. This keeps work cheap, decisions sticky, and prior learnings reusable.
16
+
17
+ ## The four-tier read order
18
+
19
+ Every agent prompt (built by `engine/playbook.js`) is rendered with the rule: **check team memory first, then look outside.** The canonical read order (from `playbooks/shared-rules.md`) is:
20
+
21
+ 1. **`pinned.md`** — critical context flagged by the human teammate. **READ FIRST.**
22
+ 2. **`knowledge/`** — categorized KB entries written by the consolidation sweep.
23
+ 3. **`notes.md`** — consolidated team knowledge, decisions, and rolling context.
24
+ 4. **`notes/inbox/`** — recent agent findings not yet consolidated.
25
+
26
+ Agents are also encouraged, after the four primary tiers, to skim `agents/*/live-output.log` for related tasks and the `resultSummary` of prior completed work items on the same topic. Only after exhausting team memory should an agent fall back to web search, broad codebase exploration, or external docs.
27
+
28
+ ### 1. `pinned.md` — human-flagged critical context
29
+
30
+ `pinned.md` lives at the Minions root (`MINIONS_DIR/pinned.md`). It holds notes the human teammate has explicitly pinned through the dashboard — short, high-signal rules and warnings that **must** be visible to every agent on every dispatch.
31
+
32
+ The engine injects the file verbatim into the prompt under a "Pinned Context (CRITICAL — READ FIRST)" heading and caps it at 4 KB to protect the prompt budget.
33
+
34
+ Typical contents include credential constraints (e.g., shared `gh` auth means PR self-approval is blocked), workflow policies (e.g., merging policy, TDD requirement), and active setup state. If pinned context contradicts a playbook instruction, pinned context wins for the duration of the dispatch.
35
+
36
+ ### 2. `knowledge/` — categorized KB entries (sweep-write-only)
37
+
38
+ `knowledge/` is the long-form team knowledge base. Files are grouped by category:
39
+
40
+ - `knowledge/architecture/` — design decisions, system overviews
41
+ - `knowledge/conventions/` — coding patterns, repo norms, standards
42
+ - `knowledge/project-notes/` — per-project context and observations
43
+ - `knowledge/build-reports/` — CI/build investigation results
44
+ - `knowledge/reviews/` — PR review findings and rationale
45
+ - `knowledge/agents/<agentId>.md` — per-agent personal memory (curated by the sweep)
46
+
47
+ Filenames follow the `YYYY-MM-DD-<agent>-<slug>.md` convention so chronological sorting just works.
48
+
49
+ Agents read `knowledge/` directly with grep/glob/view. They **do not** write to it. See [Sweep-write-only constraint](#sweep-write-only-constraint) below.
50
+
51
+ ### 3. `notes.md` — consolidated team notes
52
+
53
+ `notes.md` is the rolling team notebook. The consolidation sweep periodically merges high-signal findings from the inbox into dated `### YYYY-MM-DD` sections at the top of `notes.md`, with links back to the underlying KB entries.
54
+
55
+ The engine injects `notes.md` into every prompt under a "Team Notes (MUST READ)" heading. When the file exceeds `ENGINE_DEFAULTS.maxNotesPromptBytes`, the engine keeps the most recent 10 sections, truncates the body, and appends a footer noting how many older sections live on disk so agents can read the full file when they need depth.
56
+
57
+ ### 4. `notes/inbox/` — fresh agent findings
58
+
59
+ `notes/inbox/` holds the freshest, un-consolidated learnings. Every agent writes exactly one inbox file at the end of a successful task to a path the playbook supplies (`notes/inbox/<agent>-<work-item-id>-<date>-<time>.md`). The file always begins with a YAML frontmatter block (`id`, `agent`, `date`) so the consolidation sweep can track it.
60
+
61
+ Inbox notes are the canonical place to record:
62
+
63
+ - New patterns or conventions discovered while implementing.
64
+ - Gotchas, warnings, or repro recipes for future agents.
65
+ - File-and-line citations supporting any claims.
66
+
67
+ The consolidation sweep is what eventually promotes inbox notes into `knowledge/` and `notes.md`. Until then, agents should grep the inbox themselves for very recent findings on adjacent work.
68
+
69
+ #### Failure-path exception
70
+
71
+ Inbox writes are **success artifacts only**. If a task fails, is blocked, is cancelled, or ends partial, the agent must **not** create an inbox note — the failure is reported through the completion JSON, the PR/work-item comment, or the task-specific failure channel instead. This keeps the inbox a clean signal and prevents noise from drowning out durable findings.
72
+
73
+ ## Rationale
74
+
75
+ The read order is deliberately ordered by **trust × recency**:
76
+
77
+ - `pinned.md` is human-curated and authoritative — it overrides everything else.
78
+ - `knowledge/` is curated by the sweep from validated agent findings — high-signal but slightly delayed.
79
+ - `notes.md` mirrors `knowledge/` in summarized form, optimized for prompt injection.
80
+ - `notes/inbox/` is raw, recent, and unverified — useful for the freshest context but treat critically.
81
+
82
+ Following this order achieves two goals:
83
+
84
+ 1. **Avoid re-research.** If another agent already investigated the same module, fix, or design question, the answer is somewhere in this stack. Reading saves a costly round-trip.
85
+ 2. **Respect team decisions.** Pinned policies and consolidated conventions encode prior agreement (often with the human). Skipping team memory and acting on first principles is how regressions and drift happen.
86
+
87
+ ## Sweep-write-only constraint
88
+
89
+ **Agents must never delete, move, or overwrite files in `knowledge/`.** Only the consolidation sweep (`engine/consolidation.js`) writes to `knowledge/`. This rule is restated in every dispatch prompt under a "Knowledge Base Rules" heading.
90
+
91
+ Why:
92
+
93
+ - The sweep classifies inbox notes into the right category, generates the canonical filename, and updates the `notes.md` summary in lockstep with the `knowledge/` write. Manual edits skip those side effects and desync the system.
94
+ - Agents work in parallel. Letting any agent rewrite `knowledge/` entries makes consolidation non-deterministic and makes blame attribution impossible.
95
+ - Knowledge base entries are referenced by date-stamped path. Rewriting an entry breaks every existing link.
96
+
97
+ If an agent thinks a `knowledge/` file is wrong, the correct response is to **note the disagreement in the agent's own inbox file**. The sweep will pick up the note on the next consolidation cycle and either supersede or correct the existing entry.
98
+
99
+ The same constraint applies to `knowledge/agents/<agentId>.md` — those are curated by the sweep and should not be hand-edited.
100
+
101
+ ## Quick reference for agents
102
+
103
+ ```
104
+ 1. pinned.md ← critical, human-flagged. Read first.
105
+ 2. knowledge/ ← categorized KB. Read, never write.
106
+ 3. notes.md ← consolidated team notes. Read.
107
+ 4. notes/inbox/ ← fresh, unconsolidated findings. Read; write one on success.
108
+ ```
109
+
110
+ After the four tiers, optionally consult `agents/*/live-output.log` and prior `resultSummary` fields. Only then go outside.
111
+
112
+ ## See also
113
+
114
+ - `playbooks/shared-rules.md` — the canonical instruction injected into every dispatch.
115
+ - `engine/playbook.js` — where `pinned.md`, `notes.md`, and per-agent memory get spliced into prompts.
116
+ - `engine/consolidation.js` — the sweep that promotes inbox notes into `knowledge/` and `notes.md`.
@@ -1,5 +1,7 @@
1
1
  # Teams Production Endpoint Migration
2
2
 
3
+ > Last verified: 2026-05-12. `botbuilder` dependency confirmed in `package.json` (4.23.3); `/api/bot` route confirmed in `dashboard.js` (`handleTeamsBot`).
4
+
3
5
  Guide for migrating the Minions Teams bot from a Dev Tunnel to a stable public HTTPS endpoint for production use. Choose one of the three deployment options below based on your infrastructure.
4
6
 
5
7
  **Key fact:** The Azure Bot messaging endpoint URL can be changed at any time in the Azure Portal — it takes effect immediately. No bot reinstallation is needed in Teams. This means you can switch between Dev Tunnel and production endpoints freely.
@@ -120,7 +122,7 @@ Containerize the dashboard and deploy to Azure Container Apps with a stable FQDN
120
122
  CMD ["node", "dashboard.js"]
121
123
  ```
122
124
 
123
- > Minions has zero npm dependencies beyond Node.js built-ins, so `npm ci` may be a no-op. The botbuilder package (added by this feature branch) is the exception.
125
+ > Minions ships with `botbuilder` as its only runtime dependency (declared in `package.json`); other operations rely on Node.js built-ins, so `npm ci` is a fast install.
124
126
 
125
127
  2. **Build and push to Azure Container Registry:**
126
128
 
@@ -0,0 +1,201 @@
1
+ # Watches
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 ticks (~3 minutes by default).
4
+
5
+ ## What a Watch Is
6
+
7
+ A watch is a small JSON record persisted to `engine/watches.json`. It binds:
8
+
9
+ | Field | Purpose |
10
+ |--------------|-------------------------------------------------------------------------|
11
+ | `target` | The thing to watch (PR number, work item id, plan filename, ...) |
12
+ | `targetType` | Which registered target type (`pr`, `work-item`, `meeting`, ...) |
13
+ | `condition` | One of the conditions the target type accepts (e.g. `merged`, `failed`) |
14
+ | `interval` | Min ms between checks (default 5min, floor 60s) |
15
+ | `notify` | `'inbox'` writes an inbox alert on trigger (set `'none'` to suppress) |
16
+ | `owner` | Inbox owner that receives notifications (`'human'` by default) |
17
+ | `stopAfter` | `0` = run forever for change conditions / fire-once for absolute; `N` = expire after N triggers |
18
+ | `onNotMet` | `null` or `'notify'` — write a per-poll inbox note when the condition is not yet met |
19
+ | `action` | Optional follow-up action (see "Follow-Up Actions" below) |
20
+ | `status` | `WATCH_STATUS.ACTIVE` \| `PAUSED` \| `TRIGGERED` \| `EXPIRED` |
21
+
22
+ `createWatch()` allocates a `watch-<uid>` id, defaults the fields above, and persists atomically via `mutateJsonFileLocked` *(source: `engine/watches.js:103-145`)*.
23
+
24
+ ## Lifecycle (`WATCH_STATUS`)
25
+
26
+ Defined in `engine/shared.js:1557`:
27
+
28
+ | Status | Meaning |
29
+ |-------------|-------------------------------------------------------------------------|
30
+ | `active` | Eligible for evaluation each tick |
31
+ | `paused` | Skipped by `checkWatches`; persists indefinitely until resumed/deleted |
32
+ | `triggered` | Reserved status (set on demand by callers; not auto-applied) |
33
+ | `expired` | Auto-set when `stopAfter` is reached, or on first trigger for absolute conditions when `stopAfter === 0`. The watch is left on disk for audit but no longer evaluated *(source: `engine/watches.js:305-310`)* |
34
+
35
+ Pause/resume flips the `status` field via `POST /api/watches/update` *(source: `engine/watches.js:153-178`, `dashboard.js:6400-6412`)*.
36
+
37
+ ## Conditions (`WATCH_CONDITION`)
38
+
39
+ Defined in `engine/shared.js:1573-1592`. Conditions split into two families:
40
+
41
+ ### Absolute conditions (`WATCH_ABSOLUTE_CONDITIONS`)
42
+ *(source: `engine/shared.js:1595-1599`)*
43
+
44
+ `merged`, `build-fail`, `build-pass`, `completed`, `failed`, `concluded`, `approved`, `rejected`.
45
+
46
+ When `stopAfter === 0`, these are **fire-once** — the engine flips the watch to `expired` after the first trigger so a permanently-merged PR doesn't keep notifying *(source: `engine/watches.js:305-310`)*.
47
+
48
+ ### Change-based conditions
49
+ `status-change`, `any`, `new-comments`, `vote-change`, `stage-complete`, `ran`, `enabled`, `disabled`, `activity-change`.
50
+
51
+ These compare the live entity against the watch's `_lastState` snapshot and run forever when `stopAfter === 0`. Baseline `_lastState` is captured on the first check so the very next change triggers the watch *(source: `engine/watches.js:262-266`)*.
52
+
53
+ ## Target Types — `TARGET_TYPES` Registry
54
+
55
+ Target-type behavior in `engine/watches.js` is **data-driven via a registry** *(source: `engine/watches.js:50-72`)*. Each spec must provide:
56
+
57
+ - `label` — human name shown in dashboard pickers
58
+ - `description` — short help text
59
+ - `conditions` — non-empty array of accepted condition keys (also acts as the per-type allowlist)
60
+ - `fetchEntity(target, state)` — entity-or-null lookup
61
+ - `captureState(entity)` — snapshot used for change-detection diffs
62
+ - `evaluate(condition, entity, prevState, target)` — returns `{ triggered, message }`
63
+
64
+ The registry IS the allowlist for `createWatch` and `/api/watches/target-types`; the old hardcoded "pr or work-item" check is gone. Add a new target type at runtime with `registerTargetType(type, spec)` and look one up with `getTargetType(type)`. `listTargetTypes()` returns the serializable form used by the dashboard *(source: `engine/watches.js:62-88`)*.
65
+
66
+ ### Built-in target types
67
+
68
+ The eight built-ins are registered at module load *(source: `engine/watches.js:399-748`)*. Constants live at `engine/shared.js:1563-1572` (`WATCH_TARGET_TYPE`).
69
+
70
+ | `targetType` | Target value | Conditions | Notes |
71
+ |---------------|--------------------------------------|----------------------------------------------------------------------------|-------|
72
+ | `pr` | PR number, canonical id, or display id | `merged`, `build-fail`, `build-pass`, `status-change`, `any`, `new-comments`, `vote-change` | Reads from `pull-requests.json` for any project; `new-comments` watches `humanFeedback.lastProcessedCommentDate` |
73
+ | `work-item` | Work item id | `completed`, `failed`, `status-change`, `any` | `completed` matches `DONE_STATUSES`; `failed` matches `WI_STATUS.FAILED` |
74
+ | `meeting` | Meeting id | `concluded`, `status-change`, `any` | `concluded` fires on terminal status (`completed`, `archived`) |
75
+ | `plan` | PRD JSON filename or plan id | `approved`, `rejected`, `completed`, `status-change`, `any` | Looked up by `_source` (PRD file), `_sourcePlan` (.md), or `id`; uses `PLAN_STATUS` |
76
+ | `schedule` | Schedule id | `ran`, `enabled`, `disabled`, `status-change`, `any` | `ran` fires when `lastRun` advances; `enabled`/`disabled` fire on the flip |
77
+ | `pipeline` | Pipeline id (latest run is tracked) | `completed`, `failed`, `stage-complete`, `status-change`, `any` | `failed` covers `PIPELINE_STATUS.FAILED` and `STOPPED`; `stage-complete` only counts within the same `runId` |
78
+ | `dispatch` | Dispatch entry id | `completed`, `failed`, `status-change`, `any` | Looks across `pending` / `active` / `completed` lists |
79
+ | `agent` | Agent id | `activity-change`, `status-change`, `any` | `activity-change` fires only on transitions in/out of `'working'` |
80
+
81
+ `evaluateWatch` dispatches to `tt.evaluate(...)`; unknown target types return `"Unknown target type: ..."` and unknown conditions return `"Unknown condition: ..."` — both are non-triggering *(source: `engine/watches.js:208-224`)*.
82
+
83
+ ## Tick Integration
84
+
85
+ `engine.js` calls `checkWatches(config, state)` every 3 ticks (~3 min at the default 60s tick) inside its own `safe('checkWatches', ...)` block *(source: `engine.js:4480-4538`)*. The engine builds the state object from cached project files + module reads:
86
+
87
+ ```
88
+ {
89
+ pullRequests, workItems, // flattened across all projects + central
90
+ meetings, plans, // optional — try/catch'd, missing modules give []
91
+ scheduleRuns, pipelineRuns,
92
+ dispatch, agents, config,
93
+ }
94
+ ```
95
+
96
+ `checkWatches` walks every active watch and, inside a single `mutateJsonFileLocked` callback *(source: `engine/watches.js:241-345`)*:
97
+
98
+ 1. Skips paused/expired watches and any watch checked within its `interval`.
99
+ 2. Captures a baseline `_lastState` on first check (so change conditions have something to diff).
100
+ 3. Calls `evaluateWatch(watch, state)`.
101
+ 4. On trigger: increments `triggerCount`, sets `last_triggered`, queues an inbox notification (if `notify === 'inbox'`), and snapshots an action task for any configured `watch.action`.
102
+ 5. Applies fire-once / `stopAfter` expiration.
103
+ 6. On non-trigger: writes a per-poll inbox note when `onNotMet === 'notify'`.
104
+ 7. Refreshes `_lastState` for the next check.
105
+
106
+ I/O happens **outside the lock**: notifications via `writeToInbox`, follow-up actions via `_runActionTask` (`Promise` per action, failures isolated). Each action's result is persisted back onto the watch as `_lastActionResult` in a follow-up locked write *(source: `engine/watches.js:330-377`)*.
107
+
108
+ ## Follow-Up Actions on Trigger
109
+
110
+ `watch.action` is an optional structured action that runs after the inbox notification fires. Action types live in a sibling registry in `engine/watch-actions.js` and are validated at create/update time *(source: `engine/watches.js:112-115`, `engine/watch-actions.js:50-73`)*. `GET /api/watches/action-types` returns the live list for dashboard pickers.
111
+
112
+ ### Built-in actions
113
+
114
+ | Action type | What it does |
115
+ |------------------------|-----------------------------------------------------------------------------------------------|
116
+ | `notify` | Explicit inbox write; lets you customize `owner`/`body` instead of the default trigger string |
117
+ | `dispatch-work-item` | Append a new WI to the project (or central) `work-items.json` with `createdBy: "watch:<id>"` |
118
+ | `run-skill` | Wrapper around `dispatch-work-item` that asks the agent to run a specific `.claude` skill |
119
+ | `webhook` | `http`/`https` request to an arbitrary URL (10s safety timeout, JSON or string body) |
120
+ | `cancel-work-item` | Flip a WI to `WI_STATUS.CANCELLED` across all known work-items files |
121
+ | `trigger-pipeline` | Start a new pipeline run (skipped if the pipeline already has an active run) |
122
+ | `archive-plan` | Set PRD `status="archived"` + `archivedAt` |
123
+ | `resume-plan` | Set PRD `status=PLAN_STATUS.ACTIVE` and clear `planStale` |
124
+
125
+ Constants live in `WATCH_ACTION_TYPE` (`engine/shared.js:1605-1616`); handlers in `engine/watch-actions.js:185-464`.
126
+
127
+ ### Templating
128
+
129
+ Action params support `{{var}}` substitution from the trigger context *(source: `engine/watch-actions.js:83-99`)*. Built-in vars from `buildTriggerContext` *(source: `engine/watch-actions.js:107-154`)*:
130
+
131
+ - Always: `target`, `targetType`, `condition`, `watchId`, `triggerCount`, `message`
132
+ - All scalar fields from `newState` (e.g. `status`, `buildStatus`, `reviewStatus`, `lastRun`)
133
+ - Type-specific aliases: `prNumber`, `branch`, `prId`, `prUrl`, `project` (pr); `workItemId`, `workItemTitle`, `project` (work-item); `meetingId`, `meetingTitle` (meeting); `planFile`, `planMd` (plan); `pipelineId`, `runId` (pipeline); `dispatchId` (dispatch); `agentId` (agent)
134
+
135
+ Unknown vars are left as `{{var}}` so callers can detect them downstream.
136
+
137
+ ### Failure isolation
138
+
139
+ Action handlers must return `{ ok: false, summary }` rather than throw — `runWatchAction` wraps them in try/catch as a backstop *(source: `engine/watch-actions.js:160-178`)*. A bad action never blocks other watches in the same tick, and the failed result is persisted onto the watch as `_lastActionResult` for debugging.
140
+
141
+ ## Dashboard Surface
142
+
143
+ | Route | Method | Handler | Purpose |
144
+ |--------------------------------|--------|-------------------------------|-------------------------------------------------------------------------|
145
+ | `/watches` | GET | `dashboard/pages/watches.html` | Watches page (sidebar link in `dashboard/layout.html:75`) |
146
+ | `/api/watches` | GET | `handleWatchesList` | List all watches |
147
+ | `/api/watches/target-types` | GET | `handleWatchesTargetTypes` | Live registry from `listTargetTypes()` — used by the create-watch modal |
148
+ | `/api/watches/action-types` | GET | `handleWatchesActionTypes` | Live registry from `listActionTypes()` |
149
+ | `/api/watches` | POST | `handleWatchesCreate` | Create a watch (body: `target, targetType, condition, interval?, owner?, description?, project?, notify?, stopAfter?, onNotMet?, action?`) |
150
+ | `/api/watches/update` | POST | `handleWatchesUpdate` | Pause/resume/modify (body: `id, status?, interval?, description?, notify?, stopAfter?, onNotMet?, condition?, action?`) |
151
+ | `/api/watches/delete` | POST | `handleWatchesDelete` | Delete (body: `id`) |
152
+
153
+ *(source: route table in `dashboard.js:7395-7400`; handlers in `dashboard.js:6370-6422`)*
154
+
155
+ The watches page is rendered by `dashboard/js/render-watches.js`; the **+ New** button calls `openCreateWatchModal()` which fetches `/api/watches/target-types` and `/api/watches/action-types` to populate the picker dynamically *(source: `dashboard/pages/watches.html:3`, `dashboard/js/render-watches.js:354-414`)*.
156
+
157
+ The CC state preamble injects a `Watches: <count>` line via `getWatches()` so the chat brain knows how many watches exist *(source: `dashboard.js:1342`)*.
158
+
159
+ ## Command Center Integration
160
+
161
+ CC creates, pauses, resumes, and deletes watches by calling the REST API directly via its `Bash` tool — the older `===ACTIONS===` `create-watch`/`delete-watch`/`pause-watch`/`resume-watch` actions have been retired *(source: `test/unit.test.js:16976-17008`, `test/unit/watches-module.test.js:673`)*. To use a watch from CC chat, ask it something like:
162
+
163
+ > "Watch PR 1234 for build-pass and dispatch a verify work-item when it fires."
164
+
165
+ CC will discover the endpoints from `GET /api/routes` and `POST /api/watches` with the appropriate `targetType`, `condition`, and `action` payload.
166
+
167
+ ## Expected Outputs
168
+
169
+ When a watch fires:
170
+
171
+ - `engine/watches.json` entry gets `triggerCount++`, `last_triggered=<ISO>`, `_lastTriggerMessage=<message>`, and a refreshed `_lastState`.
172
+ - An inbox file lands at `notes/inbox/<owner>/watch-<watchId>-<n>.md` (when `notify === 'inbox'`).
173
+ - If `action` is set, a follow-up runs (work item, webhook POST, plan archive, ...) and writes `_lastActionResult` (`{ type, ok, summary, dispatchedItemId?, at }`) back onto the watch.
174
+ - `engine.log.json` gets `Watch triggered: <id> — <message>` and `Watch <id> action <type>: <summary>`.
175
+
176
+ Absolute conditions firing under `stopAfter === 0` flip `status` to `expired`; `stopAfter > 0` flips it to `expired` once `triggerCount >= stopAfter`.
177
+
178
+ ## Common Failure Modes
179
+
180
+ | Symptom | Likely cause / debug |
181
+ |-------------------------------------------------------------------------|----------------------------------------------------------------------------------------|
182
+ | Watch never fires | Check `status === 'active'`; check `last_checked` advancing each cycle; confirm engine tick is running and `interval` isn't longer than your test window |
183
+ | `evaluateWatch` returns `"<label> <target> not found"` | `fetchEntity` got nothing back — wrong `target` (e.g. PR display id vs canonical id), the target type isn't loaded, or the underlying file (PR cache, plan PRD) doesn't exist |
184
+ | `"Unknown target type"` / `"Unknown condition"` | The registry doesn't recognise the value. Check `GET /api/watches/target-types` to see what's registered server-side; condition must be in that target type's `conditions[]` |
185
+ | Change condition fires immediately on first tick | Won't happen — baseline `_lastState` is captured on the first check before `evaluate` runs *(source: `engine/watches.js:262-266`)*. If you see this, suspect manual edits to `watches.json` |
186
+ | Absolute watch fires forever instead of once | `stopAfter` is set to a non-zero value; only `stopAfter === 0` triggers fire-once expiration |
187
+ | Action runs but inbox notification doesn't | `notify` field isn't `'inbox'`, or `owner` is empty. `notify` and `action` are independent — both can fire, or only one |
188
+ | `_lastActionResult.ok === false` with `"unknown action type"` | The `action.type` isn't registered. List with `listActionTypes()` / `GET /api/watches/action-types` |
189
+ | Webhook action returns `"only http/https allowed"` | URLs must use `http://` or `https://` schemes; other protocols are rejected by design *(source: `engine/watch-actions.js:297-299`)* |
190
+ | Trigger fires but follow-up `dispatch-work-item` is missing | Check the engine log for `Watch <id> action <type>: <summary>`. Common reasons: missing `title`, the project's `work-items.json` couldn't be written, or the WI landed in central `work-items.json` because no project was specified |
191
+ | Watch `_lastActionResult` shows `"timeout"` for webhook | Webhooks have a 10s safety timeout to keep the watches tick fast *(source: `engine/watch-actions.js:334-337`)* |
192
+ | `checkWatches` block crashes silently | Wrapped in `safe('checkWatches', ...)` so one failure doesn't abort the tick *(source: `engine.js:4484`)*. Inspect `engine/log.json` for `Watch check error (<id>)` lines. Regression #1088: the block must use `getProjects(config)`, never the long-removed `PROJECTS` constant |
193
+
194
+ ## See Also
195
+
196
+ - `engine/shared.js:1557-1616` — `WATCH_STATUS`, `WATCH_TARGET_TYPE`, `WATCH_CONDITION`, `WATCH_ABSOLUTE_CONDITIONS`, `WATCH_ACTION_TYPE` constants
197
+ - `engine/watches.js` — registry, lifecycle, tick integration
198
+ - `engine/watch-actions.js` — action registry and built-in handlers
199
+ - `dashboard/pages/watches.html`, `dashboard/js/render-watches.js` — dashboard UI
200
+ - `test/unit/watches-module.test.js`, `test/unit/watch-actions.test.js` — module-level tests
201
+ - [`auto-discovery.md`](auto-discovery.md) — overall tick cycle context
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yemi33/minions",
3
- "version": "0.1.1884",
3
+ "version": "0.1.1885",
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/docs.md CHANGED
@@ -92,7 +92,7 @@ git commit -m "{{commit_message}}"
92
92
  git push -u origin {{branch_name}}
93
93
  ```
94
94
 
95
- PR creation is MANDATORY for docs tasks — docs go through the same review flow as code.
95
+ Create the PR for docs tasks — docs go through the same review flow as code.
96
96
  Use the appropriate repo-host tooling for PR creation. For Azure DevOps, prefer the
97
97
  `az` CLI first and use the ADO MCP only as a fallback.
98
98
 
package/playbooks/fix.md CHANGED
@@ -102,15 +102,4 @@ Your task is complete when each review finding has either been fixed or answered
102
102
 
103
103
  ## Completion
104
104
 
105
- After finishing, write the JSON completion report described in the shared rules. Also output this structured completion block as a compatibility fallback:
106
-
107
- ```completion
108
- status: done | partial | failed
109
- files_changed: <comma-separated list of key files changed>
110
- tests: pass | fail | skipped | N/A
111
- pr: PR-<number> or N/A
112
- failure_class: N/A
113
- pending: <any remaining work, or none>
114
- ```
115
-
116
- Replace the values with your actual results.
105
+ After finishing, write the JSON completion report described in the shared rules (schema: `docs/completion-reports.md`). A fenced ` ```completion ` block in stdout is accepted only as a compatibility fallback — do not duplicate the schema here.
@@ -84,15 +84,4 @@ Your task is complete when the requested implementation is delivered, the valida
84
84
 
85
85
  ## Completion
86
86
 
87
- After finishing, write the JSON completion report described in the shared rules. Also output this structured completion block as a compatibility fallback:
88
-
89
- ```completion
90
- status: done | partial | failed
91
- files_changed: <comma-separated list of key files changed>
92
- tests: pass | fail | skipped | N/A
93
- pr: N/A
94
- failure_class: N/A
95
- pending: <any remaining work, or none>
96
- ```
97
-
98
- Replace the values with your actual results.
87
+ After finishing, write the JSON completion report described in the shared rules (schema: `docs/completion-reports.md`). A fenced ` ```completion ` block in stdout is accepted only as a compatibility fallback — do not duplicate the schema here.
@@ -83,12 +83,12 @@ git push -u origin {{branch_name}}
83
83
 
84
84
  {{pr_create_instructions}}
85
85
 
86
- PR creation is MANDATORY for implement tasks because the engine tracks review and completion from the PR.
86
+ Create the PR for implement tasks the engine tracks review and completion from the PR.
87
87
 
88
88
  Include build/test status and run instructions in the PR description. If the project has a runnable app, include the localhost URL.
89
89
 
90
90
  ## When to Stop
91
91
 
92
- Your task is complete when the requested implementation is delivered, the validation story is truthful and sufficient for review, the branch is pushed, and the PR exists. Your final message MUST include the PR URL so the engine can track it.
92
+ Your task is complete when the requested implementation is delivered, the validation story is truthful and sufficient for review, the branch is pushed, and the PR exists. Include the PR URL in your final message so the engine can track it.
93
93
 
94
94
  Do NOT run `gh pr merge` or any other merge command on your own PR. The engine reviews and merges PRs through a separate review cycle. Self-merging is prohibited.
@@ -27,7 +27,7 @@ A user has provided a plan. Analyze it against the codebase and produce a struct
27
27
 
28
28
  Write the PRD to: `{{team_root}}/prd/{{prd_filename}}`
29
29
 
30
- **CRITICAL: Use the exact filename above.** The engine pre-generates a unique filename to avoid collisions with existing or archived PRDs. Do NOT change or rename it.
30
+ **Use the exact filename above.** The engine pre-generates a unique filename to avoid collisions with existing or archived PRDs; renaming it will detach the PRD from the plan.
31
31
 
32
32
  This file is NOT checked into the repo. The engine reads it on every tick and dispatches implementation work automatically.
33
33
 
@@ -87,7 +87,7 @@ Rules for items:
87
87
  - IDs must be `P-<uuid>` format (e.g. `P-a3f9b2c1`) — globally unique, never sequential
88
88
  - **`status` is `"missing"` for new items** — do not set `done`, `complete`, `implemented`, or any other value based on codebase observations. The only exception is when reusing an existing PRD (see below) — items already `"done"` in the existing PRD carry forward as `"done"`. Pre-setting any other status on new items causes them to be silently skipped by the engine.
89
89
  - **Do NOT include a "verify" or "test" or "integration test" item** — the engine automatically creates a verify task when all PRD items are done. Adding one manually creates a duplicate that blocks plan completion.
90
- - **`project` field is REQUIRED** — set it to the project name where the code changes go (e.g., `"OfficeAgent"`, `"office-bohemia"`). If the plan declares a single project, every item must use `{{project_name}}`; contextual mentions of another repo/product must not override it. Cross-repo plans must route each item to the correct project. The engine materializes items into that project's work queue.
90
+ - **`project` field is required** — set it to the project name where the code changes go (e.g., `"OfficeAgent"`, `"office-bohemia"`). If the plan declares a single project, every item should use `{{project_name}}`; contextual mentions of another repo/product should not override it. Cross-repo plans should route each item to the correct project. The engine materializes items into that project's work queue.
91
91
  - **Cross-repo plans:** when the plan body does NOT declare `**Project:**` (or declares it empty), `{{project_name}}` is unset. In that case, set each item's `project` to the actual repo where its code changes belong, and omit the top-level `"project"` field (or set it to `""`). Example:
92
92
  ```json
93
93
  "missing_features": [
@@ -112,7 +112,7 @@ When the engine detects an existing PRD for this plan (`source_plan` match), it
112
112
  **When an existing PRD is provided:**
113
113
 
114
114
  1. **Parse the existing PRD JSON** — extract all `missing_features` items with their `id`, `status`, and metadata
115
- 2. **Preserve item IDs** — match existing items to current plan items by name/description similarity. Each plan item that corresponds to an existing PRD item MUST reuse that item's `P-<id>`. Do NOT generate new IDs for items that already exist.
115
+ 2. **Preserve item IDs** — match existing items to current plan items by name/description similarity. Each plan item that corresponds to an existing PRD item should reuse that item's `P-<id>`. Do not generate new IDs for items that already exist — duplicates orphan the prior work.
116
116
  3. **Preserve done items** — any existing item with `"status": "done"` carries forward as `"done"` with the same ID, description, and acceptance criteria. Do NOT reset done items to `"missing"`
117
117
  4. **Carry forward in-progress items** — items with `"status": "missing"` or other non-done statuses (except `"updated"`) keep their existing ID and reset to `"missing"`. Items with `"status": "updated"` keep their existing ID and remain `"updated"` — do NOT reset to `"missing"`
118
118
  5. **New items only** — only generate new `P-<uuid>` IDs for items in the plan that have no match in the existing PRD
@@ -59,7 +59,7 @@ Use subagents only for genuinely parallel, independent tasks (e.g., reviewing un
59
59
 
60
60
  ## Post Review — Submit your verdict
61
61
 
62
- You MUST post a review comment with a clear verdict and write the completion report described in the shared rules. The verdict in the report is the primary machine-readable signal; the verdict line in the PR comment is for humans and backward compatibility.
62
+ You MUST post a review comment with a clear verdict and write the completion report described in the shared rules (schema: `docs/completion-reports.md`). The verdict in the report is the primary machine-readable signal; the verdict line in the PR comment is for humans and backward compatibility.
63
63
 
64
64
  ### Post your review with verdict
65
65
 
@@ -70,9 +70,7 @@ The engine provides a completion report path in the prompt and in `MINIONS_COMPL
70
70
  {"status":"success","summary":"what changed and how it was validated","verdict":null,"pr":"PR id/url or N/A","failure_class":"N/A","retryable":false,"needs_rerun":false,"artifacts":[{"type":"note|plan|prd|pr|file","path":"relative/path/or/url","title":"short label"}]}
71
71
  ```
72
72
 
73
- Use `status: "failed"` plus an accurate `failure_class`, `retryable`, and `needs_rerun` when the task could not be completed. For PR reviews, set `verdict` to `approved` or `changes-requested`. Include every durable artifact you created or updated in `artifacts` (PRs, notes, plans, PRDs, important files) so the dashboard can display them. Fenced `completion` blocks are still accepted as a fallback, but the JSON report is the primary signal.
74
-
75
- **No-op completions:** when you correctly decline to do the work — the change was already shipped on master, the dispatch premise is wrong, the flagged review comment is your own author-notes, etc. — write `status: "success"`, `pr: "N/A"`, AND add `"noop": true`. The engine treats `noop: true` as the canonical signal that no PR was expected, marks the work item done with the rationale surfaced in `_noopReason` for the dashboard, and skips the missing-PR-attachment failure. Without `noop: true`, an empty PR will still be flagged as a silent failure and auto-retried up to `maxRetries` times.
73
+ 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.
76
74
 
77
75
  ## Long-Running Commands
78
76
 
@@ -63,4 +63,4 @@ Track each E2E PR in the project's `.minions/pull-requests.json` if it is not al
63
63
 
64
64
  Your task is complete once dependency branches are merged, build/test verification has run, the permanent guide is written, the inbox copy is written only if verification succeeded, and E2E PR URLs are included in your final response.
65
65
 
66
- Your final message MUST include each E2E PR URL and the testing guide path, for example: `E2E PR created: <url>` and `Testing guide: {{team_root}}/prd/guides/verify-{{plan_slug}}.md`.
66
+ Include each E2E PR URL and the testing guide path in your final message, for example: `E2E PR created: <url>` and `Testing guide: {{team_root}}/prd/guides/verify-{{plan_slug}}.md`.
@@ -113,12 +113,20 @@ curl -s -X POST http://localhost:{{dashboard_port}}/api/knowledge \
113
113
  -d '{"category":"architecture","title":"Doc-chat session lifecycle","content":"..."}'
114
114
  ```
115
115
 
116
- Build-and-test a PR:
116
+ Build-and-test a PR (dispatched as a `test` work item — engine routes it to the `build-and-test` playbook):
117
+ ```
118
+ curl -s -X POST http://localhost:{{dashboard_port}}/api/work-items \
119
+ -H 'Content-Type: application/json' \
120
+ -H 'X-CC-Turn-Id: {{cc_turn_id}}' \
121
+ -d '{"title":"Build-and-test PR #1234","type":"test","project":"MyApp","description":"Run build + tests against PR #1234.","references":[{"url":"https://github.com/owner/MyApp/pull/1234","label":"PR #1234"}]}'
122
+ ```
123
+
124
+ Trigger a user-defined pipeline (only `id` is read from the body — there is no per-trigger context payload, so the pipeline runs exactly as configured):
117
125
  ```
118
126
  curl -s -X POST http://localhost:{{dashboard_port}}/api/pipelines/trigger \
119
127
  -H 'Content-Type: application/json' \
120
128
  -H 'X-CC-Turn-Id: {{cc_turn_id}}' \
121
- -d '{"id":"build-and-test","context":{"pr":1234,"project":"MyApp"}}'
129
+ -d '{"id":"my-pipeline-id"}'
122
130
  ```
123
131
 
124
132
  Link an external PR for tracking: