@yemi33/minions 0.1.2363 → 0.1.2365

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1286,6 +1286,9 @@ async function _ccDoSend(message, skipUserMsg, forceTabId, intentMetadata) {
1286
1286
  updateStreamDiv();
1287
1287
  } else if (evt.type === 'heartbeat') {
1288
1288
  return;
1289
+ } else if (evt.type === 'status') {
1290
+ streamStatusNote = evt.message || '';
1291
+ updateStreamDiv();
1289
1292
  } else if (evt.type === 'tool') {
1290
1293
  ccSegmentsApplyTool(segments, { name: evt.name, input: evt.input || {}, id: evt.id || null });
1291
1294
  if (activeTab) activeTab._segments = segments;
package/dashboard.js CHANGED
@@ -2644,15 +2644,14 @@ function _getMtimes() {
2644
2644
  return result;
2645
2645
  }
2646
2646
 
2647
- // Reset the per-source caches whose TTL would otherwise serve stale data
2648
- // across rebuilds. Skill files (`queries._skillsCache` 30s) and MCP servers
2649
- // (`_mcpServersCache` 5min) are the two with TTLs longer than our poll
2650
- // cadence, and both back slices the user sees on screen. Called on every
2651
- // mtime-detected rebuild so the rebuild always reads fresh disk state.
2647
+ // Reset per-source caches whose TTL would otherwise survive an mtime-triggered
2648
+ // status rebuild and serve stale data.
2652
2649
  function _invalidateInnerCachesForRebuild() {
2653
2650
  try { queries.invalidateSkillsCache(); } catch { /* optional */ }
2654
2651
  _mcpServersCache = null;
2655
2652
  _mcpServersCacheTs = 0;
2653
+ _diskVersionCache = null;
2654
+ _diskVersionCacheTs = 0;
2656
2655
  }
2657
2656
 
2658
2657
  function _mtimesChanged(prev, curr) {
@@ -3706,6 +3705,7 @@ function _ensureCcLiveStream(tabId) {
3706
3705
  tabId,
3707
3706
  text: '',
3708
3707
  tools: [],
3708
+ statusMessage: '',
3709
3709
  donePayload: null,
3710
3710
  writer: null,
3711
3711
  endResponse: null,
@@ -6341,6 +6341,11 @@ const server = http.createServer(async (req, res) => {
6341
6341
  if (!wiPath) {
6342
6342
  const prdFile = body.prdFile;
6343
6343
  if (!prdFile) return jsonReply(res, 404, { error: 'work item not found in any source' });
6344
+ // W-mrdykt9m0006e459 — prdFile is unauthenticated client input; reject
6345
+ // traversal/absolute paths and anything that resolves outside PRD_DIR
6346
+ // before it ever reaches a filesystem read.
6347
+ try { shared.sanitizePath(prdFile, PRD_DIR); }
6348
+ catch { return jsonReply(res, 400, { error: 'invalid prdFile path' }); }
6344
6349
 
6345
6350
  // Look up PRD item to create a new work item on-demand.
6346
6351
  // safeJsonNoRestore — PRDs are terminal artifacts; a stale .backup
@@ -10048,6 +10053,14 @@ What would you like to discuss or change? When you're happy, say "approve" and I
10048
10053
  if (!hasImages && shared.resolveCcUseWorkerPool(engineConfig)) {
10049
10054
  return _invokeCcStreamViaPool({ prompt, liveState, toolUses, model, effort, engineConfig, systemPrompt, tabId });
10050
10055
  }
10056
+ const setStatus = (message) => {
10057
+ liveState.statusMessage = message;
10058
+ if (liveState.writer) liveState.writer({ type: 'status', message });
10059
+ };
10060
+ if (hasImages) {
10061
+ _touchCcLiveStream(liveState);
10062
+ setStatus('Looking at the image...');
10063
+ }
10051
10064
  const { callLLMStreaming } = require('./engine/llm');
10052
10065
  return callLLMStreaming(prompt, systemPrompt, {
10053
10066
  timeout: CC_CALL_TIMEOUT_MS, label: 'command-center', model, maxTurns,
@@ -10056,11 +10069,17 @@ What would you like to discuss or change? When you're happy, say "approve" and I
10056
10069
  engineConfig, images,
10057
10070
  onChunk: (text, segmentId) => {
10058
10071
  _touchCcLiveStream(liveState);
10072
+ if (liveState.statusMessage) {
10073
+ setStatus('');
10074
+ }
10059
10075
  liveState.text = text;
10060
10076
  if (liveState.writer) liveState.writer({ type: 'chunk', text, segmentId });
10061
10077
  },
10062
10078
  onToolUse: (name, input, id) => {
10063
10079
  _touchCcLiveStream(liveState);
10080
+ if (liveState.statusMessage) {
10081
+ setStatus('');
10082
+ }
10064
10083
  // Claude (direct) now threads a tool_use id; with an id the chip starts
10065
10084
  // 'pending' and can be flipped to completed/failed by onToolUpdate.
10066
10085
  const entry = { name, input: input || {}, id: id || null, status: id ? 'pending' : null };
@@ -10515,6 +10534,7 @@ What would you like to discuss or change? When you're happy, say "approve" and I
10515
10534
  writeCcEvent({ type: 'tool-update', id: tool.id, status: tool.status });
10516
10535
  }
10517
10536
  }
10537
+ if (live.statusMessage) writeCcEvent({ type: 'status', message: live.statusMessage });
10518
10538
  if (live.text) writeCcEvent({ type: 'chunk', text: live.text });
10519
10539
  if (live.donePayload) {
10520
10540
  writeCcEvent(live.donePayload);
@@ -11276,6 +11296,15 @@ What would you like to discuss or change? When you're happy, say "approve" and I
11276
11296
  } catch { /* unknown/free-text runtime */ }
11277
11297
  return modelStr;
11278
11298
  }
11299
+ const _isClear = (v) => v === '' || v === null;
11300
+ let _registeredCliNames = null;
11301
+ const _validCli = (name) => {
11302
+ if (_registeredCliNames == null) {
11303
+ try { _registeredCliNames = _engineRuntimes.listRuntimes(); }
11304
+ catch { _registeredCliNames = []; }
11305
+ }
11306
+ return _registeredCliNames.length === 0 || _registeredCliNames.includes(String(name));
11307
+ };
11279
11308
  if (body.engine) {
11280
11309
  const e = body.engine;
11281
11310
  const D = shared.ENGINE_DEFAULTS;
@@ -11385,15 +11414,6 @@ What would you like to discuss or change? When you're happy, say "approve" and I
11385
11414
  // chooses)" option submits '' and we must persist that as "unset".
11386
11415
  // Validate `defaultCli` and `ccCli` against the runtime registry so a
11387
11416
  // typo in the dashboard can't pin the fleet to a non-existent runtime.
11388
- const _isClear = (v) => v === '' || v === null;
11389
- let _registeredCliNames = null;
11390
- const _validCli = (name) => {
11391
- if (_registeredCliNames == null) {
11392
- try { _registeredCliNames = require('./engine/runtimes').listRuntimes(); }
11393
- catch { _registeredCliNames = []; }
11394
- }
11395
- return _registeredCliNames.length === 0 || _registeredCliNames.includes(String(name));
11396
- };
11397
11417
  if (e.defaultCli !== undefined) {
11398
11418
  if (_isClear(e.defaultCli)) _deleteEngineConfig('defaultCli');
11399
11419
  else if (_validCli(e.defaultCli)) _setEngineConfig('defaultCli', String(e.defaultCli));
@@ -11589,8 +11609,9 @@ What would you like to discuss or change? When you're happy, say "approve" and I
11589
11609
  // CLI values pin the agent to a specific runtime. `0` is a valid
11590
11610
  // maxBudgetUsd (read-only / dry-run agents).
11591
11611
  if (updates.cli !== undefined) {
11592
- if (updates.cli === '' || updates.cli === null) delete config.agents[id].cli;
11593
- else config.agents[id].cli = String(updates.cli);
11612
+ if (_isClear(updates.cli)) delete config.agents[id].cli;
11613
+ else if (_validCli(updates.cli)) config.agents[id].cli = String(updates.cli);
11614
+ else _clamped.push(`agents.${id}.cli: "${updates.cli}" not registered (kept previous value)`);
11594
11615
  }
11595
11616
  if (updates.model !== undefined) {
11596
11617
  if (updates.model === '' || updates.model === null) delete config.agents[id].model;
@@ -14901,7 +14922,8 @@ What would you like to discuss or change? When you're happy, say "approve" and I
14901
14922
  // dashboard route serves dashboard/slim.html instead of the full SPA.
14902
14923
  // Checked at request time so /api/features/toggle flips behavior with
14903
14924
  // no restart; flag-off means zero behavior change for the catch-all.
14904
- if (pathname === '/' && features.isFeatureOn('slim-ux', CONFIG)) {
14925
+ const fullDashboardRequested = new URL(req.url, 'http://localhost').searchParams.get('fullDashboard') === '1';
14926
+ if (pathname === '/' && !fullDashboardRequested && features.isFeatureOn('slim-ux', CONFIG)) {
14905
14927
  return serveSlimUx(req, res);
14906
14928
  }
14907
14929
 
@@ -15134,13 +15156,17 @@ if (require.main === module) {
15134
15156
  // engine reads, port binding) is captured rather than dying silently.
15135
15157
  _installCrashHandlers();
15136
15158
 
15137
- // Every copilot the dashboard spawns Command Center, doc-chat, and the CC
15138
- // worker pool (`copilot --acp`) inherits this process's COPILOT_HOME. Point
15139
- // it at the seeded, MCP-filtered home so those `direct:true` copilot calls
15140
- // load the filtered config instead of the operator's full `~/.copilot` stack
15141
- // (which would pop a Microsoft auth window per call). Mirrors the engine's
15142
- // boot-time set. See shared.ensureAgentCopilotHome. Fail-open.
15143
- try { process.env.COPILOT_HOME = shared.ensureAgentCopilotHome(MINIONS_DIR, CONFIG?.engine); } catch {}
15159
+ // Deliberately do NOT set process.env.COPILOT_HOME here. Command Center,
15160
+ // doc-chat, and the CC worker pool (`copilot --acp`) all spawn `copilot`
15161
+ // as this process's children, so they should inherit the operator's real
15162
+ // `~/.copilot` home (including their model preference in settings.json)
15163
+ // exactly like an interactive `copilot` CLI session would that's the
15164
+ // documented CLI contract. An earlier revision forced these onto an
15165
+ // isolated, MCP-filtered home to dodge MCP-server auth popups, but that
15166
+ // broke CC's settings inheritance and reintroduced a staleness/reseed bug;
15167
+ // reverted (W-mre75l6x00024f49). Per-dispatch agent isolation is unaffected
15168
+ // — the engine process still seeds its own COPILOT_HOME at boot and
15169
+ // re-stamps it per spawn via `_applyAgentCopilotHome` (engine.js).
15144
15170
 
15145
15171
  // Kick off first npm check on startup, then re-check every 4 hours.
15146
15172
  // Guarded here so that requiring dashboard as a module (unit tests, _withDashServer)
package/docs/README.md CHANGED
@@ -7,14 +7,28 @@ A navigable index of every Markdown file under `docs/`. Entries are grouped by a
7
7
  Hands-on stories and distribution guides for people running or evaluating Minions.
8
8
 
9
9
  - [blog-first-successful-dispatch.md](blog-first-successful-dispatch.md) — Narrative walkthrough of the first end-to-end agent dispatch and the seven failed spawn attempts that preceded it.
10
- - [distribution.md](distribution.md) — How Minions is published (this repo: `@opg-microsoft/minions` to GitHub Packages; paired peer `yemi33/minions` to npm) and the bidirectional sync contract — automated opg → yemi33 backport workflow + manual yemi33 → opg sync PRs.
10
+ - [documentation-audit-2026-07-09.md](documentation-audit-2026-07-09.md) — Verified user-facing documentation audit covering runtime, CLI, storage, routing, and broken-link corrections.
11
+ - [distribution.md](distribution.md) — How Minions is published (this repo: `@opg-microsoft/minions` to GitHub Packages; paired peer `@yemi33/minions` to npm) and the bidirectional sync contract — automated opg → yemi33 backport workflow + manual yemi33 → opg sync PRs.
12
+ - [onboarding.md](onboarding.md) — Condensed first-30-minutes walkthrough for a new operator.
13
+ - [tutorials/README.md](tutorials/README.md) — Progressive tutorial track from installation through advanced automation and operations.
14
+
15
+ ### Tutorial track
16
+
17
+ - [tutorials/01-install-and-connect.md](tutorials/01-install-and-connect.md) — Install Minions, verify a runtime, link a project, and start the dashboard.
18
+ - [tutorials/02-first-task.md](tutorials/02-first-task.md) — Dispatch and inspect a bounded first task.
19
+ - [tutorials/03-plan-driven-feature.md](tutorials/03-plan-driven-feature.md) — Review, approve, execute, and verify a dependency-aware plan.
20
+ - [tutorials/04-runtimes-and-harness.md](tutorials/04-runtimes-and-harness.md) — Select runtimes and verify project skill, command, and MCP propagation.
21
+ - [tutorials/05-schedules-and-watches.md](tutorials/05-schedules-and-watches.md) — Combine time-based schedules with state-based watches.
22
+ - [tutorials/06-managed-services.md](tutorials/06-managed-services.md) — Leave an engine-owned development service running with health checks.
23
+ - [tutorials/07-cross-repo-plan.md](tutorials/07-cross-repo-plan.md) — Coordinate one plan across multiple linked repositories.
24
+ - [tutorials/08-operations-and-recovery.md](tutorials/08-operations-and-recovery.md) — Diagnose pending, failed, orphaned, and stopped work safely.
11
25
 
12
26
  ## Contributor-facing
13
27
 
14
28
  Architecture, design proposals, and lifecycle references for people working on the engine, dashboard, or playbooks.
15
29
 
16
30
  - [branch-derivation.md](branch-derivation.md) — Engine-side branch fallback (`work/<wi-id>`) vs. agent-authored long form, the structured-vs-loose PR-pointer extractors, and the canonical PR-fix duplication incident.
17
- - [command-center.md](command-center.md) — Command Center (CC) chat panel: persistent Sonnet sessions, `--resume` semantics, system-prompt invalidation, and per-tab session storage.
31
+ - [command-center.md](command-center.md) — Command Center (CC) chat panel: configured-runtime sessions, resume semantics, system-prompt invalidation, and per-tab session storage.
18
32
  - [specs/agent-rename.md](specs/agent-rename.md) — Design spec for **agent rename & name decoupling** — the prerequisite for the Role/Agent Library. Formalizes the stable opaque agent id, makes charters name-agnostic, and adds `POST /api/agents/rename` (display name + emoji). Feature-flagged (`agent-rename`) and fully backward-compatible.
19
33
  - [specs/agent-configurability.md](specs/agent-configurability.md) — Design spec for the user-configurable Role / Agent Library, feature-flagged behind `agent-library` and fully backward-compatible. Builds on `agent-rename.md`. Role = the reusable durable charter; agent = a thin instance (identity + variable expertise + role pointer). Phase 0 renames `skills`→`expertise`; Phase 1 exposes/edits role charters + per-agent expertise; Phase 2 adds role/agent CRUD with builtin delete-protection.
20
34
  - [completion-reports.md](completion-reports.md) — Canonical schema for the per-spawn completion JSON: trust nonce, `failure_class` enum, `noop` semantics, `retryable` / `needs_rerun` shape, and the artifacts array.
@@ -26,7 +40,7 @@ Architecture, design proposals, and lifecycle references for people working on t
26
40
  - [cross-repo-plans.md](cross-repo-plans.md) — Cross-repo plans: a single plan whose work items ship into two or more configured projects — per-item `project` field, per-project work-item fan-out, and one verify work item per touched repo.
27
41
  - [dead-code-audit-retractions.md](dead-code-audit-retractions.md) — Retracted dead-code-audit findings (false positives) that future audits MUST read before re-citing.
28
42
  - [deprecated-process.md](deprecated-process.md) — Schema for `docs/deprecated.json` and the weekly `cleanup-deprecated` audit walk that retires entries past their removal signal.
29
- - [design-state-storage.md](design-state-storage.md) — Design proposal evaluating five database options for replacing Minions' file-based JSON state; recommends `node:sqlite` as the medium-term target (accepted; implementation tracked in CHANGELOG.md Phases 0–9).
43
+ - [design-state-storage.md](design-state-storage.md) — Design proposal evaluating five database options for replacing Minions' file-based JSON state; recommends `node:sqlite` (accepted; implementation tracked in CHANGELOG.md Phases 0–10).
30
44
  - [harness-mode.md](harness-mode.md) — Tri-Agent Harness Mode (`harness_mode: "tri_agent"` on scheduled tasks): Planner → Generator → Evaluator loop that iterates a shared on-disk artifact until a rubric passes or the iteration cap fires.
31
45
  - [harness-propagation.md](harness-propagation.md) — How user-level and project-local harness assets (skills, slash-commands, MCP config, `CLAUDE.md` / `AGENTS.md`) propagate into an agent's worktree via `--add-dir`, the `harnessPropagateProjectLocal` flag, and the project-local-on-main worktree-visibility footgun.
32
46
  - [harness-transparency.md](harness-transparency.md) — The `harnessUsed` self-report contract: capture (agent reports the skills / MCPs / commands / docs it used) → ground (engine cross-checks against `_harnessPropagated` and annotates `grounded:true\|false`, never dropping) → surface (PR comment, final agent note, work-item modal).
@@ -54,7 +68,7 @@ Architecture, design proposals, and lifecycle references for people working on t
54
68
  - [slim-ux/architecture-suggestions.md](slim-ux/architecture-suggestions.md) — Slim-UX follow-up architecture suggestions paired with `concepts.md`.
55
69
  - [team-memory.md](team-memory.md) — Per-agent memory layer (`knowledge/agents/<id>.md`) and the consolidation/routing rules that populate it from `notes/inbox/`.
56
70
  - [timeouts-and-liveness.md](timeouts-and-liveness.md) — What kills (or doesn't kill) a live tracked agent: the wall-clock vs steering kill invariants, spawn-phase watchdog gates, steering safety nets, and stale-orphan detection ladder.
57
- - [watches.md](watches.md) — Persistent monitoring jobs (`engine/watches.json`): target-type registry, conditions, follow-up actions, and the `watches.d/` plugin folder.
71
+ - [watches.md](watches.md) — Persistent monitoring jobs: target-type registry, conditions, follow-up actions, and the `watches.d/` plugin folder.
58
72
  - [workspace-manifests.md](workspace-manifests.md) — Declarative per-agent permission scoping: `allowed_tools` / `allowed_repos` / `allowed_external_urls` / `memory_scope`, dispatch-time repo gate, and runtime `--allowedTools` narrowing.
59
73
  - [worktree-lifecycle.md](worktree-lifecycle.md) — Worktree pool recycling, the live-dispatch guard that prevents wiping an agent's unpushed work, the dirty/divergent quarantine path, and the Windows EPERM/EBUSY file-lock retry footgun.
60
74
 
@@ -62,15 +76,13 @@ Architecture, design proposals, and lifecycle references for people working on t
62
76
 
63
77
  Operational runbooks for engine operators and fleet maintainers.
64
78
 
65
- - [notes.md](notes.md) — Consolidated team knowledge: patterns, conventions, bugs, and build findings accumulated from agent inbox notes. Read by the engine and injected into agent prompts as shared context.
66
79
  - [auto-discovery.md](auto-discovery.md) — Auto-discovery and execution pipeline: the per-tick orchestration loop and the four work-discovery sources.
67
80
  - [diagnostics-memory.md](diagnostics-memory.md) — Operator runbook for the in-process memory + perf observability surface: `/api/diagnostics/memory[/history]`, `/api/diagnostics/heap-snapshot` guard-token capture, `MEMORY_BASELINE` log emissions, `--cpu-prof`/`--heap-prof` capture, and the `test/perf/soak.test.js` heap-growth regression gate.
68
81
  - [diagnostics-crash-reports.md](diagnostics-crash-reports.md) — Proactive Node crash-diagnostics reports (`--report-on-fatalerror --report-on-signal --diagnostic-dir=...`) for the engine.js process on Windows: where reports land, config/opt-out, and retention.
69
82
  - [engine-restart.md](engine-restart.md) — How agents survive an engine restart: state persistence, the 20-minute startup grace period, and orphan reattachment via PID files and `live-output.log`.
70
83
  - [human-vs-automated.md](human-vs-automated.md) — Quick reference table of which features humans start, run, decide, and recover, and the two human approval gates.
71
84
  - [kb-sweep.md](kb-sweep.md) — Knowledge-base sweep runbook: how `engine/kb-sweep.js` consolidates `notes/inbox/` into `knowledge/` and survives `minions restart`.
72
- - [onboarding.md](onboarding.md) — First-30-minutes walkthrough for a new operator: install, init, dispatch a first work item, watch it land.
73
- - [preflight.md](preflight.md) — `minions doctor` and the lighter per-CLI `minions preflight` checks: what each non-self-explanatory row (permission bypass, runtime detection, drive-root, etc.) is asserting.
85
+ - [preflight.md](preflight.md) — `minions doctor` and the internal initialization/startup preflight: what each non-self-explanatory row (permission bypass, runtime detection, drive-root, etc.) is asserting.
74
86
  - [security.md](security.md) — Threat model: single-user/loopback deployment assumptions, dashboard Origin gate, data-flow trust boundaries, secret handling, and known residual risks (CSRF sweep, prompt injection, log-redactor audit).
75
87
 
76
88
  ---
@@ -0,0 +1,94 @@
1
+ # Architecture Review — 2026-07-09
2
+
3
+ Scope: engine orchestration, SQL/JSON state boundaries, runtime/process recovery,
4
+ worktree lifecycle, pipelines/schedules, dashboard/API settings, and prompt
5
+ assembly. Findings were verified against `origin/main` at `b1b83dd2`.
6
+
7
+ ## Fixed in this change
8
+
9
+ ### High: Codex processes were rejected by orphan recovery
10
+
11
+ `isProcessCommandLineMatchingAgent` recognized only Claude and Copilot. After an
12
+ engine restart, a live Codex dispatch could therefore be classified as a
13
+ recycled PID and retried while the original process was still running.
14
+
15
+ The process gate now recognizes all bundled runtime CLIs, including native and
16
+ npm-installed Codex and Claude layouts, without accepting unrelated substring
17
+ matches.
18
+
19
+ ### High: worktree GC lost the project name from real dispatch rows
20
+
21
+ Worktree GC assumed `dispatch.meta.project` was a string, while production
22
+ dispatches store a project descriptor object. In shared worktree-root layouts,
23
+ the resulting `default` hash could fail to protect another project's active
24
+ worktree from orphan cleanup.
25
+
26
+ GC now resolves both the current object shape and the legacy string shape.
27
+
28
+ ### Medium: invalid per-agent runtimes bypassed settings validation
29
+
30
+ Fleet runtime fields were validated against the adapter registry, but
31
+ `agents.<id>.cli` was persisted verbatim. A typo could poison future dispatches
32
+ with a runtime-resolution failure. Per-agent overrides now share the fleet
33
+ validation path and preserve the previous value on invalid input.
34
+
35
+ ### Medium: the no-TTL status cache omitted its own source files
36
+
37
+ `/api/status` includes config-, install-, and package-derived fields but its
38
+ mtime registry omitted `config.json`, `.install-id`, and `package.json`.
39
+ Out-of-process changes could remain stale indefinitely. These inputs now
40
+ participate in cache invalidation.
41
+
42
+ ### Medium: Slim UX trapped users away from advanced settings
43
+
44
+ Slim linked to `/?fullDashboard=1`, but root takeover ignored the query string
45
+ and served Slim again. The root dispatcher now honors that explicit classic-UX
46
+ escape hatch.
47
+
48
+ ### Low: prompt validation generated warnings for valid templates
49
+
50
+ The validator required the literal `## Your Task`, while established playbooks
51
+ also use `## Task Description` and `## Mission`. Those valid aliases now satisfy
52
+ the task-section contract, eliminating recurring false-positive warnings.
53
+
54
+ ### Low: cancelled cron work blocked future schedule fires
55
+
56
+ The dashboard run-now path already treated cancelled work as terminal, but the
57
+ engine's cron enqueue dedup did not. Both paths now allow a new run after
58
+ cancellation.
59
+
60
+ ## Verified follow-up findings
61
+
62
+ These require separate focused changes rather than expanding this PR:
63
+
64
+ 1. **Dispatch SQL/JSON empty-state resurrection (high).** A process that has not
65
+ written SQL can still adopt stale `dispatch.json` when the SQL table is empty.
66
+ The durable fix is a mirror-generation/hash marker or removal of the legacy
67
+ fallback, not moving filesystem writes inside a SQLite transaction.
68
+ 2. **Pipeline task terminal-state model (high).** Task stages use a boolean
69
+ completion check and promote failed work items to a completed pipeline stage;
70
+ cancelled/review-blocked work can wedge indefinitely. Replace the boolean
71
+ helper with an explicit terminal outcome (`completed`, `failed`,
72
+ `waiting-human`, non-terminal) and propagate it through parallel stages.
73
+ 3. **Settings save spans two independent writes (medium).** Config and routing
74
+ are saved through separate endpoints, so routing failure can leave a partial
75
+ configuration update. A combined transactional settings contract should
76
+ replace the client-side two-step sequence.
77
+ 4. **ADO identity lookup duplication (medium).** PR polling resolves the same
78
+ authenticated user more than once per PR. Cache it once per organization and
79
+ poll pass to reduce throttling pressure.
80
+ 5. **Late descendant process leak window (medium).** Spawn cleanup snapshots
81
+ descendants at 3 seconds and then every 30 seconds. Children created and
82
+ reparented inside that window can evade final cleanup. Track descendants more
83
+ frequently while the runtime is alive.
84
+ 6. **Supervisor stale PID reuse (medium).** Startup trusts any live PID in
85
+ `supervisor.pid`. Validate the command line against this checkout's
86
+ `engine/supervisor.js` before suppressing startup.
87
+
88
+ ## Healthy areas
89
+
90
+ - Config/routing references and schedule schemas were valid.
91
+ - Runtime work-item and pull-request mirrors had no stuck dispatched rows,
92
+ duplicate PR IDs, or unknown PR states.
93
+ - No active dispatch was older than the five-hour wall-clock limit.
94
+ - Cooldown state was small and not exhibiting pending-context bloat.
@@ -1,8 +1,8 @@
1
1
  # Command Center
2
2
 
3
- The Command Center (CC) is the dashboard's conversational chat panel. It opens from the **CC** button in the top-right header and lets you drive the engine — dispatching work items, saving notes, approving plans, and asking questions about minions state — through a persistent Claude/Copilot session with full repo awareness.
3
+ The Command Center (CC) is the dashboard's conversational chat panel. It opens from the **CC** button in the top-right header and lets you drive the engine — dispatching work items, saving notes, approving plans, and asking questions about Minions state — through a persistent session on the configured CC runtime.
4
4
 
5
- CC is intentionally a thin wrapper around the runtime CLI: state changes happen via `Bash`-tool `curl` calls to the dashboard's own REST API, not via parsed delimiter blocks. The end-to-end flow is `dashboard/js/command-center.js` `_ccDoSend()` → `POST /api/command-center` (or `/api/command-center/stream`) in `dashboard.js` (`handleCommandCenter`) → `engine/llm.js` `callLLM({ direct: true })` → claude/copilot CLI session persisted in `engine/cc-sessions.json`. Per-turn API mutations are correlated via the `X-CC-Turn-Id` header and surfaced as standalone `role='action'` chips rendered outside the assistant bubble (`_ccActionResultLine` + `addMsg('action', ...)`).
5
+ CC is intentionally a thin wrapper around the runtime CLI: state changes happen via tool-driven calls to the dashboard's own REST API, not via parsed delimiter blocks. The end-to-end flow is `dashboard/js/command-center.js` `_ccDoSend()` → `POST /api/command-center` (or `/api/command-center/stream`) in `dashboard.js` (`handleCommandCenter`) → `engine/llm.js` `callLLM({ direct: true })` → runtime session persisted in `engine/state.db` (`cc_sessions`; `engine/cc-sessions.json` is a passive mirror). Per-turn API mutations are correlated via the `X-CC-Turn-Id` header and surfaced as standalone `role='action'` chips rendered outside the assistant bubble (`_ccActionResultLine` + `addMsg('action', ...)`).
6
6
 
7
7
  For canonical detail (system prompt, session lifecycle, turn-ID surfacing pipeline, doc-chat integration, and CC API contract), read [`CLAUDE.md`](../CLAUDE.md) — see the **CC API Contract** and **Sessions** sections — and the source in [`dashboard/js/command-center.js`](../dashboard/js/command-center.js), [`dashboard.js`](../dashboard.js) (`handleCommandCenter`), and [`prompts/cc-system.md`](../prompts/cc-system.md).
8
8
 
@@ -70,6 +70,8 @@ Any violation rejects the **whole turn** with a typed error envelope (`code: 'in
70
70
 
71
71
  **Worker-pool interaction (W-mray463s001zcee2).** The opt-in Copilot ACP worker pool (`engine.ccUseWorkerPool: true`, default ON for Copilot CC) is a **separate protocol path** from the table above — `engine/cc-worker-pool.js`'s `session/prompt` call only ever sends a single text content block (`[{ type: 'text', text: prompt }]`) and has no image/attachment content-block support today. This used to mean image turns were silently dropped whenever the pool was active, with a comment incorrectly blaming a Copilot capability gap (`imageInput:false`) that doesn't exist — Copilot's `imageInput` is `true` and fully supports images on the direct spawn path. The fix: `_invokeCcStream` (dashboard.js) now checks for a non-empty `images` array **before** routing through `resolveCcUseWorkerPool` — image-bearing turns bypass the warm pool for that turn only and cold-spawn a one-off CLI process via the working `--attachment` path (same as the non-pooled path always used), while image-less turns keep using the fast warm pool. If `engine/cc-worker-pool.js`'s ACP session protocol is later extended to carry attachments (Option A), this per-turn bypass in `_invokeCcStream` should be revisited/removed.
72
72
 
73
+ Because that bypass can spend tens of seconds starting a one-off process before the first model token, the SSE stream shows a user-focused “Looking at the image…” status instead of exposing the cold-start implementation detail or leaving an apparently stalled spinner.
74
+
73
75
  `_resolveImageOpts` (in `engine/llm.js`) is pure and unit-tested: it forwards the `images` list only when `runtime.capabilities.imageInput` is truthy, otherwise returns the typed error so CC/doc-chat surface the envelope instead of a silently-text-only reply. `_spawnProcess` re-applies the same gate (`if (!caps.imageInput) adapterOpts.images = undefined`) as defense in depth. Image **filenames** are run through the untrusted-input fence (`buildSource('cc-image-filename', …)`) before they reach prompt text, since they are user-supplied.
74
76
 
75
77
  **No new `engine.*` flag.** The limits above are module-level constants in `dashboard.js` (`CC_IMAGE_MAX_COUNT`, `CC_IMAGE_MAX_DECODED_BYTES`, `CC_IMAGE_MIME_ALLOWLIST`), not config keys; `engine/shared.js` `ENGINE_DEFAULTS` was not touched. Per CLAUDE.md Best Practice #9 (Settings parity), no Settings toggle is added because no new `engine.*` flag was introduced. Runtime selection that determines whether images are accepted is the existing `engine.ccCli` / `ccModel` override, which already has a Settings control.
package/docs/constants.md CHANGED
@@ -6,7 +6,7 @@ All cross-cutting status / type / condition values are defined in [`engine/share
6
6
  WI_STATUS = { PENDING, DISPATCHED, DONE, FAILED, PAUSED, QUEUED, DECOMPOSED, CANCELLED }
7
7
  DONE_STATUSES = Set([WI_STATUS.DONE, 'in-pr', 'implemented', 'complete']) // legacy aliases on read only
8
8
  WORK_TYPE = { IMPLEMENT, IMPLEMENT_LARGE, FIX, REVIEW, VERIFY, PLAN, PLAN_TO_PRD,
9
- DECOMPOSE, MEETING, EXPLORE, ASK, TEST, DOCS, SETUP }
9
+ DECOMPOSE, MEETING, EXPLORE, ASK, TEST, DOCS, SETUP, BUILD_FIX_COMPLEX }
10
10
  PLAN_STATUS = { ACTIVE, AWAITING_APPROVAL, APPROVED, PAUSED, REJECTED, COMPLETED, REVISION_REQUESTED }
11
11
  PRD_ITEM_STATUS = { MISSING, UPDATED, DONE }; PRD_MATERIALIZABLE = Set([MISSING, UPDATED])
12
12
  PR_STATUS = { ACTIVE, MERGED, ABANDONED, CLOSED, LINKED }; PR_POLLABLE_STATUSES = Set([ACTIVE, LINKED])
@@ -0,0 +1,43 @@
1
+ # Documentation Audit: 2026-07-09
2
+
3
+ Scope: `README.md`, `CLAUDE.md`, `docs/**/*.md`, `playbooks/*.md`,
4
+ `agents/*/charter.md`, and `prompts/*.md`.
5
+
6
+ The audit prioritized user-facing claims that can cause an operator to install
7
+ the wrong runtime, use a nonexistent command, inspect a passive state mirror as
8
+ canonical data, or misunderstand routing and dashboard behavior.
9
+
10
+ ## Corrected findings
11
+
12
+ | Stale claim | Correction | Source of truth |
13
+ |---|---|---|
14
+ | Node.js 18+ was sufficient | Node.js 22.5+ is required for `node:sqlite` | `package.json:68` |
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 | Migrated runtime state is primary in `engine/state.db`; JSON trackers are passive mirrors | `engine/dispatch-store.js:49-68`, `CLAUDE.md` "State storage" |
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
+ | Git repository host default was documented as ADO | The fallback host is GitHub | `engine/projects.js:504`, `engine/projects.js:564` |
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 | Sessions are stored in SQLite; the JSON file is a mirror | `engine/shared.js:1830`, `engine/db/migrations/011-remaining-state.js` |
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
+ | 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
+
24
+ ## New tutorial coverage
25
+
26
+ The tutorial track under [`docs/tutorials/`](tutorials/README.md) now covers:
27
+
28
+ 1. installation and project linking;
29
+ 2. a first bounded dispatch;
30
+ 3. plan-to-PRD implementation and verification;
31
+ 4. runtime and harness configuration;
32
+ 5. schedules and watches;
33
+ 6. managed development services;
34
+ 7. cross-repository plans;
35
+ 8. operations and recovery.
36
+
37
+ ## Follow-up policy
38
+
39
+ Reference docs remain intentionally detailed. Tutorials should link to those
40
+ references rather than duplicate full schemas. Future sweeps should continue to
41
+ verify CLI examples against `bin/minions.js` and `engine/cli.js`, dashboard
42
+ routes against `dashboard.js`, work types against `routing.md`, and state
43
+ storage claims against the SQL store modules.
@@ -10,8 +10,8 @@ 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/dispatch.json` | Yes |
14
- | Agent status (working/idle/error) | Derived from `engine/dispatch.json` | Yes |
13
+ | Dispatch queue (pending/active/completed) | `engine/state.db` (`dispatches`; `engine/dispatch.json` is a passive mirror) | Yes |
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
17
  | Cooldown timestamps | In-memory Map | **No** (repopulated from `engine/cooldowns.json`) |
@@ -1,7 +1,7 @@
1
1
  # Managed-spawn primitive
2
2
 
3
3
  > Engine-owned long-running services with sidecar declaration, healthcheck-gated dispatch SUCCESS, and dashboard discovery.
4
- > Plan: [`plans/plan-w-mp7k1r760003b5dd-2026-05-15.md`](../plans/plan-w-mp7k1r760003b5dd-2026-05-15.md) · Module: [`engine/managed-spawn.js`](../engine/managed-spawn.js) · Dashboard panel: `/engine` → "Managed Processes".
4
+ > Module: [`engine/managed-spawn.js`](../engine/managed-spawn.js) · Dashboard panel: `/engine` → "Managed Processes".
5
5
 
6
6
  ## Why this exists
7
7