@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
|
@@ -6,10 +6,10 @@ the **quarantine path** (dirty/divergent → quarantine dir + retry), and the
|
|
|
6
6
|
**Windows file-lock retry** (EPERM/EBUSY footgun). CLAUDE.md → Worktree
|
|
7
7
|
Lifecycle keeps the cross-cutting invariants; the detail lives here.
|
|
8
8
|
|
|
9
|
-
> Source of truth: `engine/
|
|
9
|
+
> Source of truth: `engine/worktrees/pool.js`, `engine/core/shared.js#removeWorktree`
|
|
10
10
|
> + `_retryFsOp`, `engine.js` (`_quarantineDirtyWorktree`,
|
|
11
11
|
> `_killGitDescendantsForWorktree`, `pruneOrphanWorktrees*`, `gcDispatchWorktreeIfOrphan`),
|
|
12
|
-
> `engine/cleanup.js`. Last verified: 2026-07-23.
|
|
12
|
+
> `engine/orchestration/cleanup.js`. Last verified: 2026-07-23.
|
|
13
13
|
|
|
14
14
|
## Operator-checkout boundary
|
|
15
15
|
|
|
@@ -76,14 +76,14 @@ dispatch was still active.
|
|
|
76
76
|
(`excludeDispatchId: id` so the dispatch can clean up its own worktree);
|
|
77
77
|
dispatch-end orphan GC (`gcDispatchWorktreeIfOrphan`); `_quarantineDirtyWorktree`
|
|
78
78
|
(returns `{ skipped: true, quarantinedPath: null }` on skip — callers
|
|
79
|
-
MUST honor `skipped` and not set `quarantined: true`); `engine/cleanup.js`
|
|
79
|
+
MUST honor `skipped` and not set `quarantined: true`); `engine/orchestration/cleanup.js`
|
|
80
80
|
orphan-dir sweep.
|
|
81
81
|
- **On skip:** drops a deduped operator note at
|
|
82
82
|
`notes/inbox/engine-worktree-skip-live-<basename>-<date>.md`.
|
|
83
83
|
|
|
84
84
|
## Worktree pool (opt-in)
|
|
85
85
|
|
|
86
|
-
`ENGINE_DEFAULTS.worktreePoolSize > 0` enables `engine/
|
|
86
|
+
`ENGINE_DEFAULTS.worktreePoolSize > 0` enables `engine/worktrees/pool.js` to
|
|
87
87
|
recycle worktree dirs across branches.
|
|
88
88
|
|
|
89
89
|
- **Borrow** in `spawnAgent` only when (a) the new branch doesn't exist on
|
|
@@ -107,6 +107,88 @@ that intentionally preserve an existing branch continue using that branch
|
|
|
107
107
|
unchanged. Warm-pool borrowing keeps its existing fetch-and-checkout of
|
|
108
108
|
`origin/<mainBranch>`.
|
|
109
109
|
|
|
110
|
+
### Shared remote-ref refresh serialization (W-ms5tb6ha016fda10)
|
|
111
|
+
|
|
112
|
+
`refs/remotes/origin/<mainBranch>` is shared by every dispatch in a clone. Two
|
|
113
|
+
fresh-worktree preparations in the same tick both run `git fetch origin
|
|
114
|
+
<mainBranch>`, git takes a `<ref>.lock` for the update, and the loser dies with:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
error: cannot lock ref 'refs/remotes/origin/main': is at <sha> but expected <sha>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
That is a **local serialization defect**, not connectivity. Two mitigations:
|
|
121
|
+
|
|
122
|
+
1. **`shared.withSerializedRemoteRefRefresh(repoPath, fn)`** — an in-process,
|
|
123
|
+
per-repository async mutex that every shared-ref fetch runs inside
|
|
124
|
+
(`_fetchWithTransientRetry`, reuse-sync, pool borrow, pool return, dependency
|
|
125
|
+
pre-merge). The engine daemon issues the concurrent fetches, so chaining them
|
|
126
|
+
per repo removes the race at its source. Deliberately **not** a file lock — a
|
|
127
|
+
file lock would be held across a network round-trip, which the concurrency
|
|
128
|
+
rules forbid. Keyed on the resolved repo path, so unrelated projects still
|
|
129
|
+
fetch in parallel, and it wraps **only** the fetch (never the follow-up merge,
|
|
130
|
+
checkout, or worktree add).
|
|
131
|
+
2. **`FAILURE_CLASS.GIT_REF_LOCK`** — classification for the residual
|
|
132
|
+
cross-process case (an operator's own `git fetch`, the dashboard, the CLI).
|
|
133
|
+
Previously these classified as `NETWORK_ERROR`, which hid the real cause
|
|
134
|
+
behind a "flaky network" story and burned dispatch retries. `GIT_REF_LOCK` is
|
|
135
|
+
**retryable** (the lock clears in seconds) and
|
|
136
|
+
`_fetchWithTransientRetry` now retries once on it before surfacing.
|
|
137
|
+
|
|
138
|
+
`shared.isGitRefLockError(err)` is the predicate; it inspects `message`,
|
|
139
|
+
`stderr`, and `stdout` (exec rejections put the actionable git text on stderr)
|
|
140
|
+
and deliberately does not match genuine network/auth failures.
|
|
141
|
+
|
|
142
|
+
## Control-plane dispatch: the cheapest worktree is the one never created
|
|
143
|
+
|
|
144
|
+
Some scheduled maintenance work is 100% Minions/REST + `gh`/`az` and never reads
|
|
145
|
+
or writes repository files — "merge approved PRs", "sweep the PR tracker", "CI
|
|
146
|
+
health check". Allocating a checkout for it is pure cost, and it was also
|
|
147
|
+
*harmful*: those dispatches raced the shared-ref fetch above and failed jobs that
|
|
148
|
+
needed no checkout at all.
|
|
149
|
+
|
|
150
|
+
A work item (or schedule) may declare `meta.executionSurface: "control-plane"`.
|
|
151
|
+
`shared.resolveSpawnPaths` then short-circuits **before** live-checkout mode and
|
|
152
|
+
before the read-only / mutating split, returning
|
|
153
|
+
`{ cwd: MINIONS_DIR, worktreeRootDir: null, controlPlane: true }`. No worktree is
|
|
154
|
+
created, no branch is derived, and the branch mutex, live-mode concurrency cap,
|
|
155
|
+
operator branch-hold probe, and worktree free-space preflight are all skipped.
|
|
156
|
+
|
|
157
|
+
The opt-in is **structured and fail-closed** — never inferred from prose:
|
|
158
|
+
|
|
159
|
+
| | |
|
|
160
|
+
|---|---|
|
|
161
|
+
| Field | `meta.executionSurface` (`"repo"` \| `"control-plane"`; unset ⇒ `"repo"`) |
|
|
162
|
+
| Eligible types | `shared.CONTROL_PLANE_ELIGIBLE_TYPES` — `setup`, `ask`, `explore`, `meeting` |
|
|
163
|
+
| Rejection class | `FAILURE_CLASS.INVALID_EXECUTION_SURFACE` (non-retryable) |
|
|
164
|
+
| Validated at | `POST /api/work-items` (create + update final-state guard), `validateScheduleDefinition`, and again in `spawnAgent` |
|
|
165
|
+
|
|
166
|
+
Eligibility is an **allowlist**, not a denylist: a repo-reading/mutating type
|
|
167
|
+
(`fix`, `implement`, `review`, `test`, `verify`, `decompose`, `docs`,
|
|
168
|
+
`build-fix-complex`) can never declare `control-plane`, an unknown surface string
|
|
169
|
+
is rejected rather than defaulted, and an invalid declaration degrades to a
|
|
170
|
+
normal repo dispatch rather than silently skipping the checkout.
|
|
171
|
+
|
|
172
|
+
Project context is preserved without a checkout: the rendered prompt still
|
|
173
|
+
carries `project_name` / `repo_name` / `repo_host` / `main_branch`, plus an
|
|
174
|
+
injected `## Execution surface: control-plane` section
|
|
175
|
+
(`engine/agents/playbook.js#buildControlPlaneHint`) telling the agent the repo is not on
|
|
176
|
+
disk, to work through the API and platform CLIs, not to clone, and to stop and
|
|
177
|
+
report if the task turns out to need repository files.
|
|
178
|
+
|
|
179
|
+
Schedules carry the declaration through `createScheduledWorkItem`:
|
|
180
|
+
|
|
181
|
+
```json
|
|
182
|
+
{
|
|
183
|
+
"id": "merge-approved-prs",
|
|
184
|
+
"cron": "0 */4 *",
|
|
185
|
+
"title": "Every 4h: merge approved PRs",
|
|
186
|
+
"type": "setup",
|
|
187
|
+
"project": "minions-opg",
|
|
188
|
+
"executionSurface": "control-plane"
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
110
192
|
## Creation integrity gate (#905)
|
|
111
193
|
|
|
112
194
|
`engine.js#runWorktreeAdd` treats every nonzero `git worktree add` result as a
|
|
@@ -154,6 +236,11 @@ post-reset re-verify) and the dirty-files prompt-injection site
|
|
|
154
236
|
A same-named file nested in a subdir is NOT the marker and stays dirty;
|
|
155
237
|
the marker filter never masks a genuine user/agent edit beside it.
|
|
156
238
|
|
|
239
|
+
Agent playbook health checks use the same boundary: an untracked root-level
|
|
240
|
+
marker is expected orchestration metadata and must be preserved, while every
|
|
241
|
+
staged, modified, deleted, or other untracked entry remains source dirt that
|
|
242
|
+
stops the agent before it edits the worktree.
|
|
243
|
+
|
|
157
244
|
When the dirt is real and the worktree can't be auto-healed, the engine
|
|
158
245
|
still quarantines and fails non-retryably, and the quarantine
|
|
159
246
|
auto-recovery loop re-queues the item once in a fresh worktree (see
|
|
@@ -171,11 +258,29 @@ quarantine fallback; transient probe, fetch, auth, and push failures leave clean
|
|
|
171
258
|
ahead commits in place for retry. Recoverable
|
|
172
259
|
`refs/minions/quarantine/*` and `refs/minions/quarantine-wip/*` refs are listed
|
|
173
260
|
on Dashboard → Engine under **Quarantined Work**.
|
|
174
|
-
Periodic cleanup deletes these recovery refs
|
|
261
|
+
Periodic cleanup (`engine/orchestration/cleanup.js`, every 60 ticks) deletes these recovery refs
|
|
262
|
+
after 30 days — no operator schedule or manual `minions cleanup` run is required.
|
|
263
|
+
The window is defined once as `QUARANTINE_REF_RETENTION_DAYS` in
|
|
264
|
+
`engine/core/shared.js` (re-exported by `engine/worktrees/quarantine-refs.js`), and
|
|
265
|
+
`ENGINE_DEFAULTS.worktreeProtectedQuarantineRetentionMs` is derived from it so
|
|
266
|
+
the directory and ref windows cannot drift apart. This is intentionally
|
|
175
267
|
longer than the 24-hour quarantine-directory retention window: the refs preserve
|
|
176
268
|
committed HEAD and WIP snapshots after the directory itself is removed, while
|
|
177
269
|
still preventing recovery refs from growing without bound.
|
|
178
270
|
|
|
271
|
+
The sweep fails safe. Age comes only from the canonical `/<epoch-ms>` segment
|
|
272
|
+
encoded in the ref name; a ref whose trailing segment is not a plain positive
|
|
273
|
+
integer is **preserved and logged**, never deleted speculatively. Only the
|
|
274
|
+
`refs/minions/quarantine/*` and `refs/minions/quarantine-wip/*` namespaces are
|
|
275
|
+
touched — branches, tags, and near-miss namespaces are ignored — and every delete
|
|
276
|
+
is SHA-guarded (`git update-ref -d <ref> <sha>`) so a concurrently rewritten ref
|
|
277
|
+
is refused rather than clobbered. Commit and WIP refs expire independently on
|
|
278
|
+
their own timestamps, so a pair created by the same quarantine event expires
|
|
279
|
+
together. Each configured project is swept independently: a missing or
|
|
280
|
+
unavailable repository is reported and skipped without aborting the sweep for the
|
|
281
|
+
remaining projects, and repeated sweeps are idempotent. Cleanup logs the removed,
|
|
282
|
+
retained, malformed, and failed counts per run.
|
|
283
|
+
|
|
179
284
|
At dispatch close, an error result is inspected before orphan GC. A clean branch
|
|
180
285
|
that is aligned with origin (or successfully pushed there) remains removable.
|
|
181
286
|
Dirty files, unpushed commits that cannot be pushed, and indeterminate branch
|
|
@@ -363,6 +468,36 @@ After `worktreeStuckThreshold` consecutive failures the path is
|
|
|
363
468
|
`removeWorktree`** — you'll bypass the retry, escalation, and metrics
|
|
364
469
|
layers.
|
|
365
470
|
|
|
471
|
+
### Removal is verified, never assumed (W-ms9q3dql00vxd4a8)
|
|
472
|
+
|
|
473
|
+
Every stage of `removeWorktree` clears the worktree's **contents first** —
|
|
474
|
+
including `.git` and the `.minions-worktree` ownership marker — and only then
|
|
475
|
+
unlinks the top directory. On Windows a handle on the directory itself can make
|
|
476
|
+
that final unlink fail while the stage still reports success (`rd /s /q` in
|
|
477
|
+
particular can exit 0 having deleted everything but the root). The husk left
|
|
478
|
+
behind carries no marker, no `gitdir:` pointer and no registration, so every
|
|
479
|
+
safe GC correctly classifies it as `foreignUnmanaged` and keeps it **forever**.
|
|
480
|
+
That is how an install accumulates a hundred empty `W-*` directories nothing is
|
|
481
|
+
ever allowed to reclaim.
|
|
482
|
+
|
|
483
|
+
So `removeWorktree` verifies the outcome instead of trusting the exit code.
|
|
484
|
+
Each success path funnels through `_verifyWorktreeRemoved`, which:
|
|
485
|
+
|
|
486
|
+
1. returns success when the path is genuinely gone;
|
|
487
|
+
2. **finishes the job** with a plain `rmdir` when the leftover is empty — an
|
|
488
|
+
empty directory has nothing left to lose;
|
|
489
|
+
3. otherwise **re-stamps the ownership marker** (a restore of prior ownership,
|
|
490
|
+
never a claim over a path that never had one) and reports the removal as the
|
|
491
|
+
failure it was, so the caller retries and the ordinary orphan sweep can still
|
|
492
|
+
classify and reclaim the residue at its retention boundary.
|
|
493
|
+
|
|
494
|
+
The corollary for the GC: the **marker is the proof of ownership, not `.git`**.
|
|
495
|
+
Owned residue whose `.git` pointer is missing entirely or dangles at pruned
|
|
496
|
+
admin metadata still expires normally. Residue with *no* marker stays foreign
|
|
497
|
+
regardless of age, emptiness, or how engine-shaped its name looks — an operator
|
|
498
|
+
may have created that directory a moment ago and be about to `git worktree add`
|
|
499
|
+
into it.
|
|
500
|
+
|
|
366
501
|
## Periodic prune (W-mq5o6bvy000x7191)
|
|
367
502
|
|
|
368
503
|
`pruneWorktreesPeriodic` runs `reconcileStaleWorktreeDirectories`
|
|
@@ -389,9 +524,49 @@ remain protected. Deletion requires either `.minions-worktree` or the narrow
|
|
|
389
524
|
legacy proof used for old Windows residue: a `W-*` basename plus a `.git` file
|
|
390
525
|
pointing inside one of the project's common `.git/worktrees` directories.
|
|
391
526
|
Ordinary unregistered residue is eligible after 2h; unprotected
|
|
392
|
-
`-quarantine-<epoch-ms>` residue after 24h. Protected quarantine directories
|
|
393
|
-
|
|
394
|
-
|
|
527
|
+
`-quarantine-<epoch-ms>` residue after 24h. Protected quarantine directories get
|
|
528
|
+
the longest — but no longer unbounded — window: they expire after
|
|
529
|
+
`engine.worktreeProtectedQuarantineRetentionMs` (default **30 days**), whose
|
|
530
|
+
default is *derived* from `engine/core/shared.js#QUARANTINE_REF_RETENTION_MS` — the
|
|
531
|
+
same constant `engine/worktrees/quarantine-refs.js` prunes recovery refs at — so a
|
|
532
|
+
quarantine DIRECTORY and its recovery REFS expire together instead of one
|
|
533
|
+
outliving the other. Setting the value to `0` restores the previous indefinite
|
|
534
|
+
retention; the operator range is 0–365 days.
|
|
535
|
+
|
|
536
|
+
> **Why the default is derived, not duplicated (W-ms87lj8j018j2572).** The two
|
|
537
|
+
> windows were originally independent literals that both happened to read 10 days.
|
|
538
|
+
> When the ref window moved to 30 days they silently drifted, main went red, and
|
|
539
|
+
> `GET /api/worktree-quarantine-refs` would have listed refs whose directories had
|
|
540
|
+
> already been swept. `ENGINE_DEFAULTS.worktreeProtectedQuarantineRetentionMs` now
|
|
541
|
+
> references the shared constant, and a regression test asserts the structural
|
|
542
|
+
> link rather than mere numeric equality.
|
|
543
|
+
|
|
544
|
+
> **Why the OVERRIDE is resolved through one seam too (W-ms87rbe601a66d13).**
|
|
545
|
+
> Deriving the two *defaults* from one constant closes the drift at build time
|
|
546
|
+
> but not at runtime. `worktreeProtectedQuarantineRetentionMs` is an operator
|
|
547
|
+
> knob, and the recovery-ref sweep used to ignore it entirely — `cleanup.js`
|
|
548
|
+
> called `pruneExpiredQuarantineRefs(projects)` with no retention argument, so it
|
|
549
|
+
> stayed pinned to the built-in 30 days. An operator who shortened retention to,
|
|
550
|
+
> say, 3 days therefore shortened only the DIRECTORY sweep and reintroduced the
|
|
551
|
+
> exact divergence the shared constant removed. Both sweeps now resolve the
|
|
552
|
+
> window through `shared.resolveQuarantineRetentionMs(config)`:
|
|
553
|
+
> `engine/worktrees/gc.js` for directories and
|
|
554
|
+
> `engine/orchestration/cleanup.js` → `pruneExpiredQuarantineRefs(projects,
|
|
555
|
+
> { retentionMs })` for refs. A non-positive value means **indefinite retention
|
|
556
|
+
> on both sides** — `pruneExpiredQuarantineRefs` treats it as "expire nothing",
|
|
557
|
+
> never as "everything is already expired". `GET /api/worktree-inventory`
|
|
558
|
+
> reports `recoverableRefs.retentionMs` at the resolved window, so the API never
|
|
559
|
+
> advertises a 30-day recovery story on an install that shortened it.
|
|
560
|
+
|
|
561
|
+
> **Why this changed (W-ms5tb6ha016fda10).** Protected quarantines — the ones
|
|
562
|
+
> whose dirty WIP could NOT be snapshotted to a durable
|
|
563
|
+
> `refs/minions/quarantine-wip/*` ref — were kept forever by *both*
|
|
564
|
+
> `reconcileStaleWorktreeDirectories` and `pruneOrphanWorktrees`. That is how a
|
|
565
|
+
> busy install accumulates quarantine residue with no expiry (20 such dirs
|
|
566
|
+
> observed on 2026-07-29). Every existing protection still applies first: a live
|
|
567
|
+
> SQL claim, a pool member, a keep_processes / managed_spawn cwd anchor, a
|
|
568
|
+
> detected CWD holder, or a missing ownership marker all keep the directory
|
|
569
|
+
> regardless of age, and `isWorktreePathLive` still fails OPEN.
|
|
395
570
|
|
|
396
571
|
`runCleanup` delegates its configured-root orphan pass to the same reconciler;
|
|
397
572
|
the former fail-open duplicate scan and generic age/count-cap deletion are
|
|
@@ -406,6 +581,110 @@ an expected `skipped` outcome, not a failed removal, and is retried on the next
|
|
|
406
581
|
sweep after the dispatch becomes terminal. Aggregate logs report mutually
|
|
407
582
|
meaningful `removed`, `skipped`, `preserved`, and `failed` counts.
|
|
408
583
|
|
|
584
|
+
### Inventory / diagnostics — `GET /api/worktree-inventory`
|
|
585
|
+
|
|
586
|
+
The sweeps above tell you what they *did*. `engine/worktrees/inventory.js`
|
|
587
|
+
answers the question an operator actually asks when a worktree root has 142
|
|
588
|
+
directories and 6 registrations: **what are all these, and which will the engine
|
|
589
|
+
ever reclaim?** It is strictly read-only — no git mutation, no filesystem writes,
|
|
590
|
+
no removal — and classifies every directory under each project's worktree root
|
|
591
|
+
plus every out-of-root git registration into disjoint buckets:
|
|
592
|
+
|
|
593
|
+
| Bucket | Meaning | Engine-removable? |
|
|
594
|
+
|---|---|---|
|
|
595
|
+
| `active` | a non-terminal dispatch claims the path (`isWorktreePathLive`) | never |
|
|
596
|
+
| `pooled` | warm worktree-pool entry | never (while pooled) |
|
|
597
|
+
| `anchored` | held by a live `keep_processes` / `managed_spawn` cwd | never |
|
|
598
|
+
| `registered` | known to `git worktree list`, not otherwise claimed | only when unlocked + unclaimed |
|
|
599
|
+
| `ownedOrphan` | carries `.minions-worktree`, unregistered, not live | **yes — this is the reclaimable backlog** |
|
|
600
|
+
| `quarantined` (+ `quarantinedProtected`) | `-quarantine-<ts>` recovery artifact | after its retention window |
|
|
601
|
+
| `foreignUnmanaged` | no ownership marker | **never** |
|
|
602
|
+
|
|
603
|
+
The response also carries `recoverableRefs` (`refs/minions/quarantine{,-wip}/*`
|
|
604
|
+
counts + their retention window, so operators can see what is still recoverable)
|
|
605
|
+
and `avoidedAllocations` — how many dispatches ran with
|
|
606
|
+
`executionSurface: "control-plane"` and therefore never allocated a checkout.
|
|
607
|
+
That count is **derived from the dispatch records themselves**, so there is no
|
|
608
|
+
counter to migrate, reset, or drift out of sync.
|
|
609
|
+
|
|
610
|
+
Every probe fails safe in the direction of *not* reporting reclaimable work: an
|
|
611
|
+
`isWorktreePathLive` throw classifies as `active`, an unreadable git registry
|
|
612
|
+
sets `registryReadable: false` and reports owned dirs as `registered` (never
|
|
613
|
+
`ownedOrphan`), and both surface an entry in `errors[]`.
|
|
614
|
+
|
|
615
|
+
Liveness is resolved **once per build** via `shared.listLiveWorktreePaths`
|
|
616
|
+
rather than one dispatch-table scan per directory. That helper is read-only and
|
|
617
|
+
deliberately separate from `isWorktreePathLive`: destructive wipe sites keep the
|
|
618
|
+
per-path guard with its `excludeDispatchId` / `excludeSpawnLeaseId` semantics.
|
|
619
|
+
Its failure contract is identical — `{ ok: false }` means "unknown", and the
|
|
620
|
+
inventory then treats every path as `active`.
|
|
621
|
+
|
|
622
|
+
#### The count contract — `engine.worktreeCount` (W-ms9q3dql00vxd4a8)
|
|
623
|
+
|
|
624
|
+
The buckets roll up into a `lifecycle` split that is the **only** supported
|
|
625
|
+
source for a headline "how many worktrees does Minions have?" number:
|
|
626
|
+
|
|
627
|
+
| Field | Meaning |
|
|
628
|
+
|---|---|
|
|
629
|
+
| `lifecycle.owned` | everything the engine's lifecycle created and is responsible for: `active` + `pooled` + `anchored` + `registered` + `ownedOrphan` + `quarantined` |
|
|
630
|
+
| `lifecycle.foreignUnmanaged` | operator scratch checkouts and unclassifiable residue — reported, never deleted, never folded into the headline |
|
|
631
|
+
| `lifecycle.total` | every directory scanned |
|
|
632
|
+
|
|
633
|
+
`GET /api/status` publishes `engine.worktreeCount` (`lifecycle.owned`) and
|
|
634
|
+
`engine.worktreeForeignCount` (`lifecycle.foreignUnmanaged`) from this same
|
|
635
|
+
classifier, so the dashboard chip and the inventory endpoint can never disagree.
|
|
636
|
+
The previous metric was a raw filesystem walk that counted any directory
|
|
637
|
+
containing a `.git` entry, which reported an operator's hand-made review scratch
|
|
638
|
+
checkout as an active Minions worktree (observed: 18 "worktrees", 3 engine-owned)
|
|
639
|
+
while missing engine residue whose `.git` pointer was already gone.
|
|
640
|
+
**Do not reintroduce a second classifier** — extend `inventory.js` instead.
|
|
641
|
+
|
|
642
|
+
Directories are also de-duplicated **across** projects, so two projects whose
|
|
643
|
+
`worktreeRoot` resolves to the same directory classify it once. The second
|
|
644
|
+
project reports `sharedRoot: true`.
|
|
645
|
+
|
|
646
|
+
#### The scan must not block the dashboard event loop
|
|
647
|
+
|
|
648
|
+
`_scanWorktreeCount` runs inside the dashboard process every
|
|
649
|
+
`worktreeCountCacheTtl` (30 s), and `/api/status` must not block the event loop
|
|
650
|
+
for more than ~100 ms or it starves Command Center's SSE stream. Classification
|
|
651
|
+
is a tight loop over N directories, so **one synchronous probe per directory is
|
|
652
|
+
enough to blow that budget** — and N is dominated by `foreignUnmanaged` residue
|
|
653
|
+
the engine deliberately never reclaims, so it grows without bound.
|
|
654
|
+
|
|
655
|
+
`buildWorktreeInventoryAsync` is therefore the only entry point a server may
|
|
656
|
+
call. It pays **every** cost up front and asynchronously — the
|
|
657
|
+
`git worktree list` shell-out per project, the worktree-root directory
|
|
658
|
+
listings, the per-directory ownership / protected-quarantine marker probes, and
|
|
659
|
+
the symlink resolution of both the candidate paths and the `keep_processes` /
|
|
660
|
+
`managed_spawn` anchor cwds — then hands the results to the same synchronous
|
|
661
|
+
classifier, which does pure in-memory lookups. `buildWorktreeInventory` stays
|
|
662
|
+
synchronous and directly unit-testable; when a pre-read is absent or a single
|
|
663
|
+
probe failed, it transparently falls back to the real synchronous helper, so a
|
|
664
|
+
partial pre-read can only make the scan cheaper, never change a classification.
|
|
665
|
+
|
|
666
|
+
Two footguns this encodes:
|
|
667
|
+
|
|
668
|
+
- **Never call `shared.isPathInsideOrEqual` inside a per-directory loop.** It
|
|
669
|
+
symlink-resolves *both* sides on every call, so anchor matching used to cost
|
|
670
|
+
O(directories × anchors) `realpath` syscalls. Measured on a 150-directory root
|
|
671
|
+
with three live process anchors: 105–120 ms of contiguous synchronous work and
|
|
672
|
+
130 ms of event-loop stall per refresh. Resolve once with
|
|
673
|
+
`shared.pathForComparisonAsync` and compare via
|
|
674
|
+
`shared.isResolvedPathInsideOrEqual`.
|
|
675
|
+
- **Resolve both sides of a comparison with the same twin.**
|
|
676
|
+
`fs.realpathSync.native` can canonicalize case on a case-insensitive volume
|
|
677
|
+
where the promises API does not, so mixing the sync and async resolvers could
|
|
678
|
+
make an already-resolved path miscompare on macOS.
|
|
679
|
+
|
|
680
|
+
`test/unit/worktree-lifecycle-bounded-gc.test.js` guards this behaviourally: it
|
|
681
|
+
counts real synchronous `fs` calls during a live
|
|
682
|
+
`buildWorktreeInventoryAsync` run and asserts zero, and asserts the async and
|
|
683
|
+
synchronous paths classify an identical tree identically. A source-string
|
|
684
|
+
assertion is not sufficient here — the original guard was silently neutralised
|
|
685
|
+
by re-anchoring it onto a thin delegating wrapper, where a "no sync filesystem
|
|
686
|
+
calls" assertion is vacuously true.
|
|
687
|
+
|
|
409
688
|
### Scratch-dir skip and bounded reaper (PR #388)
|
|
410
689
|
|
|
411
690
|
The worktree root (`<localPath>/../worktrees/`) is a **shared namespace**: it
|
|
@@ -423,7 +702,7 @@ infra / scratch, never a managed worktree. Engine-created worktree dir names
|
|
|
423
702
|
never start with `.`. The predicate `shared.isWorktreeRootInfraEntry(name)`
|
|
424
703
|
(returns `true` when `name` starts with `.`) is now applied at **both**
|
|
425
704
|
on-disk enumeration sites — `cleanup.js` worktree scan and
|
|
426
|
-
`
|
|
705
|
+
`engine/worktrees/gc.js#pruneOrphanWorktrees` — so the sweep is O(1) on scratch size
|
|
427
706
|
regardless of backlog.
|
|
428
707
|
|
|
429
708
|
**Bounded TTL reaper (`cleanup.js#reapAgentScratch`).** Skipping scratch
|
|
@@ -467,7 +746,7 @@ Previously unmarked paths are never marked by this recovery.
|
|
|
467
746
|
|
|
468
747
|
`engine.worktreeMinFreeSpaceBytes` defaults to 10 GiB (`0` disables) and is
|
|
469
748
|
editable in Dashboard -> Settings -> Worker Pool & Worktrees. Allocation and a
|
|
470
|
-
second spawn-time race check call `engine/
|
|
749
|
+
second spawn-time race check call `engine/worktrees/preflight.js` before any
|
|
471
750
|
`git worktree add`. The probe uses the nearest existing ancestor when the
|
|
472
751
|
configured worktree root has not been created yet.
|
|
473
752
|
|
|
@@ -481,7 +760,7 @@ on every allocation tick.
|
|
|
481
760
|
|
|
482
761
|
### Locked-`initializing` missing-dir reclaim (W-mqifblkf00149df5)
|
|
483
762
|
|
|
484
|
-
A third pruner, `
|
|
763
|
+
A third pruner, `engine/worktrees/gc.js#reclaimMissingDirWorktrees`, closes a
|
|
485
764
|
branch-bricking gap the two scanners above are blind to. An interrupted
|
|
486
765
|
`git worktree add` (engine crash / kill during git's `initializing` lock
|
|
487
766
|
phase) leaves a `.git/worktrees/<id>/` ADMIN entry behind — often carrying
|
|
@@ -552,8 +831,8 @@ Windows-only; three layers, each fail-open:
|
|
|
552
831
|
dispatch that owned this worktree path (discovered via
|
|
553
832
|
`_findTerminalWorktreeOwners`, which reads the dispatch store's non-live
|
|
554
833
|
sections), look up its recorded root PID from the PID file
|
|
555
|
-
(`shared.findDispatchPidFile`) and
|
|
556
|
-
tree
|
|
834
|
+
(`shared.findDispatchPidFile`) and terminate that PID plus its full descendant
|
|
835
|
+
tree through the registered ownership-aware gateway. Because it
|
|
557
836
|
only ever kills a tree the engine itself spawned for THIS path, it can never
|
|
558
837
|
touch a live sibling.
|
|
559
838
|
- **Layer 2 — CWD-scan reap (fallback for orphans the map / PID files no
|
|
@@ -585,7 +864,7 @@ Windows-only; three layers, each fail-open:
|
|
|
585
864
|
|
|
586
865
|
**Two call sites (W-mqinlicl §6).** `_reapWorktreeHolders` runs both BEFORE the
|
|
587
866
|
quarantine rename (`_quarantineDirtyWorktree`) AND at **dispatch-end GC**
|
|
588
|
-
(`
|
|
867
|
+
(`engine/worktrees/gc.js#gcDispatchWorktreeIfOrphan`, via the injected `reapHolders`
|
|
589
868
|
hook) right before `shared.removeWorktree`. The dispatch-end call passes the
|
|
590
869
|
ending dispatch's id as `ownerDispatchId` (terminal-owner discovery misses it —
|
|
591
870
|
the owner is still `active`) plus `excludeDispatchId`, so a dispatch always
|
package/engine/README.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Engine Source Layout
|
|
2
|
+
|
|
3
|
+
`engine/` contains the Minions orchestration implementation and the live
|
|
4
|
+
installation's generated runtime files. Tracked JavaScript belongs to a source
|
|
5
|
+
domain below; generated databases, logs, PID files, diagnostics, completions,
|
|
6
|
+
and QA artifacts are runtime state and must remain ignored.
|
|
7
|
+
|
|
8
|
+
## Source domains
|
|
9
|
+
|
|
10
|
+
| Directory | Responsibility |
|
|
11
|
+
|---|---|
|
|
12
|
+
| `agents/` | Agent spawning, worker pools, LLM/model integration, playbooks, steering, and harness context |
|
|
13
|
+
| `ado/` | Azure DevOps polling, auth, comments, status, and Git credentials |
|
|
14
|
+
| `api/` | API validation, settings validation, and Constellation bridge handling |
|
|
15
|
+
| `api-contracts/` | Dashboard route request/response contracts |
|
|
16
|
+
| `core/` | Shared constants/utilities, queries, feature resolution, safe expressions, and trust fences |
|
|
17
|
+
| `db/` | SQLite connection, schema, migrations, and DB utilities |
|
|
18
|
+
| `memory/` | Consolidation, KB sweeps, retrieval, review learning, and promotion |
|
|
19
|
+
| `observability/` | Memory diagnostics, engine logs, and metrics stores |
|
|
20
|
+
| `operations/` | CLI commands, status checks, and distribution metadata |
|
|
21
|
+
| `orchestration/` | Dispatch, lifecycle, scheduling, routing, pipelines, meetings, cleanup, and timeouts |
|
|
22
|
+
| `persistence/` | Runtime-state stores, DB events, small-state stores, and state operations |
|
|
23
|
+
| `planning/` | Projects, PRDs, work items, issues, validation, and area resolution |
|
|
24
|
+
| `processes/` | Managed/kept processes, launchers, sweeps, and cross-platform process utilities |
|
|
25
|
+
| `providers/` | GitHub and provider-neutral PR comments, reconciliation, cloning, patching, and fix targeting |
|
|
26
|
+
| `qa/` | QA sessions, runs, runbooks, PRD bridge, and process validation |
|
|
27
|
+
| `qa-runners/` | QA runner registry and built-in Playwright/Maestro adapters |
|
|
28
|
+
| `recovery/` | Restart health verification, in-process supervision, and OS-scheduled watchdog recovery |
|
|
29
|
+
| `runtimes/` | Versioned Claude, Copilot, and Codex runtime adapters |
|
|
30
|
+
| `watches/` | Watch registry, action handlers, and SQL store |
|
|
31
|
+
| `worktrees/` | Worktree creation, live checkout, pool, preflight, quarantine, and GC |
|
|
32
|
+
|
|
33
|
+
No tracked JavaScript files belong directly under `engine/`; source modules
|
|
34
|
+
must live in the domain directories above.
|
|
35
|
+
|
|
36
|
+
## Rules for physical moves
|
|
37
|
+
|
|
38
|
+
1. Do not add compatibility wrappers unless an external import contract is
|
|
39
|
+
known. Track any wrapper in `docs/deprecated.json` with a removal gate.
|
|
40
|
+
2. Update source comments, docs, package contents, direct unit imports, and
|
|
41
|
+
source-inspection assertions in the same change.
|
|
42
|
+
3. Audit every executable path used by spawn/task-scheduler code on Windows,
|
|
43
|
+
Linux, and macOS.
|
|
44
|
+
4. Never move generated runtime state as part of a source-layout refactor.
|
|
45
|
+
5. Validate domain tests, syntax/lint, package contents, and the broader unit
|
|
46
|
+
suite after any future physical move.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* engine/ado
|
|
2
|
+
* engine/ado/comment.js — Azure DevOps mirror of engine/providers/gh-comment.js.
|
|
3
3
|
*
|
|
4
4
|
* Posts a minions PR-comment thread to ADO with the same hidden marker, the
|
|
5
5
|
* same deterministic brand link as the GitHub path — because both are produced by the ONE neutral
|
|
6
|
-
* builder `engine/comment-format.js#buildMinionsCommentBody`. This module owns
|
|
6
|
+
* builder `engine/providers/comment-format.js#buildMinionsCommentBody`. This module owns
|
|
7
7
|
* only the ADO transport (REST `…/threads` POST) and ADO-specific input
|
|
8
8
|
* validation (orgBase / project / repositoryId / prNumber).
|
|
9
9
|
*
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* brand trailer hyperlinked.
|
|
16
16
|
*
|
|
17
17
|
* Auth: a bearer token from `az account get-access-token` (via
|
|
18
|
-
* engine/ado
|
|
18
|
+
* engine/ado/token.js#acquireAdoToken), threaded as `Authorization: Bearer`.
|
|
19
19
|
* Both the token acquisition and the HTTP transport are injectable so the unit
|
|
20
20
|
* tests run without network or `az`.
|
|
21
21
|
*
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
* changes) stay `active` so they block/notify the author.
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
|
-
const { buildMinionsCommentBody } = require('
|
|
37
|
-
const { acquireAdoToken } = require('./
|
|
36
|
+
const { buildMinionsCommentBody } = require('../providers/comment-format');
|
|
37
|
+
const { acquireAdoToken } = require('./token');
|
|
38
38
|
|
|
39
39
|
const ADO_API_VERSION = '7.1';
|
|
40
40
|
|
|
@@ -83,7 +83,7 @@ function buildThreadsUrl({ orgBase, project, repositoryId, prNumber }) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
|
-
* Default token acquisition — thin wrapper over engine/ado
|
|
86
|
+
* Default token acquisition — thin wrapper over engine/ado/token.js so callers
|
|
87
87
|
* can inject a stub in tests. Returns the bearer token string or throws.
|
|
88
88
|
*/
|
|
89
89
|
async function _defaultAcquireToken() {
|
|
@@ -94,7 +94,7 @@ async function _defaultAcquireToken() {
|
|
|
94
94
|
/**
|
|
95
95
|
* Post a minions PR comment to an Azure DevOps pull request.
|
|
96
96
|
*
|
|
97
|
-
* Mirrors engine/gh-comment.js#postPrComment: builds the marked +
|
|
97
|
+
* Mirrors engine/providers/gh-comment.js#postPrComment: builds the marked +
|
|
98
98
|
* brand-linked body via the shared builder, then POSTs a new active thread.
|
|
99
99
|
*
|
|
100
100
|
* @param {object} args
|
|
@@ -181,7 +181,7 @@ module.exports = {
|
|
|
181
181
|
ADO_THREAD_STATUS_ACTIVE,
|
|
182
182
|
ADO_THREAD_STATUS_CLOSED,
|
|
183
183
|
// Re-export the neutral builder so ADO callers have a single import surface,
|
|
184
|
-
// mirroring engine/gh-comment.js.
|
|
184
|
+
// mirroring engine/providers/gh-comment.js.
|
|
185
185
|
buildMinionsCommentBody,
|
|
186
186
|
// Internal validators exported for tests.
|
|
187
187
|
_validateOrgBase,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* engine/ado
|
|
2
|
+
* engine/ado/git-auth.js — W-mpcuc8i80003a7b3
|
|
3
3
|
*
|
|
4
4
|
* Inject a per-invocation Authorization: Bearer <token> http.extraHeader into
|
|
5
5
|
* git ops that touch an ADO origin. Solves the headless-dispatch failure mode
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* fatal: could not read Username for 'https://office.visualstudio.com'
|
|
10
10
|
*
|
|
11
|
-
* The token is sourced via
|
|
11
|
+
* The token is sourced via `engine/ado/token.js#acquireAdoTokenSync`
|
|
12
12
|
* helper (az CLI first, azureauth fallback). We cache it for 30 minutes and
|
|
13
13
|
* back off acquisition retries for 10 minutes on failure so we don't hammer
|
|
14
14
|
* az / azureauth on every git op during an outage.
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
* Migrate to env-form if argv exposure becomes a real concern.
|
|
40
40
|
*/
|
|
41
41
|
|
|
42
|
-
const shared = require('
|
|
43
|
-
const adoToken = require('./
|
|
42
|
+
const shared = require('../core/shared');
|
|
43
|
+
const adoToken = require('./token');
|
|
44
44
|
const { log } = shared;
|
|
45
45
|
|
|
46
46
|
const TOKEN_TTL_MS = 30 * 60 * 1000; // 30 min — matches gh-token cadence
|