@yemi33/minions 0.1.2379 → 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/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-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/ado-status.js +5 -5
- package/engine/ado.js +20 -25
- package/engine/agent-worker-pool.js +58 -1
- 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/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 +147 -343
- 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
|
@@ -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
|
{
|
package/docs/slim-ux/concepts.md
CHANGED
|
@@ -33,7 +33,7 @@ Watches, etc. It is the primary human-to-fleet interface.
|
|
|
33
33
|
|
|
34
34
|
**Where it lives.**
|
|
35
35
|
- System prompt: `prompts/cc-system.md`
|
|
36
|
-
- Per-tab session state: `engine/
|
|
36
|
+
- Per-tab and global session state: `engine/state.db` (`cc_sessions`).
|
|
37
37
|
- Direct CLI invocation: `engine/llm.js` `callLLM({ direct: true })` — bypasses
|
|
38
38
|
`spawn-agent.js` and runs the runtime CLI itself.
|
|
39
39
|
|
|
@@ -70,10 +70,8 @@ Plan, Note, Schedule, Watch, Knowledge entry, Pinned note). Per-call cap is
|
|
|
70
70
|
the home page eventually reduce to a Work Item.
|
|
71
71
|
|
|
72
72
|
**Where it lives.**
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
`engine/shared.js` line ~1530).
|
|
76
|
-
- In-flight copy: `engine/dispatch.json` (`pending[] / active[] / completed[]`).
|
|
73
|
+
- Durable records: `engine/state.db` `work_items`, keyed by central/project scope.
|
|
74
|
+
- In-flight dispatch rows: `engine/state.db` `dispatches`.
|
|
77
75
|
|
|
78
76
|
**Backing classes / functions.**
|
|
79
77
|
- Status constants: `engine/shared.js` line 1348 (`WI_STATUS`).
|
|
@@ -327,7 +325,7 @@ engine drops a Work Item into the queue.
|
|
|
327
325
|
|
|
328
326
|
**Where it lives.**
|
|
329
327
|
- Definitions: `config.json` `schedules: []` (3-field cron `min hour dow`).
|
|
330
|
-
- Last-run history: `engine/
|
|
328
|
+
- Last-run history: `engine/state.db` `schedule_runs`.
|
|
331
329
|
|
|
332
330
|
**Backing classes / functions.**
|
|
333
331
|
- Cron parser: `engine/scheduler.js`
|
|
@@ -361,7 +359,7 @@ analogous to **Watch** (event-driven instead of time-driven).
|
|
|
361
359
|
(merged, build passes, build fails, status changes, new comments, vote changes). Watches are
|
|
362
360
|
event-driven, schedules are time-driven.
|
|
363
361
|
|
|
364
|
-
**Where it lives.** `engine/
|
|
362
|
+
**Where it lives.** `engine/state.db` `watches`.
|
|
365
363
|
|
|
366
364
|
**Backing classes / functions.**
|
|
367
365
|
- Status constants: `engine/shared.js` line 1380 (`WATCH_STATUS`).
|
|
@@ -389,11 +387,11 @@ A merged-PR watch is the canonical "tell me when X ships" primitive.
|
|
|
389
387
|
|
|
390
388
|
**What it is.** A multi-stage workflow. Stages can be a Work Item (`task`), a `meeting`, or a
|
|
391
389
|
`plan`. Stages can declare dependencies on previous stages. Pipelines run via triggers (manual,
|
|
392
|
-
schedule, watch) and persist
|
|
390
|
+
schedule, watch) and persist run state in SQLite.
|
|
393
391
|
|
|
394
392
|
**Where it lives.**
|
|
395
393
|
- Definitions: `pipelines/*.json` (one file per pipeline).
|
|
396
|
-
- Run state: `engine/
|
|
394
|
+
- Run state: `engine/state.db` `pipeline_runs`.
|
|
397
395
|
|
|
398
396
|
**Backing classes / functions.**
|
|
399
397
|
- Dirs: `engine/pipeline.js` lines 19–24 (`PIPELINES_DIR`, `PIPELINE_RUNS_PATH`, `PLANS_DIR`,
|
|
@@ -427,7 +425,7 @@ unrelated to ADO/GitHub Actions pipelines, even though the word collides.
|
|
|
427
425
|
dispatch holds the agent's PID, prompt sidecar path, worktree path, and current status until the
|
|
428
426
|
process exits.
|
|
429
427
|
|
|
430
|
-
**Where it lives.**
|
|
428
|
+
**Where it lives.** Active rows in `engine/state.db` `dispatches`.
|
|
431
429
|
|
|
432
430
|
**Backing classes / functions.**
|
|
433
431
|
- `mutateDispatch`: `engine/dispatch.js` line 60 (the only safe RMW path).
|
|
@@ -450,7 +448,7 @@ tick. Read shape via `/api/status` (line 6838).
|
|
|
450
448
|
|
|
451
449
|
## 13. Dispatch Queue
|
|
452
450
|
|
|
453
|
-
**What it is.** The
|
|
451
|
+
**What it is.** The sectioned view reconstructed from SQL dispatch rows:
|
|
454
452
|
|
|
455
453
|
- `pending[]` — admitted Work Items waiting on agent / dependency / cooldown / budget.
|
|
456
454
|
- `active[]` — currently spawned.
|
|
@@ -460,7 +458,7 @@ The queue's invariants are: max concurrent (default 5), per-PR / per-branch lock
|
|
|
460
458
|
agents fighting on the same branch, dependency-aware spawning (`depends_on` IDs must be done
|
|
461
459
|
first), retry on failure up to `ENGINE_DEFAULTS.maxRetries`.
|
|
462
460
|
|
|
463
|
-
**Where it lives.** `engine/
|
|
461
|
+
**Where it lives.** `engine/state.db` `dispatches`.
|
|
464
462
|
|
|
465
463
|
**Backing classes / functions.** All in `engine/dispatch.js`. Mutation only via `mutateDispatch`
|
|
466
464
|
line 60. Queue dedupe keys: `getDispatchProjectKey` line 80, `getPrDispatchTargetKey` line 85,
|
|
@@ -502,12 +500,11 @@ record). It also feeds **Pull Request** sync (`pr` field).
|
|
|
502
500
|
|
|
503
501
|
## 15. Pull Request (as tracked in minions)
|
|
504
502
|
|
|
505
|
-
**What it is.** A row in
|
|
503
|
+
**What it is.** A project-scoped row in the SQL PR store. Tracks status, vote tally, build
|
|
506
504
|
state, review verdict, and the work item that created it. *Not* the GitHub/ADO PR itself —
|
|
507
505
|
minions polls the host and writes a sanitized snapshot here.
|
|
508
506
|
|
|
509
|
-
**Where it lives.** `
|
|
510
|
-
line 1546). Cross-project links also tracked in `engine/pr-links.json`.
|
|
507
|
+
**Where it lives.** `engine/state.db` `pull_requests`; cross-project links use `pr_links`.
|
|
511
508
|
|
|
512
509
|
**Backing classes / functions.**
|
|
513
510
|
- Status / pollable constants: `engine/shared.js` line 1372 (`PR_STATUS`,
|
|
@@ -541,7 +538,7 @@ Examples: Ripley (architect / explorer), Dallas (engineer), Lambert (analyst), R
|
|
|
541
538
|
- Default roster: `engine/shared.js` line 1486 (`DEFAULT_AGENTS`).
|
|
542
539
|
- Charter: `agents/<id>/charter.md` — read by `getAgentCharter` (`engine/queries.js` line 505).
|
|
543
540
|
- Per-agent config (cli, model, budget, skills): `config.json` `agents.<id>`.
|
|
544
|
-
- Metrics: `engine/
|
|
541
|
+
- Metrics: `engine/state.db` `metrics` (per-agent token / cost / quality / runtime).
|
|
545
542
|
|
|
546
543
|
**Backing classes / functions.**
|
|
547
544
|
- Charter loader: `engine/queries.js` line 505.
|
|
@@ -569,14 +566,14 @@ gets which work type.
|
|
|
569
566
|
|
|
570
567
|
**Where it lives.**
|
|
571
568
|
- Definitions: `config.json` `projects: []`.
|
|
572
|
-
-
|
|
569
|
+
- Runtime records: project-scoped rows in `engine/state.db`.
|
|
573
570
|
- Removal artifact: `projects/.archived/<name>-YYYYMMDD/`.
|
|
574
571
|
|
|
575
572
|
**Backing classes / functions.**
|
|
576
573
|
- Helpers: `engine/shared.js`
|
|
577
|
-
- `getProjects`
|
|
578
|
-
- `
|
|
579
|
-
- `
|
|
574
|
+
- `getProjects`
|
|
575
|
+
- `stateScope`, `readWorkItems`, `readPullRequests`
|
|
576
|
+
- `projectStateDir` for file-backed project definitions/artifacts.
|
|
580
577
|
- Removal: `engine/projects.js` `removeProject` (canonical teardown — never edit `config.json`
|
|
581
578
|
directly).
|
|
582
579
|
|
|
@@ -658,13 +655,14 @@ consolidates the **Inbox**, and so on. It's also the gatekeeper for control stat
|
|
|
658
655
|
|
|
659
656
|
**Where it lives.**
|
|
660
657
|
- Process: `engine.js` (root).
|
|
661
|
-
- State: `engine/
|
|
662
|
-
|
|
658
|
+
- State: `engine/state.db` (logs, metrics, queues, watches, schedules, PRDs) plus
|
|
659
|
+
`engine/control.json` for process coordination.
|
|
663
660
|
|
|
664
661
|
**Backing classes / functions.**
|
|
665
662
|
- Tick: `engine.js` (top-level `tick()`).
|
|
666
663
|
- Defaults: `engine/shared.js` line 860 (`ENGINE_DEFAULTS`).
|
|
667
|
-
- Concurrency
|
|
664
|
+
- Concurrency: SQLite transactions for runtime records; file locks only for intentionally
|
|
665
|
+
file-backed definitions and coordination sidecars.
|
|
668
666
|
- Restart re-attach: PID files in `engine/tmp/pid-<id>.pid` + `live-output.log` mtimes,
|
|
669
667
|
20-min grace.
|
|
670
668
|
|
|
@@ -839,6 +837,6 @@ Pipeline (stages: task / meeting / plan) ──► spawns Work Items / Meetings
|
|
|
839
837
|
Schedule (cron) ──► spawns Work Items
|
|
840
838
|
Watch (event) ──► spawns notifications / Work Items / pipeline-continuations
|
|
841
839
|
|
|
842
|
-
Engine reads: routing.md, config.json,
|
|
843
|
-
Engine writes: control.json,
|
|
840
|
+
Engine reads: routing.md, config.json, definitions, engine/state.db
|
|
841
|
+
Engine writes: control.json, artifacts, engine/state.db
|
|
844
842
|
```
|
package/docs/watches.md
CHANGED
|
@@ -4,7 +4,7 @@ Persistent monitoring jobs that fire inbox notifications and follow-up actions w
|
|
|
4
4
|
|
|
5
5
|
## What a Watch Is
|
|
6
6
|
|
|
7
|
-
A watch is a record persisted in `engine/state.db
|
|
7
|
+
A watch is a record persisted in `engine/state.db`. It binds:
|
|
8
8
|
|
|
9
9
|
| Field | Purpose |
|
|
10
10
|
|--------------|-------------------------------------------------------------------------|
|
|
@@ -130,7 +130,7 @@ module.exports = {
|
|
|
130
130
|
|
|
131
131
|
Resolution is `path.join(shared.MINIONS_DIR, 'watches.d')` so it works in both dev checkouts and installed `~/.minions/` layouts. Missing folder is silent; per-file failures are isolated (load errors log a `WARN` line, don't abort boot). Tests can re-scan via the exported `_loadPluginTargetTypes()`.
|
|
132
132
|
|
|
133
|
-
**Canonical plugin example:** [`watches.d/http.js`](../watches.d/http.js) (W-mp7i22mu00191b07) ships a generic `http` target type — point a watch at any URL or `{ url, method, headers, body, extract, expected, regex, timeoutMs }` config and the plugin exposes `status-change`, `value-change`, `value-equals`, `value-matches`, and `http-error` conditions. Auth headers support `{{env.GH_TOKEN}}`-style substitution at fetch time (secrets are never
|
|
133
|
+
**Canonical plugin example:** [`watches.d/http.js`](../watches.d/http.js) (W-mp7i22mu00191b07) ships a generic `http` target type — point a watch at any URL or `{ url, method, headers, body, extract, expected, regex, timeoutMs }` config and the plugin exposes `status-change`, `value-change`, `value-equals`, `value-matches`, and `http-error` conditions. Auth headers support `{{env.GH_TOKEN}}`-style substitution at fetch time (secrets are never persisted in watch records). All conditions are change-based (`absoluteConditions: []`).
|
|
134
134
|
|
|
135
135
|
## Tick Integration
|
|
136
136
|
|
|
@@ -222,10 +222,10 @@ CC will discover the endpoints from `GET /api/routes` and `POST /api/watches` wi
|
|
|
222
222
|
|
|
223
223
|
When a watch fires:
|
|
224
224
|
|
|
225
|
-
-
|
|
225
|
+
- The SQL watch record gets `triggerCount++`, `last_triggered=<ISO>`, `_lastTriggerMessage=<message>`, and a refreshed `_lastState`.
|
|
226
226
|
- An inbox file lands at `notes/inbox/<owner>/watch-<watchId>-<n>.md` (when `notify === 'inbox'`).
|
|
227
227
|
- 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.
|
|
228
|
-
-
|
|
228
|
+
- The SQL engine log gets `Watch triggered: <id> — <message>` and `Watch <id> action <type>: <summary>`.
|
|
229
229
|
|
|
230
230
|
Absolute conditions firing under `stopAfter === 0` flip `status` to `expired`; `stopAfter > 0` flips it to `expired` once `triggerCount >= stopAfter`.
|
|
231
231
|
|
|
@@ -236,14 +236,14 @@ Absolute conditions firing under `stopAfter === 0` flip `status` to `expired`; `
|
|
|
236
236
|
| 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 |
|
|
237
237
|
| `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 |
|
|
238
238
|
| `"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[]` |
|
|
239
|
-
| 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
|
|
239
|
+
| 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`)*. If you see this, inspect the persisted watch through the API |
|
|
240
240
|
| Absolute watch fires forever instead of once | `stopAfter` is set to a non-zero value; only `stopAfter === 0` triggers fire-once expiration |
|
|
241
241
|
| 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 |
|
|
242
242
|
| `_lastActionResult.ok === false` with `"unknown action type"` | The `action.type` isn't registered. List with `listActionTypes()` / `GET /api/watches/action-types` |
|
|
243
243
|
| 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` `WEBHOOK` handler)* |
|
|
244
|
-
| Trigger fires but follow-up `dispatch-work-item` is missing | Check the engine log for `Watch <id> action <type>: <summary>`. Common reasons: missing `title`,
|
|
244
|
+
| Trigger fires but follow-up `dispatch-work-item` is missing | Check the engine log for `Watch <id> action <type>: <summary>`. Common reasons: missing `title`, a project-scope write failed, or the WI landed in the central scope because no project was specified |
|
|
245
245
|
| Watch `_lastActionResult` shows `"timeout"` for webhook | Webhooks have a 10s safety timeout to keep the watches tick fast *(source: `engine/watch-actions.js:482-484`)* |
|
|
246
|
-
| `checkWatches` block crashes silently | Wrapped in `safe('checkWatches', ...)` so one failure doesn't abort the tick
|
|
246
|
+
| `checkWatches` block crashes silently | Wrapped in `safe('checkWatches', ...)` so one failure doesn't abort the tick. Inspect the engine log for `Watch check error (<id>)` lines. Regression #1088: the block must use `getProjects(config)`, never the long-removed `PROJECTS` constant |
|
|
247
247
|
|
|
248
248
|
## See Also
|
|
249
249
|
|
|
@@ -84,7 +84,7 @@ const { MEMORY_SCOPES, WORKSPACE_MANIFEST_DEFAULTS,
|
|
|
84
84
|
|
|
85
85
|
## Audit trail
|
|
86
86
|
|
|
87
|
-
Because rejections route through the standard `completeDispatch(... ERROR ...)` path, the existing dispatch + inbox + dashboard timeline machinery already surfaces every manifest rejection. No new
|
|
87
|
+
Because rejections route through the standard `completeDispatch(... ERROR ...)` path, the existing dispatch + inbox + dashboard timeline machinery already surfaces every manifest rejection. No new table or event topic was added. Per-agent audit can be derived from SQL dispatch records filtered by `failure_class === 'workspace-manifest-repo-forbidden'`.
|
|
88
88
|
|
|
89
89
|
## Rollout guidance
|
|
90
90
|
|
|
@@ -45,7 +45,7 @@ recycle worktree dirs across branches.
|
|
|
45
45
|
- **Return** in `onAgentClose` BEFORE `completeDispatch`:
|
|
46
46
|
`git reset --hard HEAD` → `git clean -fd` → `git fetch origin <main>`
|
|
47
47
|
→ `git checkout --detach origin/<main>` → mark IDLE.
|
|
48
|
-
- **State**
|
|
48
|
+
- **State** in SQLite (`worktree_pool`); git ops outside the transaction.
|
|
49
49
|
|
|
50
50
|
## Ownership marker is never "dirty" (#284)
|
|
51
51
|
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* The version-gated one-shot model gives us exactly what we need: re-probe
|
|
21
21
|
* once per release that bumps `ENGINE_DEFAULTS.abandonedReconciliationVersion`,
|
|
22
22
|
* then never again until the next bump. First boot after this lands cleans up
|
|
23
|
-
* the historical false-flips; subsequent boots are a single state
|
|
23
|
+
* the historical false-flips; subsequent boots are a single state-marker read +
|
|
24
24
|
* version compare → no-op.
|
|
25
25
|
*
|
|
26
26
|
* ## Why a separate WI from W-mp5trwh60008386d?
|
|
@@ -37,14 +37,13 @@
|
|
|
37
37
|
* Errors here must NOT block boot — the catch in cli.js logs and continues.
|
|
38
38
|
*/
|
|
39
39
|
|
|
40
|
-
const path = require('path');
|
|
41
40
|
const shared = require('./shared');
|
|
42
41
|
|
|
43
42
|
const { ENGINE_DEFAULTS, log, mutateEngineState, readEngineState } = shared;
|
|
44
43
|
|
|
45
44
|
/**
|
|
46
45
|
* Run the version-gated startup reconciliation pass. Idempotent across reruns
|
|
47
|
-
* at the same code version: state
|
|
46
|
+
* at the same code version: persisted engine state gates re-execution.
|
|
48
47
|
*
|
|
49
48
|
* @param {object} config — engine config (used to enumerate projects)
|
|
50
49
|
* @param {object} [options]
|
|
@@ -82,7 +81,7 @@ async function runStartupReconciliation(config, options = {}) {
|
|
|
82
81
|
const projects = shared.getProjects(config);
|
|
83
82
|
projectCount = projects.length;
|
|
84
83
|
for (const p of projects) {
|
|
85
|
-
const prs = shared.
|
|
84
|
+
const prs = shared.readPullRequests(p);
|
|
86
85
|
preCount += prs.filter(pr =>
|
|
87
86
|
pr.status === shared.PR_STATUS.ABANDONED && !pr._reconciliation404Confirmed
|
|
88
87
|
).length;
|
|
@@ -128,7 +127,7 @@ async function runStartupReconciliation(config, options = {}) {
|
|
|
128
127
|
return state;
|
|
129
128
|
});
|
|
130
129
|
} catch (err) {
|
|
131
|
-
//
|
|
130
|
+
// Marker persistence failure is non-fatal: next boot will
|
|
132
131
|
// re-run the pass, which is idempotent (already-flipped PRs aren't
|
|
133
132
|
// abandoned anymore, confirmed-404s carry the marker). Log and continue.
|
|
134
133
|
log('warn', `Abandoned PR reconciliation: failed to persist version marker: ${err.message}`);
|