@yemi33/minions 0.1.2378 → 0.1.2380
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/bin/minions.js +19 -9
- package/dashboard/js/refresh.js +2 -3
- package/dashboard/js/render-prd.js +1 -1
- package/dashboard/js/render-work-items.js +13 -6
- package/dashboard.js +393 -721
- package/docs/README.md +1 -0
- package/docs/architecture-review-2026-07-09.md +2 -4
- package/docs/auto-discovery.md +36 -49
- package/docs/blog-first-successful-dispatch.md +1 -1
- package/docs/branch-derivation.md +2 -2
- package/docs/command-center.md +1 -1
- package/docs/completion-reports.md +14 -4
- package/docs/constellation-bridge.md +59 -10
- package/docs/constellation-style-telemetry.md +6 -6
- package/docs/cooldown-merge-semantics.md +4 -0
- package/docs/copilot-cli-schema.md +3 -3
- package/docs/cross-repo-plans.md +17 -17
- package/docs/deprecated.json +2 -2
- package/docs/design-inbox-entries-schema.md +31 -39
- package/docs/design-state-storage.md +1 -1
- package/docs/documentation-audit-2026-07-09.md +2 -2
- package/docs/engine-restart.md +20 -8
- package/docs/harness-mode.md +1 -1
- package/docs/managed-spawn.md +4 -4
- package/docs/onboarding.md +1 -2
- package/docs/pr-comment-followup.md +3 -3
- package/docs/pr-review-fix-loop.md +1 -1
- package/docs/proposals/repo-pool-for-live-checkout.md +1 -2
- package/docs/qa-runbook-lifecycle.md +4 -4
- package/docs/qa-runbooks.md +2 -2
- package/docs/rfc-completion-json.md +4 -1
- package/docs/runtime-adapters.md +1 -1
- package/docs/self-improvement.md +4 -5
- package/docs/shared-lifecycle-module-map.md +3 -1
- package/docs/slim-ux/architecture-suggestions.md +5 -6
- package/docs/slim-ux/concepts.md +23 -25
- package/docs/watches.md +7 -7
- package/docs/workspace-manifests.md +1 -1
- package/docs/worktree-lifecycle.md +1 -1
- package/engine/abandoned-pr-reconciliation.js +4 -5
- package/engine/acp-transport.js +49 -8
- package/engine/ado-status.js +5 -5
- package/engine/ado.js +20 -25
- package/engine/agent-worker-pool.js +124 -15
- package/engine/bridge.js +260 -5
- package/engine/cleanup.js +48 -131
- package/engine/cli.js +125 -83
- package/engine/cooldown.js +9 -16
- package/engine/db/index.js +22 -9
- package/engine/db/migrations/009-qa.js +1 -1
- package/engine/db/migrations/020-qa-session-scopes.js +23 -0
- package/engine/db/migrations/021-archived-work-items.js +72 -0
- package/engine/db/migrations/022-global-cc-session.js +31 -0
- package/engine/db/migrations/023-engine-state.js +30 -0
- package/engine/db/migrations/024-prd-ghost-collisions.js +53 -0
- package/engine/db/migrations/025-malformed-work-item-phantoms.js +33 -0
- package/engine/dispatch-store.js +2 -7
- package/engine/dispatch.js +34 -41
- package/engine/github.js +20 -27
- package/engine/lifecycle.js +221 -283
- package/engine/llm.js +1 -1
- package/engine/logs-store.js +2 -2
- package/engine/managed-spawn.js +2 -23
- package/engine/meeting.js +6 -6
- package/engine/metrics-store.js +2 -2
- package/engine/note-link-backfill.js +6 -11
- package/engine/pipeline.js +18 -36
- package/engine/playbook.js +1 -1
- package/engine/pooled-agent-process.js +46 -26
- package/engine/prd-store.js +73 -54
- package/engine/preflight.js +2 -5
- package/engine/projects.js +15 -62
- package/engine/pull-requests-store.js +0 -17
- package/engine/qa-runbooks.js +2 -2
- package/engine/qa-runs.js +1 -8
- package/engine/qa-sessions.js +41 -64
- package/engine/queries.js +120 -219
- package/engine/routing.js +3 -5
- package/engine/scheduler.js +0 -4
- package/engine/shared-branch-pr-reconcile.js +2 -3
- package/engine/shared.js +132 -637
- package/engine/small-state-store.js +89 -10
- package/engine/state-operations.js +16 -4
- package/engine/stdio-timestamps.js +1 -1
- package/engine/timeout.js +5 -12
- package/engine/watch-actions.js +20 -22
- package/engine/watches-store.js +1 -1
- package/engine/watches.js +6 -10
- package/engine/work-item-validation.js +52 -0
- package/engine/work-items-store.js +127 -29
- package/engine/worktree-gc.js +2 -2
- package/engine/worktree-pool.js +8 -18
- package/engine.js +167 -349
- package/minions.js +2 -2
- package/package.json +1 -1
- package/playbooks/plan-to-prd.md +2 -2
- package/playbooks/shared-rules.md +3 -3
- package/playbooks/templates/followup-dispatch.md +1 -1
- package/playbooks/verify.md +1 -1
- package/prompts/cc-system.md +2 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# inbox_entries Table Schema
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
|
-
The `inbox_entries` table
|
|
4
|
+
The `inbox_entries` table and `engine/inbox-store.js` implement a persistent, time-ordered, unread-tracking message queue decoupled from the runtime work-item/dispatch lifecycle. The store API exists, but production engine, dashboard, and consolidation paths do not consume it yet; those paths still use their existing inbox mechanisms.
|
|
5
5
|
|
|
6
6
|
## Schema Specification
|
|
7
7
|
|
|
@@ -9,26 +9,27 @@ The `inbox_entries` table is the persistent store for operator and agent inbox m
|
|
|
9
9
|
|
|
10
10
|
| Column | Type | Constraints | Purpose |
|
|
11
11
|
|--------|------|-----------|---------|
|
|
12
|
-
| `id` | TEXT | PRIMARY KEY | Unique message identifier; generated as `<recipient>-<timestamp>-<sender>-<seq>`
|
|
12
|
+
| `id` | TEXT | PRIMARY KEY | Unique message identifier; generated as `<recipient>-<timestamp>-<sender>-<seq>-<counter>-<random>` |
|
|
13
13
|
| `recipient_session_id` | TEXT | NOT NULL | Session ID of the inbox owner (operator or agent); routes messages to specific actor |
|
|
14
14
|
| `sender_id` | TEXT | NOT NULL | Identifier of the message originator; structured as `<agent-id>` or `'system'` or `'operator'` |
|
|
15
15
|
| `sender_name` | TEXT | NOT NULL | Human-readable sender name for UI display; persisted at write-time for offline/archived reference |
|
|
16
|
-
| `sender_type` | TEXT | NOT NULL |
|
|
17
|
-
| `interaction_id` | TEXT | NOT NULL |
|
|
18
|
-
| `sequence` | INTEGER | NOT NULL, DEFAULT 0 |
|
|
16
|
+
| `sender_type` | TEXT | NOT NULL | Sender category; `createInboxEntry()` accepts `'agent'`, `'system'`, or `'operator'` |
|
|
17
|
+
| `interaction_id` | TEXT | NOT NULL | Caller-supplied group ID for related messages |
|
|
18
|
+
| `sequence` | INTEGER | NOT NULL, DEFAULT 0 | Caller-supplied per-interaction ordering metadata; not required to be unique |
|
|
19
19
|
| `summary` | TEXT | NOT NULL | Title/subject line; used for list views and user scanning; always present even if content is truncated |
|
|
20
20
|
| `content` | TEXT | NOT NULL | Full message body; may contain Markdown, code blocks, or structured JSON sidecars (not yet parsed at table level) |
|
|
21
|
-
| `unread` | INTEGER | NOT NULL, DEFAULT 1 | Boolean flag (0/1);
|
|
21
|
+
| `unread` | INTEGER | NOT NULL, DEFAULT 1 | Boolean flag (0/1); changed by the read/unread mutators |
|
|
22
22
|
| `sent_at` | INTEGER | NOT NULL | Unix timestamp (milliseconds) of message creation; primary sort key for time-ordered display |
|
|
23
23
|
| `read_at` | INTEGER | Nullable | Unix timestamp (milliseconds) when recipient first read the message; null until marked read |
|
|
24
24
|
| `notified_at` | INTEGER | Nullable | Unix timestamp (milliseconds) of last notification event sent to operator; null if no notification dispatched |
|
|
25
25
|
|
|
26
26
|
### Constraints & Relationships
|
|
27
27
|
|
|
28
|
-
- **PK constraint:** `id` is globally unique across all inboxes
|
|
28
|
+
- **PK constraint:** `id` is globally unique across all inboxes. The store combines caller context with a process-local counter and random suffix, then retries a conflicting insert up to five times.
|
|
29
29
|
- **FK (soft):** `recipient_session_id` does NOT enforce a referential constraint to `sessions` table (sessions may be archived). Queries SHOULD handle missing recipients gracefully.
|
|
30
|
-
- **FK (soft):** `sender_id` is informational only; no validation
|
|
31
|
-
- **
|
|
30
|
+
- **FK (soft):** `sender_id` is informational only; no validation requires it to exist in `config.agents` or active sessions.
|
|
31
|
+
- **Application validation:** `createInboxEntry()` accepts only `agent`, `system`, or `operator` for `sender_type`; SQLite does not enforce that enum.
|
|
32
|
+
- **Indexes:** Migration 019 creates composite recipient/time and interaction indexes. See Performance Considerations.
|
|
32
33
|
|
|
33
34
|
## Architectural Rationale
|
|
34
35
|
|
|
@@ -36,71 +37,62 @@ The `inbox_entries` table is the persistent store for operator and agent inbox m
|
|
|
36
37
|
|
|
37
38
|
1. **Separate inbox table vs. inline dispatch/work-item attachments.**
|
|
38
39
|
- Inboxes can outlive their source work items (e.g., a feedback message remains useful after the WI is done).
|
|
39
|
-
- Decoupling allows independent
|
|
40
|
-
- Supports rich async consolidation (team notes, KB articles) that don't belong to any single WI.
|
|
40
|
+
- Decoupling allows independent read/unread and deletion lifecycle without cascading work-item deletes.
|
|
41
41
|
|
|
42
42
|
2. **`unread` flag instead of soft-delete or status enum.**
|
|
43
43
|
- Fast count queries for badge (`SELECT COUNT(*) WHERE recipient_session_id = ? AND unread = 1`).
|
|
44
|
-
- Unread state is mutable; allows mark-as-read
|
|
45
|
-
- Simplifies archive/export (no tombstone filtering needed).
|
|
44
|
+
- Unread state is mutable; allows mark-as-read and mark-as-unread behavior without deleting content.
|
|
46
45
|
|
|
47
46
|
3. **`sender_type` as an explicit column.**
|
|
48
|
-
- Allows
|
|
47
|
+
- Allows application-level validation of the sender category.
|
|
49
48
|
- Enables filtered views ("show only agent messages" or "system alerts only") without string parsing.
|
|
50
49
|
|
|
51
50
|
4. **`interaction_id` for grouping.**
|
|
52
|
-
-
|
|
53
|
-
- Supports consolidation: multiple small notifications can be collapsed into one summary message.
|
|
51
|
+
- `getInboxByInteraction()` retrieves related messages in chronological order.
|
|
54
52
|
|
|
55
53
|
5. **Persistent `sender_name` at write-time.**
|
|
56
54
|
- Operator may rename agents or disable them; storing the name ensures historical accuracy.
|
|
57
|
-
-
|
|
55
|
+
- Future consumers can render the stored name without a runtime agent lookup.
|
|
58
56
|
|
|
59
57
|
6. **Unix timestamps (milliseconds) instead of ISO strings.**
|
|
60
58
|
- Enables range queries (`sent_at BETWEEN ? AND ?`) without parsing.
|
|
61
59
|
- Smaller column footprint; easier to sort.
|
|
62
60
|
- Downside: not human-readable; UI must convert to locale strings.
|
|
63
61
|
|
|
64
|
-
###
|
|
62
|
+
### Integration Status
|
|
65
63
|
|
|
66
|
-
-
|
|
67
|
-
- **inbox_entries ← PR reviews:** Humans and auto-review agents post comments; summarized as inbox alerts to project owners via `interaction_id` per-PR.
|
|
68
|
-
- **inbox_entries ← consolidation:** `engine/consolidation.js` reads inbox entries to extract `agent:` frontmatter and route findings into per-agent knowledge files.
|
|
64
|
+
The table currently has no foreign keys and `engine/inbox-store.js` has no production callers. Dispatch feedback, PR notifications, dashboard inbox views, and consolidation are potential consumers, not implemented relationships. Until those call sites migrate, this table is not the source of truth for `notes/inbox/` or agent steering inbox files.
|
|
69
65
|
|
|
70
66
|
## Performance Considerations
|
|
71
67
|
|
|
72
68
|
### Indexing Strategy
|
|
73
69
|
|
|
74
|
-
**Current indexes (
|
|
75
|
-
- `
|
|
76
|
-
- `
|
|
70
|
+
**Current indexes (created by migration 019):**
|
|
71
|
+
- `idx_inbox_recipient_sent` on `(recipient_session_id, sent_at DESC)` — supports recipient-scoped chronological reads.
|
|
72
|
+
- `idx_inbox_interaction` on `(interaction_id)` — supports grouping by interaction.
|
|
77
73
|
- PK `id` is implicit index.
|
|
78
74
|
|
|
79
75
|
**Query patterns:**
|
|
80
|
-
- **Unread count per session:** `SELECT COUNT(*) WHERE recipient_session_id = ? AND unread = 1`
|
|
81
|
-
- **Latest messages:** `SELECT * WHERE recipient_session_id = ? ORDER BY sent_at DESC LIMIT
|
|
82
|
-
- **Thread view:** `SELECT * WHERE interaction_id = ? ORDER BY sent_at`
|
|
83
|
-
- **Cleanup:** `
|
|
76
|
+
- **Unread count per session:** `SELECT COUNT(*) ... WHERE recipient_session_id = ? AND unread = 1` narrows by recipient, then filters `unread`.
|
|
77
|
+
- **Latest messages:** `SELECT * ... WHERE recipient_session_id = ? ORDER BY sent_at DESC LIMIT ? OFFSET ?` uses `idx_inbox_recipient_sent`.
|
|
78
|
+
- **Thread view:** `SELECT * ... WHERE interaction_id = ? ORDER BY sent_at ASC` uses `idx_inbox_interaction`, then sorts matching rows.
|
|
79
|
+
- **Cleanup:** `deleteInboxEntriesOlderThan(timestampMs, { unreadOnly })` deletes all older rows by default or only rows with `unread = 1`; no production cleanup job calls it yet.
|
|
84
80
|
|
|
85
81
|
**Missing indexes (future optimization):**
|
|
86
82
|
- `idx_sender_id` — if queries like "messages from this agent" are common, consider adding.
|
|
87
83
|
- `idx_unread` — if badge ("N unread") is a hot query, consider combining with recipient (`idx_recipient_unread` on `(recipient_session_id, unread)`).
|
|
88
84
|
|
|
89
|
-
###
|
|
85
|
+
### Cleanup
|
|
90
86
|
|
|
91
|
-
- **
|
|
92
|
-
- **Growth:** ~5–50 entries/day (depends on agent activity and verbose flagging).
|
|
93
|
-
- **Retention:** No automatic cleanup; operators may archive/export and manually delete aged entries. Engine does NOT auto-prune.
|
|
94
|
-
- **Recommended**: Implement a dashboard setting to soft-delete entries older than N days (default 30) with an undo/export option.
|
|
87
|
+
- **Retention:** No automatic cleanup is wired. The store exposes explicit single, batch, and age-based hard-delete helpers.
|
|
95
88
|
|
|
96
89
|
## Migration & Backward Compatibility
|
|
97
90
|
|
|
98
|
-
- **Migration
|
|
99
|
-
-
|
|
91
|
+
- **Migration 019** creates `inbox_entries` and its two secondary indexes. It does not import existing Markdown inbox files.
|
|
92
|
+
- The store supports inserts, read/unread updates, notification timestamps, and hard deletes. Every mutator attempts a best-effort `inbox` state event after a real change so dashboard cache versions can advance once consumers are wired.
|
|
100
93
|
|
|
101
94
|
## Related Code
|
|
102
95
|
|
|
103
|
-
- **
|
|
104
|
-
- **
|
|
105
|
-
- **
|
|
106
|
-
|
|
96
|
+
- **Schema:** `engine/db/migrations/019-inbox-entries.js`.
|
|
97
|
+
- **Store:** `engine/inbox-store.js` (reads, transactional mutators, and best-effort `emitStateEvent('inbox', ...)` after real changes).
|
|
98
|
+
- **Consumers:** none in production yet.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Author: Rebecca (Architect) | Date: 2026-04-07 | Status: **Implemented**
|
|
4
4
|
|
|
5
|
-
> **Implementation status
|
|
5
|
+
> **Implementation status:** Migrations 001–024 implement the full SQLite cutover and final compatibility cleanup. `engine/state.db` is the sole runtime-state authority; migration 018 records the cutover, migrations 020–024 absorb the remaining scoped/archive/session state and one-time PRD collision repair. Historical migrations retain one-time JSON import support. Normal runtime never references retired mirrors. `minions state check`, `backup`, and `export` provide operations support; explicit offline recovery import is available through `minions state import-legacy-json <dir> --confirm`. Sections 1–3 below are retained as historical design rationale.
|
|
6
6
|
|
|
7
7
|
## Executive Summary
|
|
8
8
|
|
|
@@ -13,11 +13,11 @@ canonical data, or misunderstand routing and dashboard behavior.
|
|
|
13
13
|
|---|---|---|
|
|
14
14
|
| Node.js 18+ was sufficient | Node.js 22.5+ is required for `node:sqlite` | `package.json:68` |
|
|
15
15
|
| Agent dispatch was described as Claude-only | Copilot is the default fleet runtime; Claude and Codex remain supported | `engine/shared.js:3520`, `engine/runtimes/index.js` |
|
|
16
|
-
| Root and per-project JSON trackers were described as primary state |
|
|
16
|
+
| Root and per-project JSON trackers were described as primary state | `engine/state.db` is the sole runtime-state authority; retired trackers are migration inputs only | `engine/db/migrations/018-sql-only-cutover.js`, `CLAUDE.md` "State storage" |
|
|
17
17
|
| `minions preflight` was documented as a public command | `minions doctor` is public; lighter checks run internally during initialization and startup | `bin/minions.js:1207-1264`, `engine/cli.js:232-255` |
|
|
18
18
|
| Git repository host default was documented as ADO | The fallback host is GitHub | `engine/projects.js:504`, `engine/projects.js:564` |
|
|
19
19
|
| `WORK_TYPE` documentation omitted complex build fixes | Added `BUILD_FIX_COMPLEX` | `engine/shared.js:4370-4396`, `routing.md:10-28` |
|
|
20
|
-
| Command Center sessions were described as a JSON-file store |
|
|
20
|
+
| Command Center sessions were described as a JSON-file store | Per-tab and global sessions are stored only in SQLite | `engine/small-state-store.js`, `engine/db/migrations/022-global-cc-session.js` |
|
|
21
21
|
| Watches were described as checking every three ticks and persisting to JSON | The default cadence is 18 ticks; persistence routes through the SQL watch store | `engine/shared.js` `watchPollEvery`, `engine/watches-store.js` |
|
|
22
22
|
| Two local documentation links targeted files that are not shipped | Removed the runtime `notes.md` link and the stale managed-spawn plan link | `docs/README.md`, `docs/managed-spawn.md` |
|
|
23
23
|
|
package/docs/engine-restart.md
CHANGED
|
@@ -10,11 +10,11 @@ When the engine restarts, it loses its in-memory process handles (`activeProcess
|
|
|
10
10
|
|
|
11
11
|
| State | Storage | Survives Restart |
|
|
12
12
|
|-------|---------|-----------------|
|
|
13
|
-
| Dispatch queue (pending/active/completed) | `engine/state.db` (`dispatches
|
|
13
|
+
| Dispatch queue (pending/active/completed) | `engine/state.db` (`dispatches`) | Yes |
|
|
14
14
|
| Agent status (working/idle/error) | Derived from `engine/state.db` | Yes |
|
|
15
15
|
| Agent live output | `agents/*/live-output.log` | Yes (mtime used for orphan cleanup) |
|
|
16
16
|
| Process handles (`ChildProcess`) | In-memory Map | **No** |
|
|
17
|
-
| Cooldown timestamps | In-memory Map |
|
|
17
|
+
| Cooldown timestamps | In-memory Map + `engine/state.db` | Yes (repopulated from SQL) |
|
|
18
18
|
|
|
19
19
|
## Protection Mechanisms
|
|
20
20
|
|
|
@@ -37,7 +37,20 @@ After the grace period expires, a dispatch with a tracked live process keeps run
|
|
|
37
37
|
|
|
38
38
|
If there is no live tracked process, the engine uses `live-output.log` mtime as indirect evidence. Once the log is stale for `engine.heartbeatTimeout`, the dispatch is treated as an orphan and marked failed.
|
|
39
39
|
|
|
40
|
-
### 3.
|
|
40
|
+
### 3. Intentional Restart Drains Pooled Leases
|
|
41
|
+
|
|
42
|
+
`engine.js stop` writes a shutdown request into `control.json`; it does not
|
|
43
|
+
tree-kill the daemon. The running engine stops dispatching new work and drains
|
|
44
|
+
fleet ACP-pool leases for up to `engine.shutdownTimeout` (5 minutes by
|
|
45
|
+
default). Cold-spawn agents are detached and are not part of this wait, so they
|
|
46
|
+
can survive for restart reattachment. A pooled lease cannot be reattached:
|
|
47
|
+
when the drain deadline expires it is cancelled and terminalized as a
|
|
48
|
+
retryable timeout before the old engine exits.
|
|
49
|
+
|
|
50
|
+
Unexpected crashes remain fail-safe: persisted `item.pooled` dispatches never
|
|
51
|
+
trust the worker PID and are reaped through the normal retry path on startup.
|
|
52
|
+
|
|
53
|
+
### 4. Stop Warning
|
|
41
54
|
|
|
42
55
|
`engine.js stop` checks for active dispatches and warns:
|
|
43
56
|
```
|
|
@@ -45,16 +58,15 @@ WARNING: 2 agent(s) are still working:
|
|
|
45
58
|
- Dallas: [office-bohemia] Build & test PR PR-4959092
|
|
46
59
|
- Rebecca: [office-bohemia] Review PR PR-4964594
|
|
47
60
|
|
|
48
|
-
|
|
49
|
-
On next start, they'll get a 20-min grace period before being marked as orphans.
|
|
61
|
+
Detached agents will continue for restart reattachment; pooled agents will drain before exit.
|
|
50
62
|
To kill them now, run: node engine.js kill
|
|
51
63
|
```
|
|
52
64
|
|
|
53
|
-
###
|
|
65
|
+
### 5. Exponential Backoff on Failures
|
|
54
66
|
|
|
55
67
|
If an agent is killed as an orphan and the work item retries, cooldowns use exponential backoff (2^failures, max 8x) to prevent spam-retrying broken tasks.
|
|
56
68
|
|
|
57
|
-
###
|
|
69
|
+
### 6. Crash-Loop Counter + Alert (W-mr2azk6i000y06e0)
|
|
58
70
|
|
|
59
71
|
The engine.js *process itself* (not an agent) can crash and get auto-respawned by three independent mechanisms: `engine/supervisor.js#checkEngine` (dead-PID), `engine/supervisor.js#checkEngineHung` (stale-heartbeat), and `engine/watchdog.js` (OS-scheduled external recovery); `dashboard.js`'s in-process 30s watchdog also auto-restarts on a dead PID (the user-triggered Restart Engine button is excluded). Each successful respawn calls `shared.recordEngineRespawn(source)`, which appends `{ts, source}` to `control.json`'s `engineRespawns[]` (rolling window, default `CRASH_LOOP_WINDOW_MS` = 10 min). Once respawns within the window reach `CRASH_LOOP_THRESHOLD` (default 3), `control.crashLoopAlert` is set (dashboard-visible via `/api/status`) and a deduped (one-per-day) `notes/inbox/` alert is written; the alert clears automatically once the window rolls past the incident. Override the window/threshold via `MINIONS_CRASH_LOOP_WINDOW_MS` / `MINIONS_CRASH_LOOP_THRESHOLD` env vars.
|
|
60
72
|
|
|
@@ -80,7 +92,7 @@ T+0s engine.js stop (warns about active agents)
|
|
|
80
92
|
Engine process exits. Agents keep running as OS processes.
|
|
81
93
|
|
|
82
94
|
T+30s Code changes made. engine.js start.
|
|
83
|
-
Engine reads dispatch
|
|
95
|
+
Engine reads SQL dispatch state — finds 2 active items.
|
|
84
96
|
Sets grace period: 20 min from now.
|
|
85
97
|
Logs: "2 active dispatch(es) from previous session"
|
|
86
98
|
|
package/docs/harness-mode.md
CHANGED
|
@@ -88,5 +88,5 @@ Within a single tick the standard scheduled-work dedup is keyed by `_scheduleId`
|
|
|
88
88
|
|
|
89
89
|
- Tri-agent items are **schedule-driven** — there's no manual "fire a harness mission" entry point. Add a schedule with `harness_mode: "tri_agent"` to opt in.
|
|
90
90
|
- Iteration pairs always reuse the original mission's artifact path, threshold, max-iterations, and rubric. The evaluator's verdict feedback is appended to the next generator's prompt.
|
|
91
|
-
- Mission state
|
|
91
|
+
- Mission work-item state uses the existing SQL store; mission artifacts remain file-backed. No new tables are required.
|
|
92
92
|
- Each iteration's evaluator is a separate work item, so dispatch retries, cooldowns, and steering apply normally to every role.
|
package/docs/managed-spawn.md
CHANGED
|
@@ -172,7 +172,7 @@ Single quotes (`'…'`) work the same way. No `powershell -Command "& '<path>' <
|
|
|
172
172
|
┌───────────────────────────────────────────────────────────────────┐
|
|
173
173
|
│ 3. spawnManagedSpec(spec) per spec │
|
|
174
174
|
│ - Uses the proven Windows detached-spawn pattern │
|
|
175
|
-
│ - Records each in
|
|
175
|
+
│ - Records each in the SQL managed-process store │
|
|
176
176
|
│ - Stdio → engine/managed-logs/<name>.log (append fd, NOT pipe) │
|
|
177
177
|
└───────────────────────────────────┬───────────────────────────────┘
|
|
178
178
|
│
|
|
@@ -241,7 +241,7 @@ The list endpoint returns an ETag; pass it as `If-None-Match` on follow-up polls
|
|
|
241
241
|
| Kill (PID terminated, removed from state) | `POST /api/managed-processes/kill` `{"name":"…"}` | Per-row "Kill" button |
|
|
242
242
|
| Restart (kill old PID, respawn from saved state, kick first healthcheck) | `POST /api/managed-processes/restart` `{"name":"…"}` | Per-row "Restart" button |
|
|
243
243
|
|
|
244
|
-
Killing a spec from outside Minions (raw `Stop-Process`) leaves a stale row
|
|
244
|
+
Killing a spec from outside Minions (raw `Stop-Process`) leaves a stale SQL row until the next 30-tick sweep notices the dead PID. Prefer the API.
|
|
245
245
|
|
|
246
246
|
### View logs
|
|
247
247
|
|
|
@@ -282,9 +282,9 @@ All knobs live under `engine.managedSpawn` in `engine/shared.js` (`ENGINE_DEFAUL
|
|
|
282
282
|
|
|
283
283
|
## Performance budget
|
|
284
284
|
|
|
285
|
-
- **Per-tick contribution**: zero unless the 30-tick sweep fires. The sweep is `O(N)` over specs
|
|
285
|
+
- **Per-tick contribution**: zero unless the 30-tick sweep fires. The sweep is `O(N)` over managed specs with one `process.kill(pid, 0)` per spec.
|
|
286
286
|
- **Healthcheck loops**: per-spec, self-scheduled (not tick-coupled). 10 specs × 1s pre-healthy cadence ⇒ 10 probes/s peak. Post-healthy drops to one probe per `healthBackoffSec` (30s).
|
|
287
|
-
- **
|
|
287
|
+
- **Write contention**: SQL updates batch on transitions (healthy ↔ unhealthy), and `last_health_at` only persists every `healthBackoffSec` to avoid flap. See [`test/perf/managed-spawn-load.test.js`](../test/perf/managed-spawn-load.test.js) for the 10-spec / 3-project load assertion.
|
|
288
288
|
- **Hard cap recommendation**: 50 specs across one engine. Above that, `process.kill(pid, 0)` shells out enough on Windows to dent tick latency.
|
|
289
289
|
|
|
290
290
|
## Source map
|
package/docs/onboarding.md
CHANGED
|
@@ -30,8 +30,7 @@ minions config set-cli claude
|
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
`minions init` creates `~/.minions/`. Primary migrated runtime state lives in
|
|
33
|
-
`engine/state.db` (SQLite, WAL mode).
|
|
34
|
-
`engine/metrics.json` are passive diagnostic mirrors; `engine/control.json`
|
|
33
|
+
`engine/state.db` (SQLite, WAL mode). Retired JSON mirrors are removed; `engine/control.json`
|
|
35
34
|
remains a live JSON control file.
|
|
36
35
|
|
|
37
36
|
## 2. Link a project
|
|
@@ -66,7 +66,7 @@ playbook contract. The poller is unchanged; the carve-out is purely additive.
|
|
|
66
66
|
- Validates `meta.pr_followup` shape (all four fields required, ≤ 512
|
|
67
67
|
chars each). Malformed → `400 { error: "<message>" }`.
|
|
68
68
|
- Checks for an existing follow-up with the same `parent_comment_id` in
|
|
69
|
-
the target project's
|
|
69
|
+
the target project's SQL work-item scope. Duplicate → `409 {
|
|
70
70
|
error: "followup_already_dispatched", existingWiId: "W-…" }` (no second WI
|
|
71
71
|
created).
|
|
72
72
|
- Persists the validated `meta.pr_followup`, and stores
|
|
@@ -104,7 +104,7 @@ playbook contract. The poller is unchanged; the carve-out is purely additive.
|
|
|
104
104
|
```
|
|
105
105
|
`engine/lifecycle.js processCompletionFollowups` logs each entry at `info`
|
|
106
106
|
and warns if the claimed `wi_id` is missing from any project's
|
|
107
|
-
|
|
107
|
+
SQL work-item store (catches reverted or non-landing dispatches).
|
|
108
108
|
|
|
109
109
|
## Server contract reference
|
|
110
110
|
|
|
@@ -131,7 +131,7 @@ Any missing/empty field, oversized field, or non-string value → `400` with a
|
|
|
131
131
|
descriptive message.
|
|
132
132
|
|
|
133
133
|
**Dedup.** A second `POST /api/work-items` with the same
|
|
134
|
-
`meta.pr_followup.parent_comment_id` in the same project's
|
|
134
|
+
`meta.pr_followup.parent_comment_id` in the same project's SQL scope
|
|
135
135
|
returns `409 { error: "followup_already_dispatched", existingWiId: "<id>" }`.
|
|
136
136
|
This is checked under the same `mutateWorkItems` lock used to insert the new
|
|
137
137
|
WI, so two pollers racing on the same comment cannot both win.
|
|
@@ -5,7 +5,7 @@ How the engine manages the lifecycle of a PR from creation through review, fix,
|
|
|
5
5
|
## 1. Implement agent creates PR
|
|
6
6
|
|
|
7
7
|
- Agent pushes code, output contains PR URL
|
|
8
|
-
- `syncPrsFromOutput()` (lifecycle.js) extracts URL
|
|
8
|
+
- `syncPrsFromOutput()` (lifecycle.js) extracts the URL, creates a SQL PR record, and links it to the work item via `addPrLink()`
|
|
9
9
|
|
|
10
10
|
## 2. Engine discovers PR needs review
|
|
11
11
|
|
|
@@ -87,8 +87,7 @@ additive.
|
|
|
87
87
|
### Persistence (survives restart)
|
|
88
88
|
|
|
89
89
|
- Dispatch state lives in **SQLite** `engine/state.db` `dispatches` table via
|
|
90
|
-
`engine/dispatch-store.js` (`readDispatchSectioned` / `applyDispatchMutation`)
|
|
91
|
-
mirrored read-only to `dispatch.json` (source: `engine/dispatch-store.js:49,244,298`).
|
|
90
|
+
`engine/dispatch-store.js` (`readDispatchSectioned` / `applyDispatchMutation`).
|
|
92
91
|
Each active dispatch is one JSON record. **Because `liveProjectsInUse` is
|
|
93
92
|
rebuilt from `dispatch.active` every tick, recording the claimed enlistment
|
|
94
93
|
path on the active dispatch record makes the per-path claim automatically
|
|
@@ -9,7 +9,7 @@ W-mpdad3mq000m53bb).
|
|
|
9
9
|
|
|
10
10
|
## Runbook location
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
`<MINIONS_DIR>/projects/<project-name>/runbooks/<runbook-id>.json`. Each entry:
|
|
13
13
|
`{ id, name, target, steps, expectedArtifacts, createdAt, createdBy }`.
|
|
14
14
|
CRUD via `GET/POST /api/qa/runbooks` (POST returns the new runbook with
|
|
15
15
|
engine-assigned `id`). `target` is a name from `/api/managed-processes` or
|
|
@@ -97,7 +97,7 @@ plain prose, the engine sets up a live target, an agent drafts a
|
|
|
97
97
|
runner-native test file, and (with user approval) a second agent executes
|
|
98
98
|
it. Sessions are a thin orchestration layer on top of the same
|
|
99
99
|
`managed-spawn` + `qa-run-result.json` infrastructure that powers
|
|
100
|
-
runbooks above — they reuse
|
|
100
|
+
runbooks above — they reuse the SQL QA-run store, `engine/qa-artifacts/`, and
|
|
101
101
|
the existing `engine/lifecycle.js#runPostCompletionHooks` qa-run sidecar
|
|
102
102
|
hook. Surfaced on `/qa` (sessions card list above the runbooks/runs
|
|
103
103
|
tables) and proxied by the Command Center natural-language shortcut.
|
|
@@ -161,7 +161,7 @@ routing default):
|
|
|
161
161
|
**Multi-project fan-out (W-mpq6xqzj000606d0):** when `spec.projects`
|
|
162
162
|
contains more than one project name, SETUP is fanned out — one work item
|
|
163
163
|
per project, queued in parallel into each project's own
|
|
164
|
-
|
|
164
|
+
the target project's SQL work-item scope. The first project (`spec.projects[0]`) is the
|
|
165
165
|
**primary** (`meta.qaSession.primary === true`); the rest are
|
|
166
166
|
**co-services** (`primary === false`). Each WI's
|
|
167
167
|
`meta.qaSession.coServices` lists the co-service project names (only the
|
|
@@ -230,7 +230,7 @@ errors are mapped to HTTP via `_qaSessionsErrorToStatus`:
|
|
|
230
230
|
|
|
231
231
|
## File locations
|
|
232
232
|
|
|
233
|
-
- **Session state**: `engine/
|
|
233
|
+
- **Session state**: `engine/state.db` `qa_sessions` (all projects,
|
|
234
234
|
capped at `QA_SESSIONS_MAX_RECORDS = 500` via `createSession`-time
|
|
235
235
|
rotation by `createdAt`).
|
|
236
236
|
- **Drafted test files**: `engine/qa-tests/<sessionId>/test.<ext>`. Created
|
package/docs/qa-runbooks.md
CHANGED
|
@@ -11,7 +11,7 @@ Runbooks are per-project test plans. Each runbook is a single JSON file at:
|
|
|
11
11
|
<MINIONS_DIR>/projects/<project-name>/runbooks/<runbook-id>.json
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
This
|
|
14
|
+
This follows the project-scoped PR-store precedent — anything
|
|
15
15
|
scoped to a single project lives under its `projects/<name>/` state dir
|
|
16
16
|
rather than a root-level `runbooks/` directory. Two reasons:
|
|
17
17
|
|
|
@@ -99,7 +99,7 @@ unlink).
|
|
|
99
99
|
|
|
100
100
|
The deferred follow-up items (W-mpeiwz6k0005bf34-b/c/d) have since landed. Brief pointers — see [CLAUDE.md](../CLAUDE.md) → "QA validation runs" for the deep dive:
|
|
101
101
|
|
|
102
|
-
- **Run dispatch + persistence** (`engine/qa-runs.js`): `POST /api/qa/runbooks/run` creates a
|
|
102
|
+
- **Run dispatch + persistence** (`engine/qa-runs.js`): `POST /api/qa/runbooks/run` creates a SQL QA-run record with `status ∈ pending|dispatched|running|passed|failed|error` and dispatches a `qa-validate` work item against the runbook's `target`. Read via `GET /api/qa/runs?limit=N&status=...` and `GET /api/qa/runs/<id>`.
|
|
103
103
|
- **Artifact contract**: the `qa-validate` agent writes `agents/<id>/qa-run.json` before exit; the engine copies listed files into `engine/qa-artifacts/<runId>/` and serves them via `GET /api/qa/artifacts/<runId>/<file>` (path-traversal-gated, 403 on escape). Per-file size cap: `engine.qaArtifactsMaxBytes`.
|
|
104
104
|
- **UI**: `/qa` dashboard page (`dashboard/pages/qa.html`, `dashboard/js/qa.js`) polls `GET /api/qa/runs` every 5s while active; auto-detects screenshots/videos/logs for inline preview.
|
|
105
105
|
- **Playbook**: `playbooks/qa-validate.md` (routed via the synthetic `qa-validate` task-type in `routing.md`).
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# RFC: `completion.json` — Structured Agent Control-Plane Protocol
|
|
2
2
|
|
|
3
|
-
> Author: Dallas (Engineer) | Date: 2026-04-27 | Status: **
|
|
3
|
+
> Author: Dallas (Engineer) | Date: 2026-04-27 | Status: **historical design record**
|
|
4
4
|
> Plan: `minions-2026-04-27.json` | Plan item: `P-7a8b9c1d` (SEC-07 / C1)
|
|
5
5
|
> Note: this RFC lives in `docs/` rather than `plans/` because the repo's `.gitignore` excludes `plans/`. The task description explicitly allowed "or similar".
|
|
6
|
+
> Storage references below describe the pre-SQL implementation evaluated by the
|
|
7
|
+
> RFC. Current completion processing writes one-shot sidecars and persists runtime
|
|
8
|
+
> records in `engine/state.db`.
|
|
6
9
|
|
|
7
10
|
## TL;DR
|
|
8
11
|
|
package/docs/runtime-adapters.md
CHANGED
|
@@ -139,7 +139,7 @@ process or a pooled lease. Assignment strategy, in order:
|
|
|
139
139
|
4. Otherwise the request queues FIFO until a `release()` or worker crash frees
|
|
140
140
|
a slot.
|
|
141
141
|
|
|
142
|
-
Dispatch records persist `item.pooled = true`
|
|
142
|
+
Dispatch records persist `item.pooled = true` in SQLite, not
|
|
143
143
|
just the in-memory `activeProcesses._pooled` flag (which is wiped on engine
|
|
144
144
|
restart) — the on-disk flag is what tells the restart-reattach path
|
|
145
145
|
(`engine/cli.js`) that a live worker PID is a pooled lease, not a resumable
|
package/docs/self-improvement.md
CHANGED
|
@@ -12,7 +12,7 @@ Agent completes task
|
|
|
12
12
|
├─ 1. Learnings Inbox → notes.md (all future agents see it)
|
|
13
13
|
├─ 2. Per-Agent History → history.md (agent sees its own past)
|
|
14
14
|
├─ 3. Review Feedback Loop → author gets reviewer's findings
|
|
15
|
-
└─ 4. Quality Metrics → engine/
|
|
15
|
+
└─ 4. Quality Metrics → engine/state.db (tracks performance)
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
## 1. Learnings Inbox → notes.md
|
|
@@ -182,7 +182,7 @@ Human comments on PR with "@minions fix the error handling here"
|
|
|
182
182
|
|
|
183
183
|
## 5. Quality Metrics
|
|
184
184
|
|
|
185
|
-
The engine tracks per-agent performance metrics in
|
|
185
|
+
The engine tracks per-agent performance metrics in the SQL metrics store. It updates them after every task completion and PR review.
|
|
186
186
|
|
|
187
187
|
### Metrics tracked
|
|
188
188
|
|
|
@@ -330,7 +330,7 @@ When a git merge or rebase produces conflicts in yarn.lock.
|
|
|
330
330
|
| Stale flag auto-reset | 5 min (300,000ms) | `_consolidationInFlight` flag auto-resets after this duration to unblock future runs |
|
|
331
331
|
| notes.md max size | 50KB | Trims to cap above this; overflow archived to `notes/archive/notes-overflow.md` (not discarded) |
|
|
332
332
|
| Agent history entries | 20 | Max entries kept in history.md |
|
|
333
|
-
| Metrics
|
|
333
|
+
| Metrics store | `engine/state.db` (`metrics`) | Created by database migrations |
|
|
334
334
|
|
|
335
335
|
## Files
|
|
336
336
|
|
|
@@ -340,6 +340,5 @@ When a git merge or rebase produces conflicts in yarn.lock.
|
|
|
340
340
|
| `notes/archive/*.md` | Archived inbox files | Engine (consolidation) |
|
|
341
341
|
| `notes.md` | Accumulated team knowledge | Engine (consolidation) |
|
|
342
342
|
| `agents/<name>/history.md` | Per-agent task history | Engine (post-completion) |
|
|
343
|
-
| `engine/
|
|
343
|
+
| `engine/state.db` `metrics` | Quality metrics per agent | Engine (post-completion + review) |
|
|
344
344
|
| `notes/inbox/feedback-*.md` | Review feedback for authors | Engine (post-review) |
|
|
345
|
-
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# `engine/shared.js` / `engine/lifecycle.js` module-boundary audit
|
|
2
2
|
|
|
3
|
-
**Status:**
|
|
3
|
+
**Status:** historical point-in-time audit. It predates the SQL-only state cutover cleanup;
|
|
4
|
+
removed JSON-routing helpers and old line numbers below are retained only as audit evidence, not
|
|
5
|
+
as the current API contract. Use `engine/shared.js` and `engine/lifecycle.js` for current symbols.
|
|
4
6
|
|
|
5
7
|
**Scope:** `engine/shared.js` (9,877 lines, 338 top-level functions) and `engine/lifecycle.js`
|
|
6
8
|
(6,988 lines, 121 top-level functions) as of this audit's HEAD commit. The PRD item that requested
|
|
@@ -81,7 +81,7 @@ shape:
|
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
Plus `/api/events?since=…&limit=…` for paged reads. The engine already writes most of these
|
|
84
|
-
moments to
|
|
84
|
+
moments to the SQL log store; we just need to enrich them and expose
|
|
85
85
|
them. The History panel becomes one fetch.
|
|
86
86
|
|
|
87
87
|
**Cost.** ~1.5 days. The hard part is catching every event-emit site — dispatch start, dispatch
|
|
@@ -98,8 +98,7 @@ each lifecycle path emits an event.
|
|
|
98
98
|
**Problem.** Carlos called out "Work Items" and "Pipelines" as conceptually overlapping in the
|
|
99
99
|
existing UI. Looking at the data: a Pipeline is a sequence of stages where each stage *is*
|
|
100
100
|
either a Work Item, a Meeting, or a Plan. So a Pipeline isn't really a separate concept — it's
|
|
101
|
-
a Work Item with a stage list. Yet they have
|
|
102
|
-
(`engine/dispatch.json` vs `engine/pipeline-runs.json`), separate API surfaces
|
|
101
|
+
a Work Item with a stage list. Yet they have separate SQL tables, separate API surfaces
|
|
103
102
|
(`/api/work-items*` vs `/api/pipelines*`), and separate UIs.
|
|
104
103
|
|
|
105
104
|
**Proposal.** Don't merge the *storage* — that's a deep migration. Instead, introduce a
|
|
@@ -199,11 +198,11 @@ becomes a "freshness" badge on the Knowledge entry rather than a separate tab.
|
|
|
199
198
|
|
|
200
199
|
**Problem.** Schedule = "fire on a cron pattern." Watch = "fire when a condition flips." They
|
|
201
200
|
share most of the lifecycle (definition, fire history, pause/resume, expire/stopAfter). The
|
|
202
|
-
two
|
|
201
|
+
two SQL stores and parallel CRUD APIs add maintenance
|
|
203
202
|
without adding real concepts.
|
|
204
203
|
|
|
205
|
-
**Proposal.** Generalize to **Trigger**, with two `kind`s: `cron` and `event`. Backend
|
|
206
|
-
|
|
204
|
+
**Proposal.** Generalize to **Trigger**, with two `kind`s: `cron` and `event`. Backend tables
|
|
205
|
+
stay separate, but the API and UI present them as one.
|
|
207
206
|
|
|
208
207
|
```jsonc
|
|
209
208
|
{
|