@yemi33/minions 0.1.2448 → 0.1.2449
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/cli-api-client.js +1 -1
- package/bin/install-internal-minions.js +1382 -44
- package/bin/install-layout.js +150 -0
- package/bin/minions.js +460 -167
- package/dashboard/docs/typography.md +65 -12
- package/dashboard/js/command-center.js +58 -4
- package/dashboard/js/detail-panel.js +36 -0
- package/dashboard/js/memory-panel.js +59 -12
- package/dashboard/js/qa.js +179 -20
- package/dashboard/js/refresh.js +148 -12
- package/dashboard/js/render-dispatch.js +3 -4
- package/dashboard/js/render-inbox.js +2 -2
- package/dashboard/js/render-other.js +3 -3
- package/dashboard/js/render-pipelines.js +14 -0
- package/dashboard/js/render-plans.js +57 -9
- package/dashboard/js/render-prd.js +132 -23
- package/dashboard/js/render-prs.js +195 -166
- package/dashboard/js/render-schedules.js +63 -3
- package/dashboard/js/render-utils.js +3 -3
- package/dashboard/js/render-watches.js +19 -3
- package/dashboard/js/render-work-items.js +238 -30
- package/dashboard/js/settings.js +205 -54
- package/dashboard/js/utils.js +51 -1
- package/dashboard/pages/home.html +1 -1
- package/dashboard/pages/qa.html +1 -16
- package/dashboard/pages/work.html +40 -0
- package/dashboard/shared/cc-limits.js +79 -0
- package/dashboard/shared/pr-filters.js +21 -38
- package/dashboard/shared/project-git-summary.js +1 -1
- package/dashboard/shared/record-filters.js +169 -0
- package/dashboard/shared/watches-source.js +1 -1
- package/dashboard/shared/welcome-popup.js +1 -1
- package/dashboard/shared/wi-filters.js +302 -0
- package/dashboard/slim/body.html +1 -0
- package/dashboard/slim/js/command-send.js +26 -0
- package/dashboard/slim/js/modals-tiles.js +380 -39
- package/dashboard/slim/js/status.js +13 -21
- package/dashboard/slim/layout.html +1 -0
- package/dashboard/slim/panel-bootstrap.js +6 -2
- package/dashboard/slim/styles.css +38 -0
- package/dashboard/styles.css +159 -55
- package/dashboard-build.js +13 -2
- package/dashboard.js +956 -423
- package/docs/README.md +11 -6
- package/docs/api-errors.md +2 -2
- package/docs/architecture-review-2026-07-09.md +1 -1
- package/docs/architecture.excalidraw +2 -2
- package/docs/auto-discovery.md +18 -9
- package/docs/branch-derivation.md +4 -4
- package/docs/capture-demos.js +39 -2
- package/docs/ci-runner-canary.md +123 -0
- package/docs/claude-md-propagation.md +2 -2
- package/docs/cloud-agent-dispatch.md +204 -0
- package/docs/command-center.md +7 -7
- package/docs/completion-reports.md +43 -20
- package/docs/constants.md +10 -3
- package/docs/constellation-bridge.md +134 -6
- package/docs/constellation-style-telemetry.md +4 -4
- package/docs/contracts/capability-protocol.v1.json +165 -0
- package/docs/cooldown-merge-semantics.md +12 -12
- package/docs/copilot-cli-schema.md +7 -7
- package/docs/cross-repo-plans.md +10 -10
- package/docs/dead-code-audit-retractions.md +5 -5
- package/docs/default-branch-ci.md +173 -0
- package/docs/deprecated.json +31 -31
- package/docs/design-inbox-entries-schema.md +3 -3
- package/docs/design-language.md +1051 -0
- package/docs/design-state-storage.md +11 -11
- package/docs/diagnostics-crash-reports.md +9 -9
- package/docs/diagnostics-memory.md +5 -5
- package/docs/documentation-audit-2026-07-09.md +7 -7
- package/docs/engine-restart.md +90 -5
- package/docs/harness-mode.md +1 -1
- package/docs/internal-install.md +338 -39
- package/docs/kb-dedup-duplicate-pair-investigation.md +5 -5
- package/docs/kb-pr3223-cascade-archiving.md +1 -1
- package/docs/kb-pr696-merge-conflict-docs.md +6 -6
- package/docs/kb-sweep.md +35 -35
- package/docs/keep-processes.md +1 -1
- package/docs/live-checkout-mode.md +30 -30
- package/docs/managed-spawn.md +18 -14
- package/docs/named-agents.md +7 -7
- package/docs/plan-lifecycle.md +69 -2
- package/docs/pr-author-identity.md +114 -0
- package/docs/pr-auto-fix-dispatch.md +19 -4
- package/docs/pr-comment-followup.md +6 -6
- package/docs/pr-review-fix-loop.md +59 -10
- package/docs/process-termination.md +40 -0
- package/docs/proposals/repo-pool-for-live-checkout.md +13 -13
- package/docs/qa-runbook-lifecycle.md +367 -17
- package/docs/qa-runbooks.md +3 -3
- package/docs/rfc-completion-json.md +18 -18
- package/docs/runtime-adapters.md +26 -21
- package/docs/security.md +6 -6
- package/docs/self-improvement.md +4 -4
- package/docs/shared-lifecycle-module-map.md +473 -472
- package/docs/skills.md +52 -3
- package/docs/slim-ux/concepts.md +121 -116
- package/docs/specs/agent-configurability.md +18 -18
- package/docs/specs/agent-rename.md +18 -18
- package/docs/team-memory.md +38 -21
- package/docs/timeouts-and-liveness.md +118 -10
- package/docs/tutorials/01-install-and-connect.md +1 -1
- package/docs/watches.md +40 -39
- package/docs/workspace-manifests.md +4 -4
- package/docs/worktree-lifecycle.md +293 -14
- package/engine/README.md +46 -0
- package/engine/{ado-comment.js → ado/comment.js} +8 -8
- package/engine/{ado-git-auth.js → ado/git-auth.js} +4 -4
- package/engine/{ado.js → ado/index.js} +417 -63
- package/engine/{ado-status.js → ado/status.js} +6 -8
- package/engine/{ado-token.js → ado/token.js} +1 -1
- package/engine/{acp-transport.js → agents/acp-transport.js} +62 -22
- package/engine/{agent-worker-pool.js → agents/agent-worker-pool.js} +17 -8
- package/engine/{cc-worker-pool.js → agents/cc-worker-pool.js} +16 -6
- package/engine/{claude-md-context.js → agents/claude-md-context.js} +5 -5
- package/engine/{harness-context.js → agents/harness-context.js} +5 -5
- package/engine/{harness.js → agents/harness.js} +3 -3
- package/engine/{llm.js → agents/llm.js} +18 -14
- package/engine/{model-discovery.js → agents/model-discovery.js} +2 -2
- package/engine/{playbook.js → agents/playbook.js} +155 -22
- package/engine/{pooled-agent-process.js → agents/pooled-agent-process.js} +14 -12
- package/engine/{preflight.js → agents/preflight.js} +29 -10
- package/engine/{spawn-agent.js → agents/spawn-agent.js} +25 -14
- package/engine/{spawn-phase-watchdog.js → agents/spawn-phase-watchdog.js} +16 -7
- package/engine/{steering.js → agents/steering.js} +5 -5
- package/engine/{tools-inventory.js → agents/tools-inventory.js} +2 -2
- package/engine/{agent-api-validation.js → api/agent-api-validation.js} +2 -2
- package/engine/{api-validation.js → api/api-validation.js} +1 -1
- package/engine/api/bridge.js +787 -0
- package/engine/{cc-api-validation.js → api/cc-api-validation.js} +1 -1
- package/engine/api/companion.js +560 -0
- package/engine/{content-api-validation.js → api/content-api-validation.js} +2 -2
- package/engine/{pr-issue-validation.js → api/pr-issue-validation.js} +33 -6
- package/engine/{settings-validation.js → api/settings-validation.js} +32 -4
- package/engine/api-contracts/agent-content.js +4 -4
- package/engine/api-contracts/capability-manifest.js +236 -0
- package/engine/api-contracts/capability-protocol.js +333 -0
- package/engine/api-contracts/cc-ops.js +1 -1
- package/engine/api-contracts/config-runtime.js +5 -0
- package/engine/api-contracts/core.js +28 -1
- package/engine/api-contracts/index.js +100 -0
- package/engine/api-contracts/orchestration.js +18 -5
- package/engine/api-contracts/pull-requests.js +37 -6
- package/engine/api-contracts/qa-process.js +29 -6
- package/engine/api-contracts/work-plan-prd.js +21 -1
- package/engine/cloud/contract.js +212 -0
- package/engine/cloud/index.js +159 -0
- package/engine/{execution-model.js → core/execution-model.js} +1 -1
- package/engine/{features.js → core/features.js} +4 -4
- package/engine/{operator-identity.js → core/operator-identity.js} +1 -1
- package/engine/{queries.js → core/queries.js} +201 -36
- package/engine/{safe-expr.js → core/safe-expr.js} +1 -1
- package/engine/{shared.js → core/shared.js} +1637 -175
- package/engine/{stdio-timestamps.js → core/stdio-timestamps.js} +1 -1
- package/engine/{untrusted-fence.js → core/untrusted-fence.js} +3 -3
- package/engine/db/index.js +11 -2
- package/engine/db/migrations/002-dispatches.js +3 -3
- package/engine/db/migrations/003-work-items.js +1 -1
- package/engine/db/migrations/004-pull-requests.js +1 -1
- package/engine/db/migrations/006-metrics.js +1 -1
- package/engine/db/migrations/007-watches.js +2 -2
- package/engine/db/migrations/008-small-state.js +1 -1
- package/engine/db/migrations/009-qa.js +1 -1
- package/engine/db/migrations/010-pr-links.js +1 -1
- package/engine/db/migrations/011-remaining-state.js +1 -1
- package/engine/db/migrations/012-steering-deliveries.js +2 -2
- package/engine/db/migrations/013-backfill-broken-note-links.js +1 -1
- package/engine/db/migrations/014-pr-fix-target-prefs.js +2 -2
- package/engine/db/migrations/015-plans-prds.js +0 -0
- package/engine/db/migrations/018-sql-only-cutover.js +2 -2
- package/engine/db/migrations/021-archived-work-items.js +1 -1
- package/engine/db/migrations/022-global-cc-session.js +1 -1
- package/engine/db/migrations/023-engine-state.js +1 -1
- package/engine/db/migrations/025-malformed-work-item-phantoms.js +1 -1
- package/engine/db/migrations/027-review-learning-lifecycle.js +1 -1
- package/engine/db/migrations/029-repair-reused-versions.js +20 -0
- package/engine/db/migrations/031-pr-author-identity.js +137 -0
- package/engine/{consolidation.js → memory/consolidation.js} +6 -6
- package/engine/{kb-sweep-runner.js → memory/kb-sweep-runner.js} +2 -2
- package/engine/{kb-sweep.js → memory/kb-sweep.js} +9 -7
- package/engine/{memory-retrieval.js → memory/memory-retrieval.js} +46 -4
- package/engine/{memory-store.js → memory/memory-store.js} +3 -3
- package/engine/{promotion.js → memory/promotion.js} +3 -3
- package/engine/{review-learning-backfill.js → memory/review-learning-backfill.js} +6 -6
- package/engine/{review-learning.js → memory/review-learning.js} +10 -5
- package/engine/{diagnostics-memory.js → observability/diagnostics-memory.js} +1 -1
- package/engine/{logs-store.js → observability/logs-store.js} +5 -5
- package/engine/{metrics-store.js → observability/metrics-store.js} +4 -4
- package/engine/{check-status.js → operations/check-status.js} +3 -3
- package/engine/{cli.js → operations/cli.js} +271 -113
- package/engine/{distribution.js → operations/distribution.js} +5 -6
- package/engine/{cleanup.js → orchestration/cleanup.js} +72 -45
- package/engine/{cooldown.js → orchestration/cooldown.js} +5 -5
- package/engine/{dispatch-events.js → orchestration/dispatch-events.js} +2 -2
- package/engine/{dispatch.js → orchestration/dispatch.js} +129 -36
- package/engine/orchestration/failed-scheduled-cleanup.js +274 -0
- package/engine/{lifecycle.js → orchestration/lifecycle.js} +198 -90
- package/engine/{meeting.js → orchestration/meeting.js} +6 -16
- package/engine/{pipeline.js → orchestration/pipeline.js} +12 -12
- package/engine/{pre-dispatch-eval.js → orchestration/pre-dispatch-eval.js} +10 -9
- package/engine/{routing.js → orchestration/routing.js} +3 -3
- package/engine/{schedule-bootstrap.js → orchestration/schedule-bootstrap.js} +4 -4
- package/engine/{scheduler.js → orchestration/scheduler.js} +38 -8
- package/engine/{timeout.js → orchestration/timeout.js} +158 -109
- package/engine/{db-events.js → persistence/db-events.js} +2 -2
- package/engine/{dispatch-store.js → persistence/dispatch-store.js} +7 -7
- package/engine/{inbox-store.js → persistence/inbox-store.js} +2 -2
- package/engine/{note-link-backfill.js → persistence/note-link-backfill.js} +4 -4
- package/engine/{pr-fix-target-store.js → persistence/pr-fix-target-store.js} +8 -8
- package/engine/{pull-requests-store.js → persistence/pull-requests-store.js} +21 -7
- package/engine/{small-state-store.js → persistence/small-state-store.js} +31 -31
- package/engine/persistence/state-operations.js +350 -0
- package/engine/{steering-store.js → persistence/steering-store.js} +6 -6
- package/engine/{issues.js → planning/issues.js} +2 -2
- package/engine/{plan-prd-validation.js → planning/plan-prd-validation.js} +8 -2
- package/engine/planning/prd-result-sidecar.js +190 -0
- package/engine/{prd-store.js → planning/prd-store.js} +17 -17
- package/engine/{project-discovery.js → planning/project-discovery.js} +5 -5
- package/engine/{projects.js → planning/projects.js} +10 -10
- package/engine/{resolve-area.js → planning/resolve-area.js} +1 -1
- package/engine/{work-item-validation.js → planning/work-item-validation.js} +39 -3
- package/engine/{work-items-store.js → planning/work-items-store.js} +29 -21
- package/engine/{keep-process-sweep.js → processes/keep-process-sweep.js} +57 -17
- package/engine/{managed-spawn-launcher.js → processes/managed-spawn-launcher.js} +3 -3
- package/engine/{managed-spawn.js → processes/managed-spawn.js} +97 -46
- package/engine/{process-utils.js → processes/process-utils.js} +599 -55
- package/engine/{abandoned-pr-reconciliation.js → providers/abandoned-pr-reconciliation.js} +17 -7
- package/engine/{comment-classifier.js → providers/comment-classifier.js} +85 -17
- package/engine/{comment-format.js → providers/comment-format.js} +5 -5
- package/engine/{gh-comment.js → providers/gh-comment.js} +15 -15
- package/engine/{gh-token.js → providers/gh-token.js} +4 -4
- package/engine/{github.js → providers/github.js} +131 -54
- package/engine/{pr-action.js → providers/pr-action.js} +13 -12
- package/engine/{pr-clone-keep.js → providers/pr-clone-keep.js} +7 -7
- package/engine/{pr-devbox.js → providers/pr-devbox.js} +6 -6
- package/engine/{pr-fix-target.js → providers/pr-fix-target.js} +13 -13
- package/engine/{pr-remote-patch.js → providers/pr-remote-patch.js} +4 -4
- package/engine/{pr-resolve.js → providers/pr-resolve.js} +7 -7
- package/engine/{pr-temp-clone.js → providers/pr-temp-clone.js} +5 -5
- package/engine/{pr-track.js → providers/pr-track.js} +11 -13
- package/engine/{shared-branch-pr-reconcile.js → providers/shared-branch-pr-reconcile.js} +4 -4
- package/engine/qa/auto-prd-qa.js +313 -0
- package/engine/{qa-from-prd.js → qa/from-prd.js} +42 -12
- package/engine/qa/prd-session.js +240 -0
- package/engine/{qa-process-validation.js → qa/process-validation.js} +14 -9
- package/engine/{qa-runbooks.js → qa/runbooks.js} +1 -1
- package/engine/{qa-runs.js → qa/runs.js} +286 -15
- package/engine/{qa-sessions.js → qa/sessions.js} +595 -49
- package/engine/qa/visual-journey.js +654 -0
- package/engine/{qa-runners.js → qa-runners/index.js} +7 -7
- package/engine/qa-runners/maestro.js +3 -3
- package/engine/qa-runners/playwright.js +2 -2
- package/engine/{restart-health.js → recovery/restart-health.js} +48 -4
- package/engine/recovery/stop-stack.js +607 -0
- package/engine/{supervisor.js → recovery/supervisor.js} +105 -175
- package/engine/{watchdog.js → recovery/watchdog.js} +136 -13
- package/engine/runtimes/claude.js +14 -12
- package/engine/runtimes/codex.js +8 -6
- package/engine/runtimes/copilot.js +17 -16
- package/engine/{watch-actions.js → watches/actions.js} +13 -13
- package/engine/{watches.js → watches/index.js} +43 -32
- package/engine/{watches-store.js → watches/store.js} +4 -4
- package/engine/{create-pr-worktree.js → worktrees/create-pr.js} +1 -1
- package/engine/{worktree-gc.js → worktrees/gc.js} +70 -22
- package/engine/worktrees/inventory.js +671 -0
- package/engine/{live-checkout.js → worktrees/live-checkout.js} +4 -4
- package/engine/{worktree-pool.js → worktrees/pool.js} +2 -2
- package/engine/{worktree-preflight.js → worktrees/preflight.js} +1 -0
- package/engine/worktrees/quarantine-refs.js +173 -0
- package/engine.js +1137 -208
- package/minions.js +147 -77
- package/package.json +10 -6
- package/playbooks/_pr-description-audit.md +110 -78
- package/playbooks/build-fix-complex.md +2 -0
- package/playbooks/fix.md +16 -12
- package/playbooks/implement-shared.md +2 -0
- package/playbooks/implement.md +19 -20
- package/playbooks/plan-to-prd.md +18 -3
- package/playbooks/qa-session-draft.md +136 -1
- package/playbooks/qa-session-execute.md +80 -2
- package/playbooks/qa-session-setup.md +17 -1
- package/playbooks/qa-validate.md +1 -1
- package/playbooks/setup.md +2 -0
- package/playbooks/shared-rules.md +25 -32
- package/playbooks/templates/followup-dispatch.md +4 -3
- package/playbooks/verify.md +1 -1
- package/prompts/cc-system.md +19 -27
- package/watch-plugins/README.md +92 -0
- package/watch-plugins/ado-author-prs.js +336 -0
- package/watch-plugins/gh-author-prs.js +375 -0
- package/watch-plugins/http.js +474 -0
- package/watch-plugins/teams-channel.js +869 -0
- package/docs/dev-composite-workflow.md +0 -101
- package/docs/pr-screenshots/pr-886/after-single-header.png +0 -0
- package/docs/pr-screenshots/pr-886/before-duplicate-header.png +0 -0
- package/docs/pr-screenshots/pr-895/01-cancellation-reason-detail.png +0 -0
- package/docs/pr-screenshots/pr-899/worker-pool-worktrees-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-899/worker-pool-worktrees-BEFORE.png +0 -0
- package/docs/pr-screenshots/pr-901/projects-tab-default.png +0 -0
- package/docs/pr-screenshots/pr-901/projects-tab-fmf-selected.png +0 -0
- package/docs/pr-screenshots/pr-916/model-picker-AFTER-crop.png +0 -0
- package/docs/pr-screenshots/pr-916/model-picker-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-916/model-picker-BEFORE-crop.png +0 -0
- package/docs/pr-screenshots/pr-916/model-picker-BEFORE.png +0 -0
- package/docs/pr-screenshots/pr-916/model-picker-dropdown-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-979/auto-fix-pane-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-979/auto-fix-pane-BEFORE.png +0 -0
- package/docs/pr-screenshots/pr-985/pr-column-em-dash-AFTER.png +0 -0
- package/docs/pr-screenshots/pr-985/pr-column-em-dash-BEFORE.png +0 -0
- package/docs/visual-evidence-ci.md +0 -103
- package/engine/bridge.js +0 -379
- package/engine/quarantine-refs.js +0 -103
- package/engine/state-operations.js +0 -178
- /package/engine/{steering-constraints.js → agents/steering-constraints.js} +0 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Cloud agent dispatch (Agency / Copilot cloud)
|
|
2
|
+
|
|
3
|
+
**Status: Phase 1 — surface + provider seam only. No provider ships yet, so
|
|
4
|
+
every cloud-declared work item is currently refused, loudly and non-retryably.**
|
|
5
|
+
|
|
6
|
+
## Why this exists
|
|
7
|
+
|
|
8
|
+
Minions is being wired as a *front-door* to remote coding agents. The observed
|
|
9
|
+
result that motivated it: a one-line intent ("in loop android, the builds rely
|
|
10
|
+
on droidx86 — replace it with droidx64") produces a mediocre cloud PR, but the
|
|
11
|
+
same intent run through a local Minions investigation first — which expands it
|
|
12
|
+
into scoped instructions naming exact files, line numbers, and which references
|
|
13
|
+
to change vs. deliberately leave alone — produces a markedly better one.
|
|
14
|
+
|
|
15
|
+
So the value is not "run agents in the cloud." It is **local investigation +
|
|
16
|
+
prompt rendering, remote execution**. The prompt Minions already builds is the
|
|
17
|
+
product; the cloud provider is just where it gets sent.
|
|
18
|
+
|
|
19
|
+
## Why cloud is an execution surface, not a runtime
|
|
20
|
+
|
|
21
|
+
`engine/runtimes/contract.js` describes a **local CLI process**: argv building,
|
|
22
|
+
prompt-on-stdin, PID tracking, stdout stream parsing, session resume, per-turn
|
|
23
|
+
cost accounting. A remote coding agent has none of that — you submit a task, get
|
|
24
|
+
an opaque handle, poll it, and a PR eventually appears.
|
|
25
|
+
|
|
26
|
+
Modelling it as a runtime would force every adapter method to lie (`buildArgs`
|
|
27
|
+
for a process that never spawns, `parseStreamChunk` for a stream that does not
|
|
28
|
+
exist) *and* would drag cloud dispatch through the local spawn, timeout, and
|
|
29
|
+
worktree machinery it specifically needs to avoid.
|
|
30
|
+
|
|
31
|
+
Instead cloud is a third value of the existing `EXECUTION_SURFACE` enum, which
|
|
32
|
+
already answers the load-bearing question "does this dispatch need a checkout?":
|
|
33
|
+
|
|
34
|
+
| Surface | Checkout | Local process | Branch | Deliverable |
|
|
35
|
+
|---|---|---|---|---|
|
|
36
|
+
| `repo` (default) | worktree or live checkout | yes | `work/<wi-id>` | commits in that checkout |
|
|
37
|
+
| `control-plane` | none | yes (runs at `MINIONS_DIR`) | none | Minions/REST + `gh`/`az` calls |
|
|
38
|
+
| `cloud` | none | **no** | none (remote agent pushes its own) | a PR Minions adopts |
|
|
39
|
+
|
|
40
|
+
## Declaring it
|
|
41
|
+
|
|
42
|
+
Structured and fail-closed, exactly like `control-plane` — never inferred from
|
|
43
|
+
prose:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"type": "implement",
|
|
48
|
+
"title": "Replace droidx86 reliance with droidx64 in Loop Android",
|
|
49
|
+
"meta": {
|
|
50
|
+
"executionSurface": "cloud",
|
|
51
|
+
"cloudProvider": "agency"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`meta.cloudProvider` is optional; resolution is most-specific-first:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
item.meta.cloudProvider → project.cloudProvider → engine.cloudProvider
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
When none is declared, or the declared id is not registered, or the provider
|
|
63
|
+
reports itself unconfigured for the project, the dispatch is **refused** with
|
|
64
|
+
non-retryable `FAILURE_CLASS.CLOUD_PROVIDER_UNAVAILABLE`.
|
|
65
|
+
|
|
66
|
+
### Eligible work types
|
|
67
|
+
|
|
68
|
+
`shared.CLOUD_ELIGIBLE_TYPES` = `implement`, `fix`, `test`, `docs`.
|
|
69
|
+
|
|
70
|
+
The allowlist asks a different question than the control-plane one. Control-plane
|
|
71
|
+
asks *"can this run with no checkout at all?"*; cloud asks *"does this produce a
|
|
72
|
+
PR that a remote agent can author?"* The two sets are deliberately **disjoint**,
|
|
73
|
+
and a test enforces that.
|
|
74
|
+
|
|
75
|
+
Notable refusals and why:
|
|
76
|
+
|
|
77
|
+
- **`review` / `verify`** — their entire deliverable is validation: build the
|
|
78
|
+
code, run the tests, inspect the tree. A remote agent that cannot run your
|
|
79
|
+
local validation build produces a *worse* review, not a cheaper one. This is
|
|
80
|
+
the single most common objection to cloud agents from people who use them, and
|
|
81
|
+
the allowlist encodes it rather than leaving it to judgment.
|
|
82
|
+
- **`build-fix-complex`** — needs live CI logs and the PR's own branch.
|
|
83
|
+
- **`implement:large`** — decomposed into children first; the *children* may go
|
|
84
|
+
to the cloud.
|
|
85
|
+
- **`setup`** — produces no PR and usually drives local processes
|
|
86
|
+
(`managed_spawn`), which a remote agent cannot leave running on this machine.
|
|
87
|
+
- **`ask` / `explore` / `meeting`** — read-only; their value is output text,
|
|
88
|
+
which the cloud surface does not stream back.
|
|
89
|
+
|
|
90
|
+
## Fail-closed behavior, end to end
|
|
91
|
+
|
|
92
|
+
Every gate degrades toward *today's behavior* (a local dispatch) or toward a
|
|
93
|
+
loud refusal — never toward silently shipping work somewhere unexpected:
|
|
94
|
+
|
|
95
|
+
- An **invalid or ineligible** `executionSurface` declaration is rejected at
|
|
96
|
+
`POST /api/work-items`, in `validateScheduleDefinition`, and again in
|
|
97
|
+
`spawnAgent` (`FAILURE_CLASS.INVALID_EXECUTION_SURFACE`, non-retryable).
|
|
98
|
+
- `shared.isCloudExecution()` returns `false` for anything invalid, so a
|
|
99
|
+
malformed opt-in **degrades to a normal local dispatch** — the behavior the
|
|
100
|
+
item would have had anyway.
|
|
101
|
+
- A **valid** cloud declaration with no usable provider is **refused**, not
|
|
102
|
+
downgraded: the engine never runs cloud-declared work locally, because a local
|
|
103
|
+
agent and a remote agent produce different work, in different checkouts, under
|
|
104
|
+
different identities.
|
|
105
|
+
- A provider whose `isConfigured()` **throws** is treated as unavailable. One
|
|
106
|
+
that cannot answer the cheap question is not one we hand a work item to.
|
|
107
|
+
- `normalizeSessionStatus` **rejects** `state: "succeeded"` with no `prUrl` — a
|
|
108
|
+
cloud dispatch succeeds only when it produced a PR Minions can adopt.
|
|
109
|
+
|
|
110
|
+
## Allocation-time gating
|
|
111
|
+
|
|
112
|
+
`engine.js#dispatchPending` resolves the surface once, fail-closed, into
|
|
113
|
+
`_itemSkipsCheckoutAllocation` — *"does this item allocate a local checkout at
|
|
114
|
+
all?"* — rather than testing for control-plane specifically. Every gate that
|
|
115
|
+
exists to protect a local checkout is skipped for both no-checkout surfaces:
|
|
116
|
+
|
|
117
|
+
| Gate | Why it cannot apply |
|
|
118
|
+
|---|---|
|
|
119
|
+
| live-mode concurrency cap (1 mutating dispatch per project) | consumes no operator checkout |
|
|
120
|
+
| operator branch hold | derives no local branch at all |
|
|
121
|
+
| worktree free-space preflight | never runs `git worktree add` |
|
|
122
|
+
| live-checkout dirty / stale-base precheck | never reads the operator tree |
|
|
123
|
+
|
|
124
|
+
Keying on the *surface* rather than on one surface's name is deliberate: a
|
|
125
|
+
second no-checkout surface added later cannot inherit repo-shaped gates by
|
|
126
|
+
omission, and a cloud item can never pend as `worktree_low_disk_space` when the
|
|
127
|
+
honest answer is `cloud-provider-unavailable`. A regression test asserts each
|
|
128
|
+
gate is individually guarded, with a proximity bound so a gate that loses its
|
|
129
|
+
guard cannot pass by borrowing a neighbour's.
|
|
130
|
+
|
|
131
|
+
## Provider contract
|
|
132
|
+
|
|
133
|
+
`engine/cloud/contract.js`, API version 1. Register through
|
|
134
|
+
`engine/cloud/index.js`; orchestration resolves via `resolveCloudProvider()` /
|
|
135
|
+
`resolveCloudDispatchTarget()` and **never** imports a concrete provider or
|
|
136
|
+
branches on a provider id — same discipline as the runtime registry.
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
isConfigured({ config, project }) -> boolean (sync, no network)
|
|
140
|
+
createSession({ project, repo, baseBranch, prompt,
|
|
141
|
+
workItem, workdir }) -> { sessionId, url?, providerRef? }
|
|
142
|
+
pollSession(sessionId, { project }) -> { state, prUrl?, summary?, error?, raw? }
|
|
143
|
+
cancelSession(sessionId, { reason, project }) -> void (required iff capabilities.cancel)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`state ∈ queued | running | succeeded | failed | cancelled` — deliberately
|
|
147
|
+
smaller than any provider's native state machine, so orchestration never learns
|
|
148
|
+
provider-specific vocabulary.
|
|
149
|
+
|
|
150
|
+
Registration is strict: a missing required method, a wrong/absent `apiVersion`,
|
|
151
|
+
or a provider that advertises `capabilities.cancel` without implementing
|
|
152
|
+
`cancelSession()` all throw at registration rather than at dispatch.
|
|
153
|
+
|
|
154
|
+
## Planned providers
|
|
155
|
+
|
|
156
|
+
| id | Status | Notes |
|
|
157
|
+
|---|---|---|
|
|
158
|
+
| `ado-assign` | planned, Phase 3 | Automates the current manual flow: create an ADO work item carrying the rendered dispatch instructions, assign it to the Agency identity, poll for the linked PR. Buildable today with the existing `az` / `engine/ado` integration — **no unknown API**. |
|
|
159
|
+
| `agency` | blocked | Native Agency Hub session API. Needs the endpoint, auth model ("a different permissions protocol" per the Agency team), repo/branch binding, and PR linkage. The provider seam means this drops in without touching orchestration. |
|
|
160
|
+
| `github-copilot` | optional | The v1 path — assign a GitHub issue to the Copilot coding agent. The only one testable on a public repo. |
|
|
161
|
+
|
|
162
|
+
## Configuration
|
|
163
|
+
|
|
164
|
+
| Field | Default | Meaning |
|
|
165
|
+
|---|---|---|
|
|
166
|
+
| `engine.cloudProvider` | *(unset)* | Fleet-wide default provider id. Unset ⇒ cloud dispatch unavailable fleet-wide. Settings → Agents Runtime & Models → **Cloud dispatch**. |
|
|
167
|
+
| `project.cloudProvider` | *(unset)* | Per-project override. |
|
|
168
|
+
| `meta.cloudProvider` | *(unset)* | Per-work-item override (most specific). |
|
|
169
|
+
|
|
170
|
+
`engine.cloudProvider` is validated against the **live registry**, not accepted
|
|
171
|
+
as free text — an id that resolves to nothing cannot be saved. The Settings
|
|
172
|
+
control (Agents Runtime & Models → **Cloud dispatch**) is a selector populated
|
|
173
|
+
from `GET /api/cloud-providers`; with no provider registered it disables itself
|
|
174
|
+
and states why, rather than offering a value the server would reject. A stored
|
|
175
|
+
value that is no longer registered stays selectable so an operator can clear it,
|
|
176
|
+
and an unreachable endpoint degrades without touching the stored config.
|
|
177
|
+
|
|
178
|
+
### Discovery
|
|
179
|
+
|
|
180
|
+
`GET /api/cloud-providers` → `{ providers: [{ name, capabilities }] }` — the
|
|
181
|
+
cloud counterpart to `GET /api/runtimes`. Read-only, no input.
|
|
182
|
+
|
|
183
|
+
## Still to come (Phases 2–5)
|
|
184
|
+
|
|
185
|
+
- **Phase 2 — dispatch + lifecycle.** `spawnAgent` renders the prompt and calls
|
|
186
|
+
`createSession`; a `pollCloudDispatches` tick phase adopts the resulting PR
|
|
187
|
+
into the existing PR record so the ADO/GitHub pollers and review/fix loops
|
|
188
|
+
take over unchanged. Two constraints already identified in the code:
|
|
189
|
+
`engine/orchestration/timeout.js` treats any processless active dispatch as a
|
|
190
|
+
stale orphan after `engine.heartbeatTimeout` (5 min), so cloud dispatches must
|
|
191
|
+
be excluded and given their own `cloudSessionTimeout` → `cancelSession`; and
|
|
192
|
+
they must not consume `engine.maxConcurrent` local slots.
|
|
193
|
+
- **Phase 3 — the `ado-assign` provider.**
|
|
194
|
+
- **Phase 4 — investigate-then-delegate.** A local `explore` agent runs first and
|
|
195
|
+
produces the scoped instructions; the engine then creates the cloud WI carrying
|
|
196
|
+
them. This is the actual product.
|
|
197
|
+
- **Phase 5 — dashboard surfaces + the native `agency` provider.**
|
|
198
|
+
|
|
199
|
+
## Non-goals
|
|
200
|
+
|
|
201
|
+
- Cloud never becomes a default or a fallback for local dispatch.
|
|
202
|
+
- No attempt to run validation builds in the cloud. Local `verify` / `test` work
|
|
203
|
+
items still own validation, and the cloud PR flows into them.
|
|
204
|
+
- No new runtime adapter.
|
package/docs/command-center.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
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 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`). 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/agents/llm.js` `callLLM({ direct: true })` → runtime session persisted in `engine/state.db` (`cc_sessions`). 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
|
|
|
@@ -91,13 +91,13 @@ All bundled runtimes currently enable it; a future adapter without image support
|
|
|
91
91
|
| copilot | `true` | Images materialized to tmp files and passed as `--attachment <path>` (W-mqv7324u0021db5d). |
|
|
92
92
|
| codex | `true` | Images materialized to tmp files and passed as repeatable `--image <path>` arguments. |
|
|
93
93
|
|
|
94
|
-
**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.
|
|
94
|
+
**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/agents/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/agents/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.
|
|
95
95
|
|
|
96
96
|
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. The one-off process does not resume the live worker's process-scoped ACP session ID: it starts a transport-compatible fresh session and receives prior conversation context through the existing transcript carryover. Every parsed direct-runtime event, including startup and reasoning events, resets the five-minute silence watchdog; the safety bound therefore still catches a truly silent process without killing an image turn that is making progress.
|
|
97
97
|
|
|
98
|
-
`_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.
|
|
98
|
+
`_resolveImageOpts` (in `engine/agents/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.
|
|
99
99
|
|
|
100
|
-
**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.
|
|
100
|
+
**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/core/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.
|
|
101
101
|
|
|
102
102
|
## Operator-checkout write authority (interactive turns)
|
|
103
103
|
|
|
@@ -112,7 +112,7 @@ The "Create PR" chip offered by CC after a local edit follows the **same checkou
|
|
|
112
112
|
- **`checkoutMode: 'live'`** (default for live-mode projects): unchanged behavior — the agent commits and pushes directly in the operator's live checkout as the CC system prompt instructs.
|
|
113
113
|
- **`checkoutMode: 'worktree'`** (the default for most projects): the engine routes the changes through an isolated worktree deterministically and server-side so nothing depends on the LLM running git correctly.
|
|
114
114
|
|
|
115
|
-
For worktree-mode projects the flow uses **`engine/create-pr
|
|
115
|
+
For worktree-mode projects the flow uses **`engine/worktrees/create-pr.js`**:
|
|
116
116
|
|
|
117
117
|
1. `prepareCreatePrWorktree` captures the live checkout's uncommitted changes (tracked diff via `git diff --binary HEAD` + untracked files) and applies them into a fresh worktree on a `cc-pr/<project>-<uid>` branch. The worktree is populated and verified **before** the live checkout is touched; on apply failure the worktree is removed and the live checkout is left untouched (operator never loses work).
|
|
118
118
|
2. The agent then commits, pushes, and opens the PR from inside the worktree.
|
|
@@ -122,8 +122,8 @@ Dashboard endpoints added by this feature:
|
|
|
122
122
|
- `POST /api/pr-action/prepare-create-pr-worktree` — called before the agent's git operations
|
|
123
123
|
- `POST /api/pr-action/cleanup-create-pr-worktree` — called after the PR is open (or on failure)
|
|
124
124
|
|
|
125
|
-
The `buildCreatePrFollowups` helper in `engine/pr-action.js` emits worktree-aware instructions for worktree mode and the legacy live-checkout instructions for live mode; `dashboard.js /api/pr-action/offer-create-pr` resolves the checkout mode and passes it through.
|
|
125
|
+
The `buildCreatePrFollowups` helper in `engine/providers/pr-action.js` emits worktree-aware instructions for worktree mode and the legacy live-checkout instructions for live mode; `dashboard.js /api/pr-action/offer-create-pr` resolves the checkout mode and passes it through.
|
|
126
126
|
|
|
127
127
|
## Per-turn surfacing pipeline
|
|
128
128
|
|
|
129
|
-
CC handler generates `ccTurnId = 'cct-' + shared.uid()` per request; injected into sysprompt AND prompt body via `_ccTurnHeaderPart(turnId)` (load-bearing: on resumed sessions `engine/llm.js` skips re-sending the sysprompt, so without body injection CC keeps the stale turn ID). Handler reads via `_readCcTurnIdHeader(req)` and calls `_recordCcTurnCreation(turnId, ...)` on success. End-of-turn: `_buildSyntheticActionResultsForTurn` produces synthetic `{action, result}` pairs (`_serverExecuted: true`). Client renders as standalone `role='action'` messages outside the assistant bubble. TTL: 5 min. Endpoints wired: `/api/work-items`, `/api/notes`, `/api/plan`, `/api/knowledge`, `/api/watches`.
|
|
129
|
+
CC handler generates `ccTurnId = 'cct-' + shared.uid()` per request; injected into sysprompt AND prompt body via `_ccTurnHeaderPart(turnId)` (load-bearing: on resumed sessions `engine/agents/llm.js` skips re-sending the sysprompt, so without body injection CC keeps the stale turn ID). Handler reads via `_readCcTurnIdHeader(req)` and calls `_recordCcTurnCreation(turnId, ...)` on success. End-of-turn: `_buildSyntheticActionResultsForTurn` produces synthetic `{action, result}` pairs (`_serverExecuted: true`). Client renders as standalone `role='action'` messages outside the assistant bubble. TTL: 5 min. Endpoints wired: `/api/work-items`, `/api/notes`, `/api/plan`, `/api/knowledge`, `/api/watches`.
|
|
@@ -13,7 +13,7 @@ The engine delivers the absolute path two ways before spawning each agent:
|
|
|
13
13
|
spawns
|
|
14
14
|
- The same path appears in the rendered playbook prompt
|
|
15
15
|
|
|
16
|
-
Path shape (resolved by `shared.dispatchCompletionReportPath()` in `engine/shared.js`):
|
|
16
|
+
Path shape (resolved by `shared.dispatchCompletionReportPath()` in `engine/core/shared.js`):
|
|
17
17
|
|
|
18
18
|
```
|
|
19
19
|
<MINIONS_DIR>/engine/completions/<dispatch-id>.json
|
|
@@ -39,7 +39,7 @@ nonce never enters reusable process state or untrusted task content. The engine
|
|
|
39
39
|
generates it with `crypto.randomBytes(16).toString('hex')` in
|
|
40
40
|
`engine.js:spawnAgent()` and stores it on the in-memory active-process record.
|
|
41
41
|
The agent is required to copy the value verbatim into the report's `nonce`
|
|
42
|
-
field. On parse, `engine/lifecycle.js:runPostCompletionHooks()` compares
|
|
42
|
+
field. On parse, `engine/orchestration/lifecycle.js:runPostCompletionHooks()` compares
|
|
43
43
|
`report.nonce` against the in-memory value:
|
|
44
44
|
|
|
45
45
|
- **Match** — the report is trusted and processed normally.
|
|
@@ -60,6 +60,25 @@ Security event log lines are emitted on the `error` channel and are designed to
|
|
|
60
60
|
|
|
61
61
|
Do **not** invent, regenerate, or share the nonce across dispatches — each spawn gets a unique value.
|
|
62
62
|
|
|
63
|
+
### The report is also the completion boundary (W-ms9tcs3o01y03383)
|
|
64
|
+
|
|
65
|
+
The nonce does more than gate trust after the fact — combined with a confirmed
|
|
66
|
+
terminal process exit it *is* the signal that the prompt is finished.
|
|
67
|
+
`engine.js#resolveCloseCompletionBoundary` accepts a report only when the child
|
|
68
|
+
exited with code `0` **and** `shared.acceptCompletionReportForNonce` matches the
|
|
69
|
+
report's `nonce` to this dispatch's spawn value (carried across steering
|
|
70
|
+
resumes). When both hold, the engine releases any queued steering-resume
|
|
71
|
+
bookkeeping instead of spawning it, so a finished dispatch terminalizes on the
|
|
72
|
+
next event-loop turn rather than waiting out `engine.resumeHeartbeatTimeout`.
|
|
73
|
+
|
|
74
|
+
This is strictly additive to validation: a non-zero exit, a missing report, a
|
|
75
|
+
malformed report, or a missing/mismatched nonce all fall through to exactly the
|
|
76
|
+
failure and timeout paths described above. The same predicate backs the
|
|
77
|
+
resume-stall watchdog in `engine/orchestration/timeout.js`, so both finalizers
|
|
78
|
+
apply one trust boundary. Completed dispatch records record which path ran in
|
|
79
|
+
`completionPath` (`shared.DISPATCH_COMPLETION_PATH`); see
|
|
80
|
+
[timeouts-and-liveness.md](timeouts-and-liveness.md#completion-boundary-terminal-exit--a-dispatch-owned-report-w-ms9tcs3o01y03383).
|
|
81
|
+
|
|
63
82
|
## Top-level schema
|
|
64
83
|
|
|
65
84
|
```json
|
|
@@ -84,10 +103,10 @@ Do **not** invent, regenerate, or share the nonce across dispatches — each spa
|
|
|
84
103
|
| Field | Type | Notes |
|
|
85
104
|
|---|---|---|
|
|
86
105
|
| `status` | string | One of `success`, `partial`, `failed`. Aliases `done` / `complete` are accepted on read for `success`. The engine refuses to mark a work item done without a status. |
|
|
87
|
-
| `summary` | string | Short prose describing what changed and how it was validated. Truncated to 500 chars in dashboard surfaces (`engine/queries.js`). Do not summarize validation as "tests passed" — name the commands that ran. |
|
|
106
|
+
| `summary` | string | Short prose describing what changed and how it was validated. Truncated to 500 chars in dashboard surfaces (`engine/core/queries.js`). Do not summarize validation as "tests passed" — name the commands that ran. |
|
|
88
107
|
| `verdict` | string \| null | Required for review tasks: `approved` or `changes-requested`. `null` for non-review tasks. Aliases: `approve`, `request_changes`, `changes_requested`. |
|
|
89
108
|
| `pr` | string | PR URL, `PR-<number>`, or `N/A`. The engine uses this to attach the PR to the work item; missing-PR detection treats anything other than a recognizable URL/PR id as missing unless `noop: true` is set. |
|
|
90
|
-
| `failure_class` | string | One of the `failure_class` enum values below, or `N/A`. Drives retry policy in `engine/dispatch.js`. |
|
|
109
|
+
| `failure_class` | string | One of the `failure_class` enum values below, or `N/A`. Drives retry policy in `engine/orchestration/dispatch.js`. |
|
|
91
110
|
| `retryable` | boolean | `true` if the engine should auto-retry the dispatch on failure. Overrides the default per-class retry policy when present. |
|
|
92
111
|
| `needs_rerun` | boolean | `true` if the same work needs to be re-dispatched (vs. retried). Used by build-fix and review-fix loops. |
|
|
93
112
|
| `artifacts` | array | Durable artifacts the agent created or updated; surfaces in the dashboard work-item detail modal. See [Artifacts](#artifacts). |
|
|
@@ -131,11 +150,11 @@ derive an identity from reviewer prose.
|
|
|
131
150
|
"project": "minions-opg",
|
|
132
151
|
"authorAgent": "dallas",
|
|
133
152
|
"applicability": "project-team",
|
|
134
|
-
"files": ["engine/lifecycle.js"],
|
|
153
|
+
"files": ["engine/orchestration/lifecycle.js"],
|
|
135
154
|
"symbols": ["normalizeReviewLearningCompletionPayload"],
|
|
136
155
|
"tags": ["completion-contract"],
|
|
137
156
|
"evidence": [
|
|
138
|
-
"engine/lifecycle.js:5742",
|
|
157
|
+
"engine/orchestration/lifecycle.js:5742",
|
|
139
158
|
"test/unit/structured-completion.test.js:158"
|
|
140
159
|
],
|
|
141
160
|
"confidence": 0.92,
|
|
@@ -146,7 +165,7 @@ derive an identity from reviewer prose.
|
|
|
146
165
|
{
|
|
147
166
|
"id": "M-review-lesson-1",
|
|
148
167
|
"outcome": "applied",
|
|
149
|
-
"evidence": "engine/lifecycle.js:5742 validates each optional field independently."
|
|
168
|
+
"evidence": "engine/orchestration/lifecycle.js:5742 validates each optional field independently."
|
|
150
169
|
}
|
|
151
170
|
]
|
|
152
171
|
}
|
|
@@ -270,11 +289,11 @@ P-mqyp0009y025z6a7. An optional `invalidates: string[]` field in the completion
|
|
|
270
289
|
- Each cancellation stamps `cancellationReason: "invalidated-by:<source-wi-id>"` and `cancelledAt` on the cancelled WI.
|
|
271
290
|
- A `work_items` state event is emitted for each successful cancellation so the dashboard reflects it.
|
|
272
291
|
|
|
273
|
-
**Implementation:** `engine/lifecycle.js#applyGoalInvalidation`, called from `runPostCompletionHooks` after the source WI is marked done.
|
|
292
|
+
**Implementation:** `engine/orchestration/lifecycle.js#applyGoalInvalidation`, called from `runPostCompletionHooks` after the source WI is marked done.
|
|
274
293
|
|
|
275
294
|
## `failure_class` enum
|
|
276
295
|
|
|
277
|
-
Defined in `engine/shared.js` as `FAILURE_CLASS`. Use the canonical hyphenated string.
|
|
296
|
+
Defined in `engine/core/shared.js` as `FAILURE_CLASS`. Use the canonical hyphenated string.
|
|
278
297
|
|
|
279
298
|
| Value | When to use | Default escalation |
|
|
280
299
|
|---|---|---|
|
|
@@ -284,6 +303,7 @@ Defined in `engine/shared.js` as `FAILURE_CLASS`. Use the canonical hyphenated s
|
|
|
284
303
|
| `merge-conflict` | Git merge conflict in worktree or dependency | Retry same agent |
|
|
285
304
|
| `build-failure` | Compilation, lint, or test failure introduced by the agent | Retry same agent |
|
|
286
305
|
| `timeout` | Hard runtime timeout or stale-orphan timeout | Retry with fresh session |
|
|
306
|
+
| `engine-restart` | Dispatch process ownership was lost across an engine restart | Retry against the bounded infrastructure retry counter, not the semantic agent retry budget |
|
|
287
307
|
| `empty-output` | Agent produced no meaningful output | Flag for human review |
|
|
288
308
|
| `runtime-truncated-exit` | The runtime process exited `0` in the MIDDLE of a turn: its output holds a tool call that was started and never completed (observed: Copilot exiting right after starting a long `npm test`), and no completion report was written. Detected through the optional adapter hook `hasUnfinishedToolCall`. Distinct from `completion-report-missing`, which means the turn ended normally but the agent skipped the report. | Retry — a fresh attempt normally runs to completion |
|
|
289
309
|
| `runtime-session-stale` | A `--resume`d runtime died because its persisted session is unusable: the backing conversation is gone, or its persisted working directory no longer exists (the engine removed/recycled that worktree). Previously surfaced as an opaque `empty-output`. | Retry — the engine clears the cached session first |
|
|
@@ -311,6 +331,9 @@ Defined in `engine/shared.js` as `FAILURE_CLASS`. Use the canonical hyphenated s
|
|
|
311
331
|
| `live-checkout-stale-base` | Spawn-time defense found local `mainRef` ahead of `origin/<mainRef>`; forking would inherit committed contamination | Never retry — operator reconciles the local base first |
|
|
312
332
|
| `live-checkout-wrong-base` | New-branch fork requested but operator HEAD is on an unrelated branch, not the project base | Never retry — operator checks out `mainRef` first (or enable `liveCheckoutAutoBaseRepair`) |
|
|
313
333
|
| `invalid-workdir` | Dispatch carried a `meta.workdir` override that failed validation (absolute path, `..` segment, escape, etc.) | Never retry until the WI's `meta.workdir` is fixed |
|
|
334
|
+
| `invalid-execution-surface` | `meta.executionSurface` failed fail-closed validation: an unknown/non-string surface, `control-plane` on a work type that reads or mutates repository files, or `cloud` on a work type whose deliverable is not a PR. Each surface is an ALLOWLIST (`shared.CONTROL_PLANE_ELIGIBLE_TYPES` / `shared.CLOUD_ELIGIBLE_TYPES`), never inferred from prose. | Never retry — operator fixes or removes the field |
|
|
335
|
+
| `cloud-provider-unavailable` | The WI validly declared `meta.executionSurface: "cloud"` but no registered **and** configured cloud provider resolved. Structural, not connectivity — the engine never downgrades the refusal to a local dispatch. | Never retry — operator configures a provider or drops the declaration |
|
|
336
|
+
| `git-ref-lock` | `git fetch` lost a **local** ref-lock race on a shared remote-tracking ref (`cannot lock ref 'refs/remotes/origin/<main>'`). A serialization defect, deliberately not `network-error`, which previously hid the cause and burned retries. `shared.withSerializedRemoteRefRefresh` removes the in-process races; this covers the residual cross-process case. | Retry — the ref lock clears in seconds |
|
|
314
337
|
| `model-unavailable` | Requested model returned `overloaded_error` / 503 / service unavailable | Retry — engine swaps in the runtime-appropriate fallback model |
|
|
315
338
|
| `workspace-manifest-repo-forbidden` | Dispatch routed an agent to a repo not in its `workspace_manifest.allowed_repos`. See [Pre-spawn preflight vs agent failure](#pre-spawn-preflight-vs-agent-failure). | Never retry |
|
|
316
339
|
| `workspace-manifest-tool-forbidden` | Out-of-scope tool call caught by manifest enforcement at the runtime gate | Never retry as-is |
|
|
@@ -328,7 +351,7 @@ Use `"N/A"` when `status` is `success` or `partial` without a failure.
|
|
|
328
351
|
|
|
329
352
|
A handful of `failure_class` values are written by the **engine at dispatch time**, *before the agent process is ever spawned*. They are emitted from `spawnAgent` (`engine.js`) while it sets up the worktree, validates the checkout, or enforces the workspace manifest — the engine calls `completeDispatch(... DISPATCH_RESULT.ERROR ...)` and returns without launching any runtime. **A completion report carrying one of these classes does not imply the agent ran any work**; no prompt was sent, no edits were attempted, and there is no agent-authored `note` or output to interpret.
|
|
330
353
|
|
|
331
|
-
| `failure_class` | Pre-spawn trigger | `engine/shared.js` |
|
|
354
|
+
| `failure_class` | Pre-spawn trigger | `engine/core/shared.js` |
|
|
332
355
|
|---|---|---|
|
|
333
356
|
| `worktree-preflight` | Worktree placement validation failed: the worktree would nest inside the project, the resolved root collapsed to a drive root, or the base directory was missing. | `FAILURE_CLASS.WORKTREE_PREFLIGHT` (~`shared.js:4279`) |
|
|
334
357
|
| `live-checkout-dirty` | Live-checkout mode (`project.checkoutMode: 'live'`) found uncommitted changes in `project.localPath`; the engine never `reset`/`clean`s the operator tree, so it refuses to spawn. | `FAILURE_CLASS.LIVE_CHECKOUT_DIRTY` (~`shared.js:4289`) |
|
|
@@ -354,7 +377,7 @@ To signal a no-op:
|
|
|
354
377
|
}
|
|
355
378
|
```
|
|
356
379
|
|
|
357
|
-
Engine behavior when `noop: true` (`engine/lifecycle.js` `parseCompletionNoop` + `runPostCompletionHooks`):
|
|
380
|
+
Engine behavior when `noop: true` (`engine/orchestration/lifecycle.js` `parseCompletionNoop` + `runPostCompletionHooks`):
|
|
358
381
|
|
|
359
382
|
- The work item is marked `done` with the rationale stored as `_noopReason` (visible in the dashboard).
|
|
360
383
|
- The PR-attachment contract is **skipped**, so an empty `pr` field will not be flagged as a silent failure or auto-retried.
|
|
@@ -373,7 +396,7 @@ If the live PR branch did not advance, the completion must set `noop: true` and
|
|
|
373
396
|
"reviewFindingResolution": {
|
|
374
397
|
"findingId": "review-0123456789abcdef",
|
|
375
398
|
"disposition": "satisfied-elsewhere",
|
|
376
|
-
"currentCodeEvidence": "engine/lifecycle.js:4600 already enforces the condition; test/unit/example.test.js covers it."
|
|
399
|
+
"currentCodeEvidence": "engine/orchestration/lifecycle.js:4600 already enforces the condition; test/unit/example.test.js covers it."
|
|
377
400
|
}
|
|
378
401
|
}
|
|
379
402
|
```
|
|
@@ -405,7 +428,7 @@ optional `followups` array on its completion report:
|
|
|
405
428
|
}
|
|
406
429
|
```
|
|
407
430
|
|
|
408
|
-
Engine behavior (`engine/lifecycle.js` `processCompletionFollowups`):
|
|
431
|
+
Engine behavior (`engine/orchestration/lifecycle.js` `processCompletionFollowups`):
|
|
409
432
|
|
|
410
433
|
- Each entry is logged at `info`: `Followup audit (<agent> wi=<parent>): dispatched <wi_id> — <title> (comment=<id>) — <reason>`.
|
|
411
434
|
- If the claimed `wi_id` does not exist in any project SQL scope at parse time, a `warn` is emitted. This catches dispatches that were deduped to a 409 / 200-duplicate response, reverted, or never actually landed.
|
|
@@ -420,7 +443,7 @@ Both default to `false`. Together they let the agent override the engine's defau
|
|
|
420
443
|
- `retryable: false` — engine should not auto-retry; the failure is terminal for this dispatch.
|
|
421
444
|
- `needs_rerun: true` — even on `status: success`, the same work item should be re-dispatched (used by review-fix and build-fix loops where one pass cannot complete the work).
|
|
422
445
|
|
|
423
|
-
When the agent sets either field, it overrides the per-`failure_class` default in `isRetryableFailureReason()` (`engine/dispatch.js`).
|
|
446
|
+
When the agent sets either field, it overrides the per-`failure_class` default in `isRetryableFailureReason()` (`engine/orchestration/dispatch.js`).
|
|
424
447
|
|
|
425
448
|
## Artifacts
|
|
426
449
|
|
|
@@ -436,11 +459,11 @@ When the agent sets either field, it overrides the per-`failure_class` default i
|
|
|
436
459
|
| `path` | string | Relative repo path, absolute path, or URL. |
|
|
437
460
|
| `title` | string | Short label used in the dashboard list. |
|
|
438
461
|
|
|
439
|
-
The dashboard caps the rendered list at 20 artifacts per report (`engine/queries.js`). Include every durable artifact you created or updated — PRs, plan/PRD files, key source files, inbox notes — so the work-item detail modal can show them.
|
|
462
|
+
The dashboard caps the rendered list at 20 artifacts per report (`engine/core/queries.js`). Include every durable artifact you created or updated — PRs, plan/PRD files, key source files, inbox notes — so the work-item detail modal can show them.
|
|
440
463
|
|
|
441
464
|
## Validation and source-of-truth precedence
|
|
442
465
|
|
|
443
|
-
The engine reads completion signals in this order (`engine/lifecycle.js`):
|
|
466
|
+
The engine reads completion signals in this order (`engine/orchestration/lifecycle.js`):
|
|
444
467
|
|
|
445
468
|
1. The JSON report at `MINIONS_COMPLETION_REPORT` — sole structured-completion source. `parseCompletionReportFile()` requires a well-formed plain-object JSON with a `status` field; anything else is dropped with a warn log.
|
|
446
469
|
2. Runtime result events + the `[process-exit]` sentinel in `live-output.log` — used to confirm the dispatch actually exited and to capture the runtime's own result prose into `resultSummary`. These cannot supply structured fields (`pr`, `failure_class`, `retryable`, `noop`, `verdict`, `artifacts`, …); without a report on disk those signals are simply absent.
|
|
@@ -462,7 +485,7 @@ The legacy fenced ` ```completion ` block parser and the `task_complete` prose-s
|
|
|
462
485
|
"needs_rerun": false,
|
|
463
486
|
"artifacts": [
|
|
464
487
|
{"type": "pr", "path": "https://github.com/opg-microsoft/minions/pull/2360", "title": "PR-2360"},
|
|
465
|
-
{"type": "file", "path": "engine/spawn-agent.js", "title": "Stale-HEAD guard"},
|
|
488
|
+
{"type": "file", "path": "engine/agents/spawn-agent.js", "title": "Stale-HEAD guard"},
|
|
466
489
|
{"type": "file", "path": "test/unit/spawn-agent.test.js", "title": "New unit test"}
|
|
467
490
|
]
|
|
468
491
|
}
|
|
@@ -514,8 +537,8 @@ The legacy fenced ` ```completion ` block parser and the `task_complete` prose-s
|
|
|
514
537
|
|
|
515
538
|
## Related
|
|
516
539
|
|
|
517
|
-
- `engine/shared.js` — `FAILURE_CLASS`, `COMPLETION_FIELDS`, `dispatchCompletionReportPath()`
|
|
518
|
-
- `engine/lifecycle.js` — `parseCompletionReportFile()`, `parseCompletionNoop()`, `enforcePrAttachmentContract()`
|
|
519
|
-
- `engine/dispatch.js` — `isRetryableFailureReason()`, `writeFailedAgentReport()`
|
|
540
|
+
- `engine/core/shared.js` — `FAILURE_CLASS`, `COMPLETION_FIELDS`, `dispatchCompletionReportPath()`
|
|
541
|
+
- `engine/orchestration/lifecycle.js` — `parseCompletionReportFile()`, `parseCompletionNoop()`, `enforcePrAttachmentContract()`
|
|
542
|
+
- `engine/orchestration/dispatch.js` — `isRetryableFailureReason()`, `writeFailedAgentReport()`
|
|
520
543
|
- `docs/rfc-completion-json.md` — original RFC describing the protocol's design
|
|
521
544
|
- `playbooks/shared-rules.md` — the per-task "Completion Reports" instruction every playbook inherits
|
package/docs/constants.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Constants — No Magic Strings
|
|
2
2
|
|
|
3
|
-
Cross-cutting status / type / condition values are defined in [`engine/shared.js`](../engine/shared.js). Engine and dashboard code should use these constants instead of raw strings; source-inspection tests enforce the load-bearing comparisons.
|
|
3
|
+
Cross-cutting status / type / condition values are defined in [`engine/core/shared.js`](../engine/core/shared.js). Engine and dashboard code should use these constants instead of raw strings; source-inspection tests enforce the load-bearing comparisons.
|
|
4
4
|
|
|
5
5
|
```js
|
|
6
6
|
WI_STATUS = { PENDING, DISPATCHED, DONE, FAILED, PAUSED, QUEUED, DECOMPOSED, CANCELLED }
|
|
@@ -20,7 +20,7 @@ WATCH_CONDITION = { MERGED, BUILD_FAIL, BUILD_PASS, COMPLETED, FAILED, STATUS_CH
|
|
|
20
20
|
STAGE_ADVANCED, STUCK_IN_STAGE }
|
|
21
21
|
WATCH_ABSOLUTE_CONDITIONS = Set([MERGED, BUILD_FAIL, BUILD_PASS, COMPLETED, FAILED, CONCLUDED, APPROVED,
|
|
22
22
|
REJECTED, READY_FOR_MERGE, RETRY_LIMIT_REACHED, ALL_ITEMS_DONE, ITEM_FAILED_N_TIMES]) // fire-once
|
|
23
|
-
WATCH_TARGET_TYPE = { PR, WORK_ITEM, MEETING, PLAN, SCHEDULE, PIPELINE, DISPATCH, AGENT } // built-ins;
|
|
23
|
+
WATCH_TARGET_TYPE = { PR, WORK_ITEM, MEETING, PLAN, SCHEDULE, PIPELINE, DISPATCH, AGENT } // built-ins; watch-plugins/*.js may register more
|
|
24
24
|
WATCH_ACTION_TYPE = { NOTIFY, DISPATCH_WORK_ITEM, RUN_SKILL, WEBHOOK, MINIONS_API,
|
|
25
25
|
CANCEL_WORK_ITEM, TRIGGER_PIPELINE, ARCHIVE_PLAN, RESUME_PLAN, CC_TRIAGE }
|
|
26
26
|
BUILD_STATUS = { PASSING, FAILING, RUNNING, NONE }
|
|
@@ -30,12 +30,19 @@ MEETING_STATUS = { INVESTIGATING, DEBATING, CONCLUDING, COMPLETED, ARCHIVED }
|
|
|
30
30
|
PIPELINE_STATUS = { PENDING, RUNNING, COMPLETED, FAILED, PAUSED, WAITING_HUMAN, STOPPED }
|
|
31
31
|
STAGE_TYPE = { TASK, MEETING, PLAN, API, MERGE_PRS, SCHEDULE, WAIT, PARALLEL, CONDITION } // pipelines/*.json stage kinds
|
|
32
32
|
CHECKOUT_MODES = { WORKTREE, LIVE } // project.checkoutMode; see docs/live-checkout-mode.md
|
|
33
|
+
CONTROL_PLANE_ELIGIBLE_TYPES = Set([setup, ask, explore, meeting]) // meta.executionSurface: 'control-plane' allowlist — fail-closed
|
|
34
|
+
CLOUD_ELIGIBLE_TYPES = Set([implement, fix, test, docs]) // meta.executionSurface: 'cloud' allowlist — deliberately DISJOINT from the control-plane set
|
|
35
|
+
KB_CATEGORIES = [architecture, conventions, project-notes, build-reports, reviews] // knowledge/<category>/
|
|
36
|
+
WORKTREE_OWNER_MARKER = '.minions-worktree' // engine-created worktrees; unmarked out-of-root paths are foreign (GC fails open)
|
|
37
|
+
QUARANTINE_REF_RETENTION_MS = 30d // single source for BOTH quarantine dirs and refs/minions/quarantine-wip/*; resolve overrides via resolveQuarantineRetentionMs(config)
|
|
33
38
|
FAILURE_CLASS = { ... } // dispatch failure taxonomy — full enum in docs/completion-reports.md
|
|
34
39
|
```
|
|
35
40
|
|
|
41
|
+
`PROCESS_TERMINATION_SITES` (46 registered sites) is the one inventory that lives in [`engine/processes/process-utils.js`](../engine/processes/process-utils.js) rather than `shared.js`. Every kill must name a registered site via `shared.terminateProcess(<site>, target, ownership)` — see [docs/process-termination.md](process-termination.md).
|
|
42
|
+
|
|
36
43
|
## Engine defaults
|
|
37
44
|
|
|
38
|
-
Retry/timeout limits, sweep cadences, fleet ceilings, and managed-spawn caps live in `ENGINE_DEFAULTS` (also in `engine/shared.js`). Read defaults from there rather than re-declaring. Some keys resolve a `config.engine.*` override through a helper (`resolveAgentMaxBudget`, etc.); others are source-only, so verify the call site before documenting a Settings/config knob.
|
|
45
|
+
Retry/timeout limits, sweep cadences, fleet ceilings, and managed-spawn caps live in `ENGINE_DEFAULTS` (also in `engine/core/shared.js`). Read defaults from there rather than re-declaring. Some keys resolve a `config.engine.*` override through a helper (`resolveAgentMaxBudget`, etc.); others are source-only, so verify the call site before documenting a Settings/config knob.
|
|
39
46
|
|
|
40
47
|
## Invariants
|
|
41
48
|
|