@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
package/docs/plan-lifecycle.md
CHANGED
|
@@ -18,12 +18,53 @@ How plans go from idea to verified, running code.
|
|
|
18
18
|
→ human archives the PRD/plan from the dashboard when ready
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
## Plan-to-PRD result sidecar
|
|
22
|
+
|
|
23
|
+
The `plan-to-prd` agent does not write PRD state directly. It writes one
|
|
24
|
+
short-lived envelope to `agents/<agent-id>/prd-result.json`:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{ "filename": "<engine-pinned>.json", "work_item": "W-…", "prd": { … } }
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The whole contract is owned by `engine/planning/prd-result-sidecar.js`, shared by
|
|
31
|
+
both import sites so their validation can never drift.
|
|
32
|
+
|
|
33
|
+
**Result identity is retry-stable.** The expected filename is generated once and
|
|
34
|
+
pinned on the work item as `_prdFilename` (with `_planKey`); every later dispatch
|
|
35
|
+
of the same work item for the same plan reuses it. It is deliberately *not*
|
|
36
|
+
re-derived per dispatch — for a cross-repo plan the slug is `cross-<uid>-<date>`,
|
|
37
|
+
so re-deriving it on retry silently orphaned the previous attempt's valid result
|
|
38
|
+
and forced a full regeneration (W-ms9tcry701xz0389).
|
|
39
|
+
|
|
40
|
+
**Import is fail-closed.** A sidecar is imported only when it *declares* the
|
|
41
|
+
expected filename (there is no permissive "assume it's ours" fallback), its
|
|
42
|
+
`prd.source_plan` matches the work item's plan, and — when present — its
|
|
43
|
+
`work_item` matches the dispatched work item. A rejected sidecar is left on disk
|
|
44
|
+
untouched, because it may belong to a sibling work item that has not retried yet.
|
|
45
|
+
|
|
46
|
+
**Consume is atomic and exactly-once.** The sidecar is claimed with an
|
|
47
|
+
intra-directory rename before the PRD is written and removed after a successful
|
|
48
|
+
import, so one sidecar can never materialize two PRDs and a consumed result can
|
|
49
|
+
never be inherited by a later unrelated dispatch from the same agent.
|
|
50
|
+
|
|
51
|
+
**Recovery beats re-running.** Because the identity is stable, a valid sidecar
|
|
52
|
+
from an interrupted attempt still matches. `engine.js#discoverCentralWorkItems`
|
|
53
|
+
imports it *before* resolving an agent and completes the work item, instead of
|
|
54
|
+
spending another full conversion run recreating identical content. When no result
|
|
55
|
+
can be imported, the `#893` gate bounces the item back to `pending` with a reason
|
|
56
|
+
that names the plan but never the failed attempt's filename — the reason is
|
|
57
|
+
replayed verbatim into the retry prompt, where only `{{prd_filename}}` is
|
|
58
|
+
authoritative.
|
|
59
|
+
|
|
21
60
|
## Dependency Management
|
|
22
61
|
|
|
23
62
|
### Dispatch Gating
|
|
24
63
|
|
|
25
64
|
PRD items can declare `depends_on: ["P001", "P003"]`. The engine won't dispatch an item until all its dependencies have status `done`. This prevents agents from starting work before prerequisite code exists.
|
|
26
65
|
|
|
66
|
+
A dependency that ends `failed` does **not** cascade. The dependent stays `pending` with `_pendingReason: 'dependency_failed'` and is skipped for that tick — it is never marked `failed` and no retry is charged (W-mpbqhstz001lf518, closes #2627). Cascading used to burn the dependent's retry budget every time the dependency was auto-retried, and left the dependent stuck at `failed` even after the dependency eventually succeeded. Because the dependent stays pending, it dispatches on its own once the dependency reaches `done`.
|
|
67
|
+
|
|
27
68
|
### Branch Merging at Spawn
|
|
28
69
|
|
|
29
70
|
When an agent is spawned for a work item with dependencies, the engine merges same-project dependency PR branches into the worktree **before the agent starts**. Cross-repo dependencies are exposed as advisory refs instead of being merged. This lets later items inspect prerequisite code that has not reached the target repo's main branch.
|
|
@@ -130,12 +171,37 @@ Manual archive behavior:
|
|
|
130
171
|
|
|
131
172
|
Use unarchive only when the archived PRD/plan should reappear in active views. Reopening completed work should happen before archive, or after unarchive, so the materializer can see the active SQL PRD.
|
|
132
173
|
|
|
174
|
+
## Revision vs Staleness (W-msa0mrus00mh466f)
|
|
175
|
+
|
|
176
|
+
Two different things can invalidate a PRD, and they must never offer competing recovery paths at the same time:
|
|
177
|
+
|
|
178
|
+
| Signal | Meaning | Recovery |
|
|
179
|
+
|--------|---------|----------|
|
|
180
|
+
| `planStale: true` | The source markdown changed under an existing PRD (engine sweep, content-hash gated) | Generic stale banner → `Regenerate PRD` / `Resume as-is` |
|
|
181
|
+
| `status: "revision-requested"` | A human asked for a revision; a `plan-to-prd` agent is rewriting this PRD | `Revision in progress` banner → no regenerate/resume |
|
|
182
|
+
|
|
183
|
+
`POST /api/plans/revise` persists `revisionWorkItemId` on the PRD and stamps the reverse `_revisionForPrd` link on the work item. That two-way relation — never title text — is the single correlation. `shared.findPrdRevisionWorkItem()` resolves it (pointer first, reverse stamp as fallback) and `shared.derivePrdRevisionState()` reduces it to one of:
|
|
184
|
+
|
|
185
|
+
- `in-progress` — the revision work item is `pending` / `dispatched` / `queued` / `paused`. The dashboard suppresses `Regenerate PRD`, `Resume as-is`, and the group Pause/Approve/Archive actions so nothing can race the agent. A second `POST /api/plans/revise` is rejected `409 { code: 'revision-in-progress', workItemId }` and enqueues nothing.
|
|
186
|
+
- `failed` — the work item is terminal (`failed`/`cancelled`), finished `done` without updating the PRD, or is missing entirely. The dashboard renders an explicit `Revision failed` banner with `Retry revision` (requeues the same work item via `POST /api/work-items/retry`). Staleness is never silently cleared, and a fresh revise request is accepted so the PRD is not stranded in `revision-requested`.
|
|
187
|
+
- `none` — no revision operation applies; the ordinary stale banner and its actions behave exactly as before.
|
|
188
|
+
|
|
189
|
+
On a successful revision import, `engine/planning/prd-result-sidecar.js#consumePrdResultSidecar` normalizes the agent's PRD through `shared.applyPrdRevisionCompletion()` and persists it with a **single** `prdStore.writePrd()`: status returns to `awaiting-approval`, `planStale` and the pre-revision approval fields are cleared, `_sourcePlanContentHash` / `sourcePlanModifiedAt` / `lastSyncedFromPlan` are re-baselined against the live markdown, and the revision pointers are dropped. Readers therefore never observe a completed revision paired with stale recovery controls. The rule lives beside the only write — the module that owns the sidecar import — so both callers apply it identically: the post-completion import in `engine/orchestration/lifecycle.js` and the pre-spawn sidecar recovery in `engine.js#discoverCentralWorkItems` each pass the work item's `_revisionForPrd` stamp, and a rescued revision therefore lands in exactly the same state as a completed one.
|
|
190
|
+
|
|
191
|
+
Derivation happens once, server-side: `engine/core/queries.js#getPrdInfo` publishes it as `progress.planRevisions[<prdFile>]` and `dashboard/js/render-prd.js` only paints the result.
|
|
192
|
+
|
|
193
|
+
### The revision feedback reaches the agent explicitly
|
|
194
|
+
|
|
195
|
+
A revision run is an ordinary `plan-to-prd` dispatch whose only distinguishing input is the operator's feedback, so that text needs a real channel. It used to arrive by accident: the work item's `planFile` named the PRD `.json`, the engine's `PLANS_DIR/<planFile>` read failed, and the description (which embeds the feedback) leaked in through the `{{plan_content}}` fallback. Pointing `planFile` at the true source markdown closed that accident, and `playbooks/plan-to-prd.md` has no `{{task_description}}` token — so without a dedicated channel a revision silently degrades into a no-op regeneration that still reports success.
|
|
196
|
+
|
|
197
|
+
`engine.js` therefore resolves `shared.resolvePrdRevisionFeedback(item, resolvedPrd, prdFilename)` into `vars.revision_feedback`, and `plan-to-prd.md` renders it inside a `{{#revision_feedback}}…{{/revision_feedback}}` "Revision Requested" block. The feedback is read from the PRD (the single source of truth — `handlePlansRevise` writes it, `applyPrdRevisionCompletion` clears it), so a retry that reuses the same work item can never render superseded text. It is gated on the same structural `_revisionForPrd` ⇄ resolved-PRD-filename relation, so a plain regeneration cannot pick up feedback left behind by an earlier failed revision.
|
|
198
|
+
|
|
133
199
|
## Human Feedback on PRs
|
|
134
200
|
|
|
135
201
|
After PRs are created, any new actionable human comment can trigger a fix task;
|
|
136
202
|
an `@minions` mention is optional and is stripped before the feedback reaches
|
|
137
203
|
the agent. Pollers filter bot, CI, preview, and Minions-authored comments. See
|
|
138
|
-
`pollPrHumanComments()` in `engine/ado.js` and `engine/github.js`.
|
|
204
|
+
`pollPrHumanComments()` in `engine/ado/index.js` and `engine/providers/github.js`.
|
|
139
205
|
|
|
140
206
|
## Routing
|
|
141
207
|
|
|
@@ -161,5 +227,6 @@ the agent. Pollers filter bot, CI, preview, and Minions-authored comments. See
|
|
|
161
227
|
| `playbooks/verify.md` | Verification task playbook |
|
|
162
228
|
| `playbooks/implement.md` | Implementation playbook |
|
|
163
229
|
| `playbooks/plan-to-prd.md` | Plan → PRD conversion playbook |
|
|
164
|
-
| `engine/
|
|
230
|
+
| `engine/planning/prd-result-sidecar.js` | One-shot PRD result envelope: validation + atomic exactly-once consume |
|
|
231
|
+
| `engine/orchestration/lifecycle.js` | `checkPlanCompletion`, completion hooks, PRD sync |
|
|
165
232
|
| `engine.js` | `spawnAgent` (dependency merging), `resolveDependencyBranches` |
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# PR author identity vs. Minions agent attribution
|
|
2
|
+
|
|
3
|
+
W-mscibegy005e8b74. A tracked pull request now carries **two distinct
|
|
4
|
+
identities** that used to be conflated in the single overloaded `pr.agent`
|
|
5
|
+
field:
|
|
6
|
+
|
|
7
|
+
| Field | Meaning | Used for |
|
|
8
|
+
|-------------|---------|----------|
|
|
9
|
+
| `pr.agent` | The **Minions agent** that created/owns the automated work. | Routing, self-review prevention, fix ownership, metrics, diagnostics. |
|
|
10
|
+
| `pr.author` | The **real individual** on whose behalf the PR exists, linked to an authoritative provider identity. | The Pull Requests table's Author column, author filter/search, and the PR detail panel. |
|
|
11
|
+
|
|
12
|
+
## The bug this replaced
|
|
13
|
+
|
|
14
|
+
GitHub and ADO provider sync used to overwrite `pr.agent` with the platform
|
|
15
|
+
author login/display name whenever `agent === 'human'`
|
|
16
|
+
(`engine/providers/github.js`, `engine/ado/index.js`, and the dashboard link
|
|
17
|
+
enrichment). Meanwhile orchestration, routing, and metrics read `pr.agent` as
|
|
18
|
+
the **Minions** author-agent. A human-authored PR therefore encoded a human
|
|
19
|
+
login in a field that self-review prevention and routing treat as a Minions
|
|
20
|
+
agent id. `pr.agent` is now **never** a human login.
|
|
21
|
+
|
|
22
|
+
## Identity conventions
|
|
23
|
+
|
|
24
|
+
1. **`pr.agent`** stays the Minions agent id/name. The sentinel
|
|
25
|
+
`shared.PR_AGENT_HUMAN` (`'human'`) means "no Minions author-agent" (a human
|
|
26
|
+
or externally created PR). We keep the `'human'` sentinel rather than `null`
|
|
27
|
+
because `agent === 'human'` checks are embedded across routing and
|
|
28
|
+
lifecycle. Providers/dashboard never write a login here again.
|
|
29
|
+
2. **`pr.author`** is a structured, provider-linked shape:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
{ provider, id, nodeId?, descriptor?, login?, displayName?, url? }
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
- GitHub: built from the PR `user` object — `id`, `node_id`, `login`,
|
|
36
|
+
`name` (→ `displayName`), `html_url` (→ `url`).
|
|
37
|
+
- ADO: built from `createdBy` — `id` (GUID), `descriptor`, `uniqueName`
|
|
38
|
+
(→ `login`), `displayName`. ADO's `createdBy.url` is an API endpoint, not a
|
|
39
|
+
browsable profile, so no `url` is surfaced unless a real `http(s)` link is
|
|
40
|
+
present under `_links.web.href`.
|
|
41
|
+
3. **Shared automation credential.** When the platform credential is a shared
|
|
42
|
+
bot but Minions has explicit structured provenance for the real human, both
|
|
43
|
+
the platform creator and the effective author are preserved rather than
|
|
44
|
+
claiming one is the other. Identity is **never** inferred from title /
|
|
45
|
+
description / free text — only from structured provider metadata or an
|
|
46
|
+
existing configured/operator identity seam.
|
|
47
|
+
4. **Unknown / legacy records** render as **Unknown**, never silently borrowing
|
|
48
|
+
the agent name. Provider reconciliation enriches old rows naturally as they
|
|
49
|
+
are re-polled; migration 031 backfills what it safely can (below).
|
|
50
|
+
5. **Human-created PRs** no longer encode their login in `agent`; `agent` is the
|
|
51
|
+
`'human'` sentinel and `author` carries the human identity.
|
|
52
|
+
|
|
53
|
+
## Canonical helpers (`engine/core/shared.js`)
|
|
54
|
+
|
|
55
|
+
- `buildGithubAuthorIdentity(user)` / `buildAdoAuthorIdentity(createdBy)` —
|
|
56
|
+
build the structured shape from a provider response.
|
|
57
|
+
- `normalizePrAuthorIdentity(raw)` — coerce an arbitrary value (structured
|
|
58
|
+
object or bare login string) into the canonical shape or `null`. Every field
|
|
59
|
+
is length-capped; `url` accepts only `^https?://` values (dropped otherwise so
|
|
60
|
+
the dashboard never links an unvalidated href).
|
|
61
|
+
- `mergePrAuthorIdentity(existing, incoming)` — field-by-field merge; a
|
|
62
|
+
non-empty incoming value wins, otherwise the existing value is preserved, so a
|
|
63
|
+
**partial provider refresh never erases a richer author already stored**. A
|
|
64
|
+
provider change trusts the newer identity wholesale.
|
|
65
|
+
- `prAuthorLabel(author)` — human-readable label (displayName → login → id) for
|
|
66
|
+
the typed SQL column and filter/search text.
|
|
67
|
+
|
|
68
|
+
`upsertPullRequestRecord` normalizes `author` on the create path and merges it
|
|
69
|
+
via `mergePrAuthorIdentity` on the update path (and in `_mergeDuplicatePrInto`),
|
|
70
|
+
so all ingestion/reconciliation paths — GitHub, ADO, output-based discovery,
|
|
71
|
+
manual link, dashboard enrich, dedup — converge on one merge behavior.
|
|
72
|
+
|
|
73
|
+
## Persistence — migration 031
|
|
74
|
+
|
|
75
|
+
`engine/db/migrations/031-pr-author-identity.js` adds a typed `author`
|
|
76
|
+
projection column (plus `idx_pr_author`). The canonical structured identity
|
|
77
|
+
always lives in the row's `data` JSON; the column is a label projection for
|
|
78
|
+
filters/sort. `engine/persistence/pull-requests-store.js` writes it via a local
|
|
79
|
+
`_authorLabel(pr.author)` (displayName → login → id).
|
|
80
|
+
|
|
81
|
+
Backfill is **fail-closed**. A legacy row whose `agent` is a human login (i.e.
|
|
82
|
+
not `'human'`, not a configured Minions agent id from `config.json`, not
|
|
83
|
+
`temp-*`, not `unknown`/`unassigned`) has that login moved into
|
|
84
|
+
`author = { login }` and `agent` reset to `'human'`. **If the configured
|
|
85
|
+
agent-id set cannot be resolved, the destructive agent→author move is skipped
|
|
86
|
+
entirely** so a real agent row (e.g. `dallas`) is never mistaken for a human
|
|
87
|
+
login. Rows that already carry a structured `data.author` only get the label
|
|
88
|
+
projected into the new column. Rows whose identity cannot be established are
|
|
89
|
+
left untouched (`author` NULL → renders Unknown).
|
|
90
|
+
|
|
91
|
+
## Snapshot / bridge projection
|
|
92
|
+
|
|
93
|
+
`engine/api/bridge.js#_projectPullRequest` projects an allowlisted, length-capped
|
|
94
|
+
`author` object (`provider`, `id`, `descriptor`, `login`, `displayName`, `url`)
|
|
95
|
+
alongside the existing `agent` field.
|
|
96
|
+
|
|
97
|
+
## Dashboard
|
|
98
|
+
|
|
99
|
+
`dashboard/js/render-prs.js` replaces the Pull Requests table's **Agent** column
|
|
100
|
+
with **Author** (display name + `@login`, safely escaped, linked to the provider
|
|
101
|
+
profile only when a validated `http(s)` URL is present). The Minions agent stays
|
|
102
|
+
visible in the PR detail panel under **Minions agent**, next to a new **Author**
|
|
103
|
+
line, so operators can still answer both "who is this for?" and "which agent
|
|
104
|
+
handled it?". The shared author filter (`dashboard/shared/pr-filters.js`) now
|
|
105
|
+
reads structured `pr.author` only and no longer falls back to `pr.agent`, so the
|
|
106
|
+
filter and the displayed column stay aligned (legacy rows group as Unknown).
|
|
107
|
+
|
|
108
|
+
## Backward-compatible reads
|
|
109
|
+
|
|
110
|
+
`normalizePrAuthorIdentity` still accepts a bare author string and coerces it to
|
|
111
|
+
`{ login }`, so any legacy record or external caller that supplied a plain
|
|
112
|
+
author string keeps rendering. This is a tolerant read, not a retained
|
|
113
|
+
compatibility shim — the overloaded `agent = login` write behavior was removed
|
|
114
|
+
outright (no `docs/deprecated.json` entry required).
|
|
@@ -17,6 +17,15 @@ All dispatch decisions happen inside `engine.js#discoverFromPrs` once per discov
|
|
|
17
17
|
|
|
18
18
|
\* `autoFixPaused` is the auto-**fix** kill-switch. It forces sites 2, 4, 5, and 6 off, but is **not** wired to suppress review dispatch (sites 1 and 3) — review verdicts stay fresh during a fix-storm incident. To halt review dispatch, clear `autoReviewPrs` / `autoReReviewPrs` directly, or set `pollingPaused: true` (which forces every per-PR auto-dispatch gate inert by zeroing `pollEnabled`).
|
|
19
19
|
|
|
20
|
+
### Per-PR row controls
|
|
21
|
+
|
|
22
|
+
The Pull Requests table exposes adjacent **Auto Fix** and **Auto Review**
|
|
23
|
+
switches for each tracked PR. Auto Fix gates sites 2, 4, 5, and 6 in the table
|
|
24
|
+
above; Auto Review gates sites 1 and 3. Existing records inherit both switch
|
|
25
|
+
values from `contextOnly` until either switch is changed. The dashboard persists
|
|
26
|
+
the independent booleans through `POST /api/pull-requests/automation`; when both
|
|
27
|
+
are off, it also sets `contextOnly:true`.
|
|
28
|
+
|
|
20
29
|
### Provider polling gate
|
|
21
30
|
|
|
22
31
|
Each PR carries a `repoHost` (`github` / `ado`). Before evaluating any of the six sites, `discoverFromPrs` resolves a `pollEnabled` boolean from that provider's poll gate:
|
|
@@ -41,7 +50,13 @@ There are two independent operator-facing master flags:
|
|
|
41
50
|
|
|
42
51
|
These compose: setting `pollingPaused: true` plus `autoFixPaused: true` halts *all* automated PR work (review + fix + polling). Setting only `autoFixPaused: true` halts only auto-fix dispatch. Setting only `pollingPaused: true` halts polling *and reconciliation* (all provider network traffic) and effectively halts dispatch (because the per-PR gates rely on cached poll state), while still letting manual `/api/pull-requests/observe` or operator-driven dispatch proceed.
|
|
43
52
|
|
|
44
|
-
Flip via Dashboard → Settings → Polling (`pollingPaused`) /
|
|
53
|
+
Flip via Dashboard → Settings → Polling (`pollingPaused`) / Plan & PR Workflow → Monitor (`autoFixPaused`), or set `engine.<flag>: true` in `config.json`.
|
|
54
|
+
|
|
55
|
+
### What `autoFixPaused` deliberately does NOT cover
|
|
56
|
+
|
|
57
|
+
`autoFixPaused` gates exactly the four auto-**fix dispatch** decisions above. It is not a general "stop writing to the platform" switch, because the table row's promise — operators keep seeing fresh verdicts *and build status* during a fix-storm — depends on polling-side work continuing.
|
|
58
|
+
|
|
59
|
+
The clearest example is `adoQueueDraftPrBuilds` (ADO draft-PR branch-policy build queueing, W-ms7kklp8002zad24). It runs inside `pollPrStatus`, POSTs a build queue request, and dispatches **no agent**, so it is gated by `pollingPaused` / `adoPollEnabled` / `adoPrStatusPollEnabled` and not by `autoFixPaused`. Its Settings control therefore lives in the **Polling** pane under "Poll-side actions", *not* in the Monitor per-cause tile grid whose copy promises agent dispatch and Pause-ALL inertness. Any future poll-side action that writes to a provider without dispatching an agent belongs in the same place — putting it in the auto-fix grid advertises a kill-switch that will not fire.
|
|
45
60
|
|
|
46
61
|
## Per-PR per-cause pause (different mechanism)
|
|
47
62
|
|
|
@@ -58,9 +73,9 @@ This per-PR per-cause pause is unrelated to `autoFixPaused`. Cause values are va
|
|
|
58
73
|
| File | Role |
|
|
59
74
|
|---------------------------|---------------------------------------------------------------------------------------------------|
|
|
60
75
|
| `engine.js#discoverFromPrs` | Single dispatch site walker — evaluates the six causes per tick in fixed order |
|
|
61
|
-
| `engine/shared.js` (`ENGINE_DEFAULTS`) | Default values for every per-cause gate + both kill-switches |
|
|
62
|
-
| `engine/lifecycle.js#runPostCompletionHooks` | Wires `autoReReviewPrs` into the closure-loop re-review path (P-e8b1c4d2) |
|
|
63
|
-
| `dashboard/js/settings.js` |
|
|
76
|
+
| `engine/core/shared.js` (`ENGINE_DEFAULTS`) | Default values for every per-cause gate + both kill-switches |
|
|
77
|
+
| `engine/orchestration/lifecycle.js#runPostCompletionHooks` | Wires `autoReReviewPrs` into the closure-loop re-review path (P-e8b1c4d2) |
|
|
78
|
+
| `dashboard/js/settings.js` | Plan & PR Workflow → Monitor subsection (gates + `autoFixPaused`); Polling pane (`pollingPaused` + granular polls + poll-side actions like `adoQueueDraftPrBuilds`) |
|
|
64
79
|
| `dashboard/js/render-dispatch.js` | `renderPausedBanner(engine)` — sticky cross-page banner showing both pause states |
|
|
65
80
|
| `POST /api/pull-requests/clear-paused-cause` | Clears a per-PR per-cause pause record |
|
|
66
81
|
| `POST /api/engine/polling/{pause,resume}`, `POST /api/engine/auto-fix/{pause,resume}` | Convenience endpoints for the two master flags (P-f3c9d0e7) |
|
|
@@ -5,7 +5,7 @@ proposal investigated in CC turn `cct-mpeira1y0001ddff`).
|
|
|
5
5
|
|
|
6
6
|
## Why
|
|
7
7
|
|
|
8
|
-
`engine/
|
|
8
|
+
`engine/providers/github.js` and `engine/ado/index.js` `pollPrHumanComments` route every actionable human PR
|
|
9
9
|
comment into `pr.humanFeedback.pendingFix:true`, which `discover-from-prs`
|
|
10
10
|
turns into a `fix` dispatch on the **same** branch. The `fix.md` playbook then
|
|
11
11
|
forbids broadening the PR or creating a replacement branch. The net result was
|
|
@@ -28,7 +28,7 @@ playbook contract. The poller is unchanged; the carve-out is purely additive.
|
|
|
28
28
|
1. **Human leaves a comment on PR #2400** asking for a related-but-distinct
|
|
29
29
|
refactor: *"Can you also extract the markdown sanitizer into a shared module?
|
|
30
30
|
Probably worth a separate PR though."*
|
|
31
|
-
2. **Engine pollers** (`engine/github.js` `pollPrHumanComments`) flag the
|
|
31
|
+
2. **Engine pollers** (`engine/providers/github.js` `pollPrHumanComments`) flag the
|
|
32
32
|
comment, set `pr.humanFeedback.pendingFix:true`, and `discover-from-prs`
|
|
33
33
|
queues a `fix` dispatch against PR #2400's branch.
|
|
34
34
|
3. **Fix agent (e.g. lambert)** starts working. After fetching the diff and
|
|
@@ -102,7 +102,7 @@ playbook contract. The poller is unchanged; the carve-out is purely additive.
|
|
|
102
102
|
]
|
|
103
103
|
}
|
|
104
104
|
```
|
|
105
|
-
`engine/lifecycle.js processCompletionFollowups` logs each entry at `info`
|
|
105
|
+
`engine/orchestration/lifecycle.js processCompletionFollowups` logs each entry at `info`
|
|
106
106
|
and warns if the claimed `wi_id` is missing from any project's
|
|
107
107
|
SQL work-item store (catches reverted or non-landing dispatches).
|
|
108
108
|
|
|
@@ -141,7 +141,7 @@ exists; you still reply on the comment thread with the returned id.
|
|
|
141
141
|
|
|
142
142
|
## What does NOT change
|
|
143
143
|
|
|
144
|
-
- The pollers (`engine/github.js`, `engine/ado.js`) still set
|
|
144
|
+
- The pollers (`engine/providers/github.js`, `engine/ado/index.js`) still set
|
|
145
145
|
`pr.humanFeedback.pendingFix:true` for every actionable human comment.
|
|
146
146
|
- `discover-from-prs` still queues `fix` dispatches against the original
|
|
147
147
|
branch; nothing prevents the in-place fix from proceeding.
|
|
@@ -179,7 +179,7 @@ exists; you still reply on the comment thread with the returned id.
|
|
|
179
179
|
| `dashboard.js` (`validatePrFollowupShape`, `findExistingFollowupForComment`, `extractMinionsAgentHeader`, `extractMinionsOriginWiHeader`, `handleWorkItemsCreate`) | Shape validation, parent_comment_id dedup, header persistence, 200/400/409 responses. |
|
|
180
180
|
| `dashboard/js/render-work-items.js` (`prFollowup` block in `wiRow`) | `↪ from PR #N` chip on follow-up WIs. |
|
|
181
181
|
| `dashboard/js/render-prs.js` (`_countPrFollowups`) | `+N follow-ups` chip on PR rows. |
|
|
182
|
-
| `engine/lifecycle.js` (`processCompletionFollowups`) | Parses the optional `followups` array on completion reports; warns on mismatch. |
|
|
182
|
+
| `engine/orchestration/lifecycle.js` (`processCompletionFollowups`) | Parses the optional `followups` array on completion reports; warns on mismatch. |
|
|
183
183
|
| `docs/completion-reports.md` (`PR-comment follow-ups`) | Schema for the optional `followups` array. |
|
|
184
184
|
|
|
185
185
|
## Migration / rollout
|
|
@@ -200,7 +200,7 @@ To adopt:
|
|
|
200
200
|
|
|
201
201
|
## Related
|
|
202
202
|
|
|
203
|
-
- `engine/github.js pollPrHumanComments` (human-comment routing — unchanged)
|
|
203
|
+
- `engine/providers/github.js pollPrHumanComments` (human-comment routing — unchanged)
|
|
204
204
|
- `engine.js HUMAN_FEEDBACK fix dispatch` (in-place fix flow — unchanged)
|
|
205
205
|
- `playbooks/templates/followup-dispatch.md` (the contract agents read)
|
|
206
206
|
- `docs/completion-reports.md` `PR-comment follow-ups` section
|
|
@@ -37,12 +37,12 @@ A successful review dispatch that posts no `VERDICT:` comment and is not a recog
|
|
|
37
37
|
|
|
38
38
|
When multiple problems coexist, earlier triggers get the first chance to enqueue work. The local `fixDispatched` flag is declared before the initial review trigger and set after first-review, human-feedback, review-feedback, and build-failure dispatches. Each later trigger checks `!fixDispatched`, so those sites suppress all subsequent review/fix work for that PR in the same discovery pass. Re-review is the exception: it checks `!fixDispatched` before enqueueing but does not set the flag afterward. Consequently, a re-review may enqueue alongside a later build-failure fix or, when no build fix is queued, a merge-conflict fix in the same pass.
|
|
39
39
|
|
|
40
|
-
The engine does not cap review→fix cycles or build-fix attempts. Each trigger evaluates its own gates on every discovery pass; loops stop only when the underlying condition clears (reviewer approves, build passes, conflict resolves, human feedback handled). Operators who need to halt automation on a runaway PR have three escalating tools: (1) clear the relevant per-cause flag (`evalLoop`, `autoFixBuilds`, `autoFixConflicts`, `autoFixHumanComments`, `autoFixReviewFeedback`, `autoReviewPrs`, `autoReReviewPrs`); (2) flip the `autoFixPaused` master kill-switch to halt every auto-**fix** dispatch (sites B, C, D, and human-comment fix) at once while leaving review verdicts and polling fresh — Dashboard → Settings →
|
|
40
|
+
The engine does not cap review→fix cycles or build-fix attempts. Each trigger evaluates its own gates on every discovery pass; loops stop only when the underlying condition clears (reviewer approves, build passes, conflict resolves, human feedback handled). Operators who need to halt automation on a runaway PR have three escalating tools: (1) clear the relevant per-cause flag (`evalLoop`, `autoFixBuilds`, `autoFixConflicts`, `autoFixHumanComments`, `autoFixReviewFeedback`, `autoReviewPrs`, `autoReReviewPrs`); (2) flip the `autoFixPaused` master kill-switch to halt every auto-**fix** dispatch (sites B, C, D, and human-comment fix) at once while leaving review verdicts and polling fresh — Dashboard → Settings → Plan & PR Workflow → Monitor or `POST /api/engine/auto-fix/pause`; (3) flip `pollingPaused` to halt PR polling entirely, which also forces every per-PR auto-dispatch gate inert. Per-PR per-cause noop pauses (`_noOpFixes[cause].paused`) are an orthogonal recovery surface — see §4E and `POST /api/pull-requests/clear-paused-cause`. See [docs/pr-auto-fix-dispatch.md](pr-auto-fix-dispatch.md) for the full site × gate × kill-switch reference table.
|
|
41
41
|
|
|
42
42
|
### A. Human comments (`humanFeedback.pendingFix`)
|
|
43
43
|
|
|
44
44
|
- Gate: `pendingFix || hasCoalescedFeedback` + not already dispatched/on cooldown
|
|
45
|
-
- Agent comments filtered out via `_isNonActionableComment()` (`engine/github.js`, `engine/ado.js`) — composes preview/bot body checks and the structural Minions-author check from `engine/comment-classifier.js`.
|
|
45
|
+
- Agent comments filtered out via `_isNonActionableComment()` (`engine/providers/github.js`, `engine/ado/index.js`) — composes preview/bot body checks and the structural Minions-author check from `engine/providers/comment-classifier.js`. Both pollers resolve marker authorship through the shared `classifyMarkerAuthorship()` seam, which combines the `<!-- minions:agent=<id> kind=<kind> -->` HTML marker on the body's first line (`hasMinionsMarker`) with a host identity signal: `viewerDidAuthor` on GitHub, a `config.engine.minionsAdoIdentities` author match on ADO. A marker plus an identity **mismatch** is never agent-authored (spoof guard). A marker with **no usable identity signal** — an unconfigured `minionsAdoIdentities`, the default — fails *safe* and is treated as agent-authored, and ADO logs a one-time warning that the spoof guard is inactive (W-msbf1kf6033f46cb). The previous fail-open reading gave the marker zero filtering power on ADO, so every agent fix-summary re-triggered a fix dispatch on the same PR.
|
|
46
46
|
- Coalesces multiple comments arriving during cooldown into single fix
|
|
47
47
|
- Routes to author
|
|
48
48
|
- Fix agents must treat human comments as claims to verify, not commands. They inspect or reproduce each claimed issue, make the smallest correct fix only when the claim is valid, and otherwise reply with evidence-backed rationale.
|
|
@@ -82,11 +82,11 @@ A paused cause **suppresses further auto-dispatch for that cause** until cleared
|
|
|
82
82
|
2. **Click the red chip on the dashboard** — confirms, then `POST /api/pull-requests/clear-paused-cause` (`dashboard.js`, issue #2969) validates the cause against `shared.PR_FIX_CAUSE`, locates the PR via `mutatePullRequests` across project/central files, and calls `clearPrNoOpFixAttempt` to wipe the cause record.
|
|
83
83
|
3. **Direct API call** — `POST /api/pull-requests/clear-paused-cause` with `{ prId, cause }`.
|
|
84
84
|
|
|
85
|
-
The exported `recordPrNoOpFixAttempt` / `clearPrNoOpFixAttempt` / `sweepStalePrNoOpFixes` helpers in `engine/lifecycle.js` are the only sanctioned entry points. The dispatch evaluator gates re-dispatch on `shared.isPrNoOpFixCausePaused(pr, cause)` (which re-validates the fingerprint per call), the dashboard chip surface uses `shared.getPrPausedCauses(pr)` (same gate), and the `pollPrStatus` callback in `engine/ado.js` + `engine/github.js` calls `sweepStalePrNoOpFixes(pr)` each tick to GC records whose fingerprint no longer matches the live PR.
|
|
85
|
+
The exported `recordPrNoOpFixAttempt` / `clearPrNoOpFixAttempt` / `sweepStalePrNoOpFixes` helpers in `engine/orchestration/lifecycle.js` are the only sanctioned entry points. The dispatch evaluator gates re-dispatch on `shared.isPrNoOpFixCausePaused(pr, cause)` (which re-validates the fingerprint per call), the dashboard chip surface uses `shared.getPrPausedCauses(pr)` (same gate), and the `pollPrStatus` callback in `engine/ado/index.js` + `engine/providers/github.js` calls `sweepStalePrNoOpFixes(pr)` each tick to GC records whose fingerprint no longer matches the live PR.
|
|
86
86
|
|
|
87
87
|
#### E2. Build-fix-ineffective tracker (`pr._buildFixIneffective`, issue #639)
|
|
88
88
|
|
|
89
|
-
A "branch unchanged" no-op report for a `BUILD_FAILURE`-cause fix used to be conflated with the generic `_noOpFixes[BUILD_FAILURE]` counter even when the build was still genuinely red on the unchanged head — silently masking a stuck failure as "handled". `checkBuildStillFailingLive()` re-polls host-specific live CI (`checkLiveBuildAndConflict` in `engine/github.js` / `engine/ado.js`) right after `detectPrFixBranchChange()` reports no branch change for a build-failure fix. If the build is still failing, `recordBuildFixIneffective()` tracks a **separate**, `headSha`-keyed counter (`pr._buildFixIneffective`) instead of bumping `_noOpFixes[BUILD_FAILURE]`, pausing after `prNoOpFixPauseAttempts` on the same head and writing a distinct `build-fix-ineffective-<pr>` inbox alert; `shared.isBuildFixIneffectivePaused()` re-validates the stored `headSha` against the live PR before honoring the pause (same fingerprint-revalidation pattern as `isPrNoOpFixCausePaused`), and `engine.js` wires `isBuildFixIneffectiveSuppressed()` into the `BUILD_FAILURE` dispatch gate alongside the existing no-op check. A real branch-changing fix clears the stale record. The dashboard PR list (`dashboard/js/render-prs.js`) renders a separate, non-interactive "infra issue suspected" chip for a paused `_buildFixIneffective` record (tooltip shows `reason`/`count`/`headSha`/`liveBuildStatus`) alongside the existing `_pausedCauses` chips.
|
|
89
|
+
A "branch unchanged" no-op report for a `BUILD_FAILURE`-cause fix used to be conflated with the generic `_noOpFixes[BUILD_FAILURE]` counter even when the build was still genuinely red on the unchanged head — silently masking a stuck failure as "handled". `checkBuildStillFailingLive()` re-polls host-specific live CI (`checkLiveBuildAndConflict` in `engine/providers/github.js` / `engine/ado/index.js`) right after `detectPrFixBranchChange()` reports no branch change for a build-failure fix. If the build is still failing, `recordBuildFixIneffective()` tracks a **separate**, `headSha`-keyed counter (`pr._buildFixIneffective`) instead of bumping `_noOpFixes[BUILD_FAILURE]`, pausing after `prNoOpFixPauseAttempts` on the same head and writing a distinct `build-fix-ineffective-<pr>` inbox alert; `shared.isBuildFixIneffectivePaused()` re-validates the stored `headSha` against the live PR before honoring the pause (same fingerprint-revalidation pattern as `isPrNoOpFixCausePaused`), and `engine.js` wires `isBuildFixIneffectiveSuppressed()` into the `BUILD_FAILURE` dispatch gate alongside the existing no-op check. A real branch-changing fix clears the stale record. The dashboard PR list (`dashboard/js/render-prs.js`) renders a separate, non-interactive "infra issue suspected" chip for a paused `_buildFixIneffective` record (tooltip shows `reason`/`count`/`headSha`/`liveBuildStatus`) alongside the existing `_pausedCauses` chips.
|
|
90
90
|
|
|
91
91
|
## 5. Fix completes
|
|
92
92
|
|
|
@@ -128,12 +128,12 @@ A "branch unchanged" no-op report for a `BUILD_FAILURE`-cause fix used to be con
|
|
|
128
128
|
| File | Functions |
|
|
129
129
|
|---|---|
|
|
130
130
|
| `engine.js` | `discoverFromPrs()` — discovery + dispatch logic |
|
|
131
|
-
| `engine/lifecycle.js` | `syncPrsFromOutput()`, `updatePrAfterReview()`, `updatePrAfterFix()` |
|
|
132
|
-
| `engine/github.js` | `pollPrStatus()`, `pollPrHumanComments()`, `fetchGhBuildErrorLog()` |
|
|
133
|
-
| `engine/ado.js` | `pollPrStatus()`, `pollPrHumanComments()`, `fetchAdoBuildErrorLog()` |
|
|
134
|
-
| `engine/comment-classifier.js` | Host-agnostic body-only predicates (`isPreviewStatusBody`, `hasMinionsMarker`, `hasVerdictPrefix`) shared by GH + ADO pollers (extracted in F2+F3+F4) |
|
|
135
|
-
| `engine/dispatch.js` | `addToDispatch()` — dedup by work item ID and dispatchKey |
|
|
136
|
-
| `engine/cooldown.js` | `isBranchActive()`, cooldown management |
|
|
131
|
+
| `engine/orchestration/lifecycle.js` | `syncPrsFromOutput()`, `updatePrAfterReview()`, `updatePrAfterFix()` |
|
|
132
|
+
| `engine/providers/github.js` | `pollPrStatus()`, `pollPrHumanComments()`, `fetchGhBuildErrorLog()` |
|
|
133
|
+
| `engine/ado/index.js` | `pollPrStatus()`, `pollPrHumanComments()`, `fetchAdoBuildErrorLog()` |
|
|
134
|
+
| `engine/providers/comment-classifier.js` | Host-agnostic body-only predicates (`isPreviewStatusBody`, `hasMinionsMarker`, `hasVerdictPrefix`) plus the shared marker+identity seam (`classifyMarkerAuthorship`, `isMarkerAuthored`, `MARKER_AUTHORSHIP`) used by GH + ADO pollers (extracted in F2+F3+F4; seam added in W-msbf1kf6033f46cb) |
|
|
135
|
+
| `engine/orchestration/dispatch.js` | `addToDispatch()` — dedup by work item ID and dispatchKey |
|
|
136
|
+
| `engine/orchestration/cooldown.js` | `isBranchActive()`, cooldown management |
|
|
137
137
|
| `playbooks/review.md` | Reviewer playbook |
|
|
138
138
|
| `playbooks/fix.md` | Fix agent playbook |
|
|
139
139
|
|
|
@@ -154,6 +154,54 @@ A "branch unchanged" no-op report for a `BUILD_FAILURE`-cause fix used to be con
|
|
|
154
154
|
| `humanFeedback` | `pollPrHumanComments()` | `{ pendingFix, feedbackContent, lastProcessedCommentDate }` |
|
|
155
155
|
| `_noOpFixes[cause]` | `recordPrNoOpFixAttempt()` | Per-cause record `{ count, paused, fingerprint, beforeHead, afterHead }` driving the issue #2969 pause loop |
|
|
156
156
|
| `_pausedCauses` | `queries.getPullRequests()` enrichment | Read-only list of currently-paused cause keys for UI rendering (computed from `_noOpFixes`) |
|
|
157
|
+
| `_abandonedAt` | Poller (on every entry into `abandoned`) | Anchor for the bounded abandoned re-probe window (below). Cleared on reopen |
|
|
158
|
+
| `_reconciliation404Confirmed` | Poller + startup reconciliation | Host confirmed the PR is genuinely deleted — closes the re-probe window early |
|
|
159
|
+
|
|
160
|
+
## Abandoned is not a one-way door (W-msav5edk00g81710)
|
|
161
|
+
|
|
162
|
+
`abandoned` is the only terminal PR status a record can legitimately **leave**:
|
|
163
|
+
GitHub reopens a closed PR and ADO reactivates an abandoned one. Both pollers
|
|
164
|
+
already implement the reopen transition (`oldStatus === abandoned && newStatus
|
|
165
|
+
=== active` → `P-f5reopenreset` clears the stale review session), but before this
|
|
166
|
+
WI that code was unreachable: `forEachActiveGhPr` / `forEachActivePr` filtered on
|
|
167
|
+
`PR_POLLABLE_STATUSES = {active, linked}`, so an abandoned record left the poll
|
|
168
|
+
loop forever and the only un-flip path was the **boot-only, version-gated**
|
|
169
|
+
`reconcileAbandonedPrs`. Incident: `github:opg-microsoft/minions#1154` was closed
|
|
170
|
+
at 16:07:07Z, reopened at 16:10:07Z, and sat `abandoned` for hours while OPEN and
|
|
171
|
+
approved — invisible to `discoverFromPrs`, misread by `syncPrdFromPrs`, and one
|
|
172
|
+
"remove merged + abandoned PRs" sweep away from a permanent `userDeleted`
|
|
173
|
+
tombstone.
|
|
174
|
+
|
|
175
|
+
**The contract now:**
|
|
176
|
+
|
|
177
|
+
- `shared.isPrPollable(pr, { windowMs, now })` is the single pollability
|
|
178
|
+
predicate. Both providers filter through it — never re-check
|
|
179
|
+
`PR_POLLABLE_STATUSES` directly, or the two pollers drift again.
|
|
180
|
+
- An `abandoned` record stays pollable for `engine.abandonedPrRepollWindowMs`
|
|
181
|
+
(`ENGINE_DEFAULTS`, default **7 days**; Settings → *Abandoned PR Re-probe
|
|
182
|
+
Window*; `0` disables) measured from `_abandonedAt`, falling back to `closedAt`
|
|
183
|
+
for records stamped before this WI.
|
|
184
|
+
- **Opt-in per call site.** Only `pollPrStatus` passes the window. The comment
|
|
185
|
+
poller never widens — the reopen signal lives on the status endpoint, so
|
|
186
|
+
widening it would burn quota for no signal.
|
|
187
|
+
- **Cost controls preserved.** The window is aged (not "re-probe every terminal
|
|
188
|
+
record forever"); a record with no anchor fails **CLOSED** and is never
|
|
189
|
+
re-probed; and a host-confirmed deletion sets `_reconciliation404Confirmed`,
|
|
190
|
+
which drops the record out of the window immediately. On GitHub that marker is
|
|
191
|
+
now reachable from the normal poll too, after the same
|
|
192
|
+
`prAbandonConfirmCount` consecutive 404s that the abandon trapdoor requires.
|
|
193
|
+
- `merged` remains genuinely irreversible and is never re-probed.
|
|
194
|
+
- Records older than the window are healed only by the version-gated startup pass
|
|
195
|
+
(`ENGINE_DEFAULTS.abandonedReconciliationVersion`), which is why this WI bumped
|
|
196
|
+
it — that boot pass is the one-off repair for pre-existing mis-stamps.
|
|
197
|
+
|
|
198
|
+
**Implication for the merged/abandoned tracker sweep:** a `status: abandoned`
|
|
199
|
+
stamp is a *sampled observation*, not proof of terminality, so verify at the host
|
|
200
|
+
before tombstoning. `POST /api/pull-requests/delete` sets a permanent
|
|
201
|
+
`userDeleted` marker that pollers must never clear, so a mis-stamp pruned by the
|
|
202
|
+
sweep is unrecoverable. The bounded window shrinks that hazard structurally: a
|
|
203
|
+
mis-stamp now self-heals within one poll cycle instead of persisting until the
|
|
204
|
+
next engine boot.
|
|
157
205
|
|
|
158
206
|
## Platform differences
|
|
159
207
|
|
|
@@ -171,4 +219,5 @@ A "branch unchanged" no-op report for a `BUILD_FAILURE`-cause fix used to be con
|
|
|
171
219
|
- Builds use the merge commit hash as `sourceVersion`, not the source branch commit — compare against `lastMergeCommit.commitId`
|
|
172
220
|
- `partiallySucceeded` counts as passing (warnings, not failures)
|
|
173
221
|
- A stale but passing build is still valid — don't re-trigger builds that already passed
|
|
222
|
+
- `refs/pull/<n>/merge` accumulates builds for EVERY push to the PR (the poller reads `$top=25`), so when no build targets the current merge commit the stale set spans the PR's whole history. `classifyStaleBuilds()` therefore groups by `sourceVersion` via `selectNewestMergeCommitBuilds()` and classifies only the **most recently built merge commit** — OR-ing failures across the full history pinned a PR to `failing` forever once any build had ever failed, burning a build-fix dispatch per poll on an already-green branch (W-msaux21y00g79c48, ADO PR 5531150). When that newest generation is green and the cached status is `failing`, the cached failure is provably older (the current merge commit has no build at all), so it is dropped to `none` — `supersededCachedFailure: true`, which `checkLiveBuildAndConflict()` also surfaces so the pre-dispatch gate skips the fix. Never `passing`: the current merge commit is still unbuilt. Draft PRs hit this path most, since ADO doesn't auto-run branch policies on drafts
|
|
174
223
|
- ADO never auto-clears a reviewer's numeric vote when new commits land, so a hard-reject `-10` vote stays live forever unless the reviewer explicitly re-votes. Both `pollPrStatus()` and the pre-dispatch `checkLiveReviewStatus()` treat a `-10` as **stale/superseded** (resolving to `waiting` instead of `changes-requested`) via `isStaleAdoRejectVote()` once a fix has completed after the vote's last review (`fixedAt > lastReviewedAt`) with no push since (`lastPushedAt <= fixedAt`) — otherwise the auto re-review-after-fix flow can never fire for that PR (issue #633).
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Process termination
|
|
2
|
+
|
|
3
|
+
All Minions-initiated process termination goes through
|
|
4
|
+
`engine/processes/process-utils.js#terminateProcess`. The exported
|
|
5
|
+
`PROCESS_TERMINATION_SITES` object is the authoritative inventory: every entry
|
|
6
|
+
declares its termination mode, ownership proof, and purpose. Unknown sites fail
|
|
7
|
+
closed.
|
|
8
|
+
|
|
9
|
+
Print the current inventory:
|
|
10
|
+
|
|
11
|
+
```powershell
|
|
12
|
+
node -e "console.table(Object.entries(require('./engine/processes/process-utils').PROCESS_TERMINATION_SITES).map(([site, policy]) => ({ site, ...policy })))"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`test/unit/process-termination-gateway.test.js` enforces that:
|
|
16
|
+
|
|
17
|
+
- every production and test-harness `terminateProcess()` call uses a registered site;
|
|
18
|
+
- every non-compatibility inventory entry has a caller;
|
|
19
|
+
- raw process signals, child/facade `.kill()`, and platform kill commands occur
|
|
20
|
+
only inside `engine/processes/process-utils.js`;
|
|
21
|
+
- production code cannot use the legacy compatibility wrappers or assert
|
|
22
|
+
`ownershipVerified`; the gateway re-checks script identity, PID birth time,
|
|
23
|
+
declaration time, tracked-agent command line, or a live child handle itself.
|
|
24
|
+
|
|
25
|
+
Signal-0 liveness probes (`process.kill(pid, 0)`) do not terminate a process and
|
|
26
|
+
are explicitly exempt. The PowerShell/bash snippets emitted in the managed-spawn
|
|
27
|
+
smoke-test instructions clean up a process launched by that same interactive
|
|
28
|
+
snippet; they are guidance text, not engine-executed termination.
|
|
29
|
+
|
|
30
|
+
The supervisor's dashboard replacement site requires consecutive ticks where
|
|
31
|
+
neither the port probe nor `/api/health` proves responsiveness. A single loaded-
|
|
32
|
+
host probe miss cannot reach `engine.supervisor.daemon-replace`.
|
|
33
|
+
|
|
34
|
+
Multi-file unit, integration, and perf runners also fail before spawning workers
|
|
35
|
+
when Windows PID 4 is already above the system-handle safety limit, and stop
|
|
36
|
+
scheduling after an OS resource-exhaustion result. This prevents a damaged host
|
|
37
|
+
from turning one infrastructure failure into process churn and daemon restarts.
|
|
38
|
+
The engine also stamps conservative test concurrency into every cold, pooled,
|
|
39
|
+
and steering-resumed agent environment, so stale PR branches are bounded before
|
|
40
|
+
their repository-local runner code loads.
|
|
@@ -50,13 +50,13 @@ additive.
|
|
|
50
50
|
(source: `config.json` → projects[] `src`).
|
|
51
51
|
- **Mode resolution.** `shared.resolveCheckoutMode(project, workItemType)`
|
|
52
52
|
returns `'live'|'worktree'`, honoring the legacy `worktreeMode` field and the
|
|
53
|
-
`liveValidation` hybrid routing (source: `engine/shared.js:2911`).
|
|
53
|
+
`liveValidation` hybrid routing (source: `engine/core/shared.js:2911`).
|
|
54
54
|
`shared.isLiveCheckoutProject(project)` is the convenience predicate
|
|
55
|
-
(source: `engine/shared.js:2943`).
|
|
55
|
+
(source: `engine/core/shared.js:2943`).
|
|
56
56
|
- **Path resolution.** `shared.resolveSpawnPaths(project, type, minionsDir, opts)`
|
|
57
57
|
short-circuits for live mode and returns
|
|
58
58
|
`{ cwd: path.resolve(project.localPath) (+workdir), worktreeRootDir:null, liveMode:true }`
|
|
59
|
-
(source: `engine/shared.js:6355-6382`). It throws `LIVE_CHECKOUT_NO_LOCALPATH`
|
|
59
|
+
(source: `engine/core/shared.js:6355-6382`). It throws `LIVE_CHECKOUT_NO_LOCALPATH`
|
|
60
60
|
if `localPath` is falsy. **This is the single point where the enlistment path
|
|
61
61
|
is chosen** — it hard-reads `project.localPath`.
|
|
62
62
|
- **Per-project mutating-concurrency cap of 1.** The allocation loop in the
|
|
@@ -69,25 +69,25 @@ additive.
|
|
|
69
69
|
it is derived from `dispatch.active`, which is SQL-backed** (see persistence
|
|
70
70
|
below). The pool needs this to become a **per-path** claim, not per-project.
|
|
71
71
|
- **In-place checkout + recovery policies.** `spawnAgent` (engine.js) calls
|
|
72
|
-
`engine/live-checkout.js#prepareLiveCheckout({ localPath: cwd, branchName, … })`
|
|
72
|
+
`engine/worktrees/live-checkout.js#prepareLiveCheckout({ localPath: cwd, branchName, … })`
|
|
73
73
|
which: runs `git status --porcelain` (dirty gate), mid-operation / detached-HEAD
|
|
74
74
|
preflight, stale-base guard, and branch checkout/creation — all against the one
|
|
75
|
-
`cwd` (source: `engine.js:2453-2497`, `engine/live-checkout.js:374`). The dirty
|
|
75
|
+
`cwd` (source: `engine.js:2453-2497`, `engine/worktrees/live-checkout.js:374`). The dirty
|
|
76
76
|
recovery policies resolve per-project-then-fleet-wide:
|
|
77
|
-
- `liveCheckoutAutoStash` — `resolveLiveCheckoutAutoStash` (source: `engine/live-checkout.js:1248`), applied via `applyLiveCheckoutAutoStash` (source: `engine.js:2509`).
|
|
78
|
-
- `liveCheckoutAutoReset` — `shared.resolveLiveCheckoutAutoReset` (self-resolves by matching project on `localPath`, source: `docs/live-checkout-mode.md` §2b, `engine/live-checkout.js:332`).
|
|
79
|
-
- `liveCheckoutAutoBaseRepair` — `_defaultResolveAutoBaseRepair` (source: `engine/live-checkout.js:352`).
|
|
77
|
+
- `liveCheckoutAutoStash` — `resolveLiveCheckoutAutoStash` (source: `engine/worktrees/live-checkout.js:1248`), applied via `applyLiveCheckoutAutoStash` (source: `engine.js:2509`).
|
|
78
|
+
- `liveCheckoutAutoReset` — `shared.resolveLiveCheckoutAutoReset` (self-resolves by matching project on `localPath`, source: `docs/live-checkout-mode.md` §2b, `engine/worktrees/live-checkout.js:332`).
|
|
79
|
+
- `liveCheckoutAutoBaseRepair` — `_defaultResolveAutoBaseRepair` (source: `engine/worktrees/live-checkout.js:352`).
|
|
80
80
|
**All three resolve/match by `localPath`**, so they need to key off the
|
|
81
81
|
selected enlistment path, not the project's canonical `localPath`.
|
|
82
82
|
- **Dispatch-end restore.** `restoreLiveCheckoutAtDispatchEnd` /
|
|
83
83
|
`maybeRestoreLiveCheckoutFromRecord` put the tree back on `originalRef`
|
|
84
|
-
(source: `engine/live-checkout.js:1009, 1423`). These operate on a recorded
|
|
84
|
+
(source: `engine/worktrees/live-checkout.js:1009, 1423`). These operate on a recorded
|
|
85
85
|
`localPath` and must record/restore the **selected** enlistment.
|
|
86
86
|
|
|
87
87
|
### Persistence (survives restart)
|
|
88
88
|
|
|
89
89
|
- Dispatch state lives in **SQLite** `engine/state.db` `dispatches` table via
|
|
90
|
-
`engine/dispatch-store.js` (`readDispatchSectioned` / `applyDispatchMutation`).
|
|
90
|
+
`engine/persistence/dispatch-store.js` (`readDispatchSectioned` / `applyDispatchMutation`).
|
|
91
91
|
Each active dispatch is one JSON record. **Because `liveProjectsInUse` is
|
|
92
92
|
rebuilt from `dispatch.active` every tick, recording the claimed enlistment
|
|
93
93
|
path on the active dispatch record makes the per-path claim automatically
|
|
@@ -99,7 +99,7 @@ additive.
|
|
|
99
99
|
|
|
100
100
|
- `meta.workdir` is already a validated relative-POSIX subpath under the spawn
|
|
101
101
|
base; when set, the engine lands the agent at `<base>/<workdir>` and clips
|
|
102
|
-
harness discovery to that subtree (source: `engine/shared.js:6404-6416`,
|
|
102
|
+
harness discovery to that subtree (source: `engine/core/shared.js:6404-6416`,
|
|
103
103
|
`validateWorkItemWorkdir`). This is the natural signal for "which subpath does
|
|
104
104
|
this WI need" — subproject validation can reuse/extend it. There is also
|
|
105
105
|
`detectDominantSubproject({ projectPath, changedPaths })` used post-completion
|
|
@@ -118,7 +118,7 @@ additive.
|
|
|
118
118
|
- **Project cards.** `dashboard/js/render-other.js` renders per-project git
|
|
119
119
|
branch/dirty/state pills from `p.gitBranch/p.gitDirty/p.gitState`
|
|
120
120
|
(source: `dashboard/js/render-other.js:103-172`), fed by the per-`localPath`
|
|
121
|
-
git-status probe cache in `engine/queries.js:2512-2607` (15s TTL, keyed by
|
|
121
|
+
git-status probe cache in `engine/core/queries.js:2512-2607` (15s TTL, keyed by
|
|
122
122
|
normalized `localPath`). Per-enlistment status reuses this probe per path.
|
|
123
123
|
- **Status payload.** `dashboard.js:2768-2785` emits `checkoutMode`,
|
|
124
124
|
`liveValidationType`, etc. per project — the place to add pool-member status.
|
|
@@ -172,7 +172,7 @@ Key design decisions and rationale:
|
|
|
172
172
|
dispatch record before the agent spawns, closing the double-claim window.
|
|
173
173
|
|
|
174
174
|
4. **Clean-tree + subpath validation at selection are cheap.** Clean-tree reuses
|
|
175
|
-
the existing 15s-TTL per-path git-status probe cache (`engine/queries.js`), so
|
|
175
|
+
the existing 15s-TTL per-path git-status probe cache (`engine/core/queries.js`), so
|
|
176
176
|
selection does not add a fresh `git status` per tick per path. Subpath
|
|
177
177
|
validation is a pure `fs.existsSync(<path>/<subpath>)` (or a `sparseScope`
|
|
178
178
|
prefix match when declared) — no build, no git. Rationale: selection runs every
|