@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
|
@@ -1,45 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* engine/github.js — GitHub integration for Minions engine.
|
|
2
|
+
* engine/providers/github.js — GitHub integration for Minions engine.
|
|
3
3
|
* Parallel to ado.js: PR status polling, comment polling, reconciliation for GitHub-hosted projects.
|
|
4
4
|
* Uses `gh` CLI for all GitHub API calls.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const shared = require('
|
|
8
|
-
const { exec, execAsync, getProjects, readWorkItems, readPullRequests, safeJson, safeJsonArr, safeWrite, mutateMetrics, mutatePullRequests, MINIONS_DIR, getPrLinks, backfillPrPrdItems, log, ts, dateStamp, PR_STATUS,
|
|
9
|
-
const { getPrs } = require('
|
|
7
|
+
const shared = require('../core/shared');
|
|
8
|
+
const { exec, execAsync, getProjects, readWorkItems, readPullRequests, safeJson, safeJsonArr, safeWrite, mutateMetrics, mutatePullRequests, MINIONS_DIR, getPrLinks, backfillPrPrdItems, log, ts, dateStamp, PR_STATUS, BUILD_STATUS, REVIEW_STATUS, FETCH_TIMEOUT_MS, ENGINE_DEFAULTS, createThrottleTracker, getProjectOrg } = shared;
|
|
9
|
+
const { getPrs } = require('../core/queries');
|
|
10
10
|
const {
|
|
11
11
|
isPreviewStatusBody,
|
|
12
|
-
hasMinionsMarker,
|
|
13
12
|
hasVerdictPrefix,
|
|
13
|
+
classifyMarkerAuthorship,
|
|
14
|
+
isMarkerAuthored,
|
|
14
15
|
classifyNonActionableAutomationComment,
|
|
15
16
|
} = require('./comment-classifier');
|
|
16
|
-
const { wrapUntrusted, buildSource } = require('
|
|
17
|
-
const reviewLearning = require('
|
|
17
|
+
const { wrapUntrusted, buildSource } = require('../core/untrusted-fence');
|
|
18
|
+
const reviewLearning = require('../memory/review-learning');
|
|
18
19
|
const ghToken = require('./gh-token');
|
|
19
20
|
const path = require('path');
|
|
20
21
|
|
|
21
22
|
// Lazy require to avoid circular dependency — only needed for engine().handlePostMerge
|
|
22
23
|
let _engine = null;
|
|
23
24
|
function engine() {
|
|
24
|
-
if (!_engine) _engine = require('
|
|
25
|
+
if (!_engine) _engine = require('../../engine');
|
|
25
26
|
return _engine;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
// Lazy require for dispatch module (avoids circular dependency via engine)
|
|
29
30
|
let _dispatch = null;
|
|
30
|
-
function dispatchModule() { if (!_dispatch) _dispatch = require('
|
|
31
|
+
function dispatchModule() { if (!_dispatch) _dispatch = require('../orchestration/dispatch'); return _dispatch; }
|
|
31
32
|
|
|
32
33
|
// Lazy require for lifecycle module — only needed for the stale-noopfix sweep
|
|
33
34
|
// in pollPrStatus (W-mpx44p05000ze8d8). lifecycle.js lazy-requires github.js
|
|
34
35
|
// back, so this MUST stay lazy to avoid a circular import at module-load time.
|
|
35
36
|
let _lifecycle = null;
|
|
36
|
-
function lifecycleModule() { if (!_lifecycle) _lifecycle = require('
|
|
37
|
+
function lifecycleModule() { if (!_lifecycle) _lifecycle = require('../orchestration/lifecycle'); return _lifecycle; }
|
|
37
38
|
|
|
38
39
|
// W-mp5trwh60008386d: test seam so unit tests can mock `gh api` shell-outs
|
|
39
40
|
// without spawning the real CLI. Production code goes through the real
|
|
40
41
|
// `execAsync` from shared; tests call `_setExecAsyncForTest(fn)` to swap in
|
|
41
42
|
// a stub. Pass `null` to restore. Mirrors the `_setAdoTokenForTest` pattern
|
|
42
|
-
// in engine/ado.js.
|
|
43
|
+
// in engine/ado/index.js.
|
|
43
44
|
let _execAsyncOverride = null;
|
|
44
45
|
function _runExec(cmd, opts) {
|
|
45
46
|
return (_execAsyncOverride || execAsync)(cmd, opts);
|
|
@@ -142,11 +143,11 @@ function getConfiguredGitHubAuthorLogins(config = {}) {
|
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
// P-f23classifier (F4): delegates to the host-agnostic VERDICT prefix check
|
|
145
|
-
// in engine/comment-classifier.js. The literal regex used to live here;
|
|
146
|
+
// in engine/providers/comment-classifier.js. The literal regex used to live here;
|
|
146
147
|
// extraction allowed F4 (Wave 3) to tighten the prefix in a single place.
|
|
147
148
|
// The tightened form requires `VERDICT: APPROVE|REQUEST_CHANGES` at line
|
|
148
149
|
// start (under /m), optionally wrapped in `**`, `__`, or `#+ ` markers —
|
|
149
|
-
// see engine/comment-classifier.js for the full contract.
|
|
150
|
+
// see engine/providers/comment-classifier.js for the full contract.
|
|
150
151
|
function _hasMinionsReviewVerdict(body) {
|
|
151
152
|
return hasVerdictPrefix(body);
|
|
152
153
|
}
|
|
@@ -169,7 +170,7 @@ function _isGitHubBotComment(c) {
|
|
|
169
170
|
|
|
170
171
|
// P-f23classifier (F2): retains the GH-side bot-author gate (ADO has no
|
|
171
172
|
// `user.type === 'bot'` equivalent) but delegates the body check to the
|
|
172
|
-
// host-agnostic isPreviewStatusBody helper in engine/comment-classifier.js.
|
|
173
|
+
// host-agnostic isPreviewStatusBody helper in engine/providers/comment-classifier.js.
|
|
173
174
|
// The body matcher covers Coverage/Build/Test/Deploy/Pipeline report
|
|
174
175
|
// headings + Firebase App Distribution + Appetize + Deploy-ready phrases +
|
|
175
176
|
// shields.io badges (same set as before the extraction).
|
|
@@ -213,11 +214,11 @@ function _entryCommentKey(entry) {
|
|
|
213
214
|
// Replaces the body-shape classifier chain (PR #2431's _isAgentSelfReviewDeclinedComment,
|
|
214
215
|
// PR #2442's _isAgentPositiveSignalComment + _hasAgentPositiveSignalBody) with a single
|
|
215
216
|
// structural check: a Minions-authored comment carries `MINIONS_COMMENT_MARKER` (defined
|
|
216
|
-
// in engine/gh-comment.js) on its own first line AND was authored by the viewer (the
|
|
217
|
+
// in engine/providers/gh-comment.js) on its own first line AND was authored by the viewer (the
|
|
217
218
|
// shared PAT identity used by all minions agents).
|
|
218
219
|
//
|
|
219
|
-
// Both gates are required:
|
|
220
|
-
// - `viewerDidAuthor:
|
|
220
|
+
// Both gates are required whenever the viewer identity is KNOWN:
|
|
221
|
+
// - `viewerDidAuthor: false` prevents humans from spoofing the marker by quoting it.
|
|
221
222
|
// - `MINIONS_COMMENT_MARKER_RE` enforces "own first line" — quoted markers in the
|
|
222
223
|
// middle of a comment, or markers with trailing content on the same line, do NOT
|
|
223
224
|
// match. This is the structural complement to the viewer check.
|
|
@@ -233,12 +234,31 @@ function _entryCommentKey(entry) {
|
|
|
233
234
|
// Sub-item -c migrates existing engine call sites + playbook examples.
|
|
234
235
|
//
|
|
235
236
|
// P-f23classifier (F3): the body-only "structural marker on own first line"
|
|
236
|
-
// check is now hasMinionsMarker in engine/comment-classifier.js.
|
|
237
|
-
//
|
|
238
|
-
//
|
|
237
|
+
// check is now hasMinionsMarker in engine/providers/comment-classifier.js.
|
|
238
|
+
//
|
|
239
|
+
// W-msbf1kf6033f46cb — marker authorship resolves through the shared
|
|
240
|
+
// `classifyMarkerAuthorship` seam that engine/ado/index.js also uses, so the
|
|
241
|
+
// two pollers cannot drift (mirror-ado-github-poll-fix convention). Only the
|
|
242
|
+
// identity SOURCE differs: GH derives it from `viewerDidAuthor` (resolved at
|
|
243
|
+
// runtime), ADO from the `config.engine.minionsAdoIdentities` allowlist.
|
|
244
|
+
//
|
|
245
|
+
// `viewerDidAuthor` is a tri-state here: `true`/`false` mean the viewer login
|
|
246
|
+
// was resolved and the comment either is or is not ours, while `null`/absent
|
|
247
|
+
// means the signal is unavailable (`_backfillViewerDidAuthor` no-ops without a
|
|
248
|
+
// viewer login or an author login). The spoof guard is unchanged for the
|
|
249
|
+
// `false` case — a real human who pasted our marker stays actionable. Only the
|
|
250
|
+
// unavailable case now fails safe instead of open, matching ADO. In production
|
|
251
|
+
// GH never reaches that case: `pollPrHumanComments` already skips the whole PR
|
|
252
|
+
// when `_resolveViewerLogin` returns null (OQ-Y), so this is defense in depth
|
|
253
|
+
// against that upstream guard being removed.
|
|
239
254
|
function _isMinionsAuthoredComment(c) {
|
|
240
|
-
if (!c
|
|
241
|
-
|
|
255
|
+
if (!c) return false;
|
|
256
|
+
const identityAvailable = c.viewerDidAuthor === true || c.viewerDidAuthor === false;
|
|
257
|
+
return isMarkerAuthored(classifyMarkerAuthorship({
|
|
258
|
+
body: c.body || '',
|
|
259
|
+
identityAvailable,
|
|
260
|
+
identityMatches: c.viewerDidAuthor === true,
|
|
261
|
+
}));
|
|
242
262
|
}
|
|
243
263
|
|
|
244
264
|
// ─── Viewer Login Resolution ────────────────────────────────────────────────
|
|
@@ -251,7 +271,7 @@ function _isMinionsAuthoredComment(c) {
|
|
|
251
271
|
// the viewer login resolved via `gh api user`.
|
|
252
272
|
//
|
|
253
273
|
// W-mp76pw7a001da7c1 — The viewer login is no longer process-wide because
|
|
254
|
-
// `ghApi` now resolves a per-slug token via `engine/gh-token.js`. Different
|
|
274
|
+
// `ghApi` now resolves a per-slug token via `engine/providers/gh-token.js`. Different
|
|
255
275
|
// accounts return different logins from `gh api user`, so the cache is keyed
|
|
256
276
|
// by the resolved account name (or the sentinel `__ambient__` when no
|
|
257
277
|
// account mapping applies and the call falls back to the ambient gh
|
|
@@ -291,7 +311,7 @@ async function _resolveViewerLogin(slug) {
|
|
|
291
311
|
// data is still worth tracking so future audits can prioritize a deeper fix
|
|
292
312
|
// (e.g. a credentialed re-probe of the gh CLI). Global counter under
|
|
293
313
|
// `_engine.viewerLoginResolutionFailures`; surfaced through the existing
|
|
294
|
-
// engine metrics aggregation (`getMetrics()` in `engine/queries.js`).
|
|
314
|
+
// engine metrics aggregation (`getMetrics()` in `engine/core/queries.js`).
|
|
295
315
|
function _incrementViewerLoginResolutionFailures() {
|
|
296
316
|
try {
|
|
297
317
|
mutateMetrics((metrics) => {
|
|
@@ -530,10 +550,18 @@ async function fetchGhBuildErrorLog(slug, failedRuns) {
|
|
|
530
550
|
|
|
531
551
|
// ─── Shared PR Polling Loop ─────────────────────────────────────────────────
|
|
532
552
|
|
|
533
|
-
async function forEachActiveGhPr(config, callback) {
|
|
553
|
+
async function forEachActiveGhPr(config, callback, options = {}) {
|
|
534
554
|
const projects = getProjects(config).filter(isGitHub);
|
|
535
555
|
let totalUpdated = 0;
|
|
536
556
|
|
|
557
|
+
// W-msav5edk00g81710 — bounded re-probe of recently-abandoned records. Opt-in
|
|
558
|
+
// per call site (pollPrStatus only): the reopen signal lives on the status
|
|
559
|
+
// endpoint, so widening the comment poller would burn quota for no signal.
|
|
560
|
+
const pollOpts = {
|
|
561
|
+
windowMs: Number(options.abandonedRepollWindowMs) || 0,
|
|
562
|
+
now: Date.now(),
|
|
563
|
+
};
|
|
564
|
+
|
|
537
565
|
for (const project of projects) {
|
|
538
566
|
// W-mp625n27000m6e78 — Honor per-project workSources.pullRequests.enabled.
|
|
539
567
|
// Default-ON: only skip when explicitly disabled (=== false). Missing/undefined
|
|
@@ -545,7 +573,7 @@ async function forEachActiveGhPr(config, callback) {
|
|
|
545
573
|
if (src && src.enabled === false) continue;
|
|
546
574
|
|
|
547
575
|
const prs = getPrs(project);
|
|
548
|
-
const activePrs = prs.filter(pr =>
|
|
576
|
+
const activePrs = prs.filter(pr => shared.isPrPollable(pr, pollOpts)
|
|
549
577
|
&& shared.isPrCompatibleWithProject(project, pr, pr.url || ''));
|
|
550
578
|
if (activePrs.length === 0) continue;
|
|
551
579
|
|
|
@@ -640,7 +668,7 @@ async function forEachActiveGhPr(config, callback) {
|
|
|
640
668
|
for (const slug of getProjectRepoSlugs(project)) configuredGhProjectsBySlug.set(slug.toLowerCase(), project);
|
|
641
669
|
}
|
|
642
670
|
const activeCentral = centralPrs.filter(pr => {
|
|
643
|
-
if (!
|
|
671
|
+
if (!shared.isPrPollable(pr, pollOpts)) return false;
|
|
644
672
|
if (!pr.url) return false;
|
|
645
673
|
// Symmetric fix for #328: only defer to the project loop when the PR is
|
|
646
674
|
// ACTUALLY present in that project's per-project file. If it's absent there,
|
|
@@ -696,7 +724,7 @@ async function forEachActiveGhPr(config, callback) {
|
|
|
696
724
|
if (updated) {
|
|
697
725
|
// Also update title/author/branch if still placeholder
|
|
698
726
|
const currentTitle = pr.title || '';
|
|
699
|
-
if (shared.isPlaceholderPrTitle(currentTitle) || pr.
|
|
727
|
+
if (shared.isPlaceholderPrTitle(currentTitle) || !pr.author || pr.description === undefined) {
|
|
700
728
|
const prData = await ghApi(`/pulls/${prNum}`, slug);
|
|
701
729
|
if (prData) {
|
|
702
730
|
const latestTitle = pr.title || '';
|
|
@@ -704,7 +732,10 @@ async function forEachActiveGhPr(config, callback) {
|
|
|
704
732
|
pr.title = (prData.title || latestTitle).slice(0, 120);
|
|
705
733
|
}
|
|
706
734
|
if (pr.description === undefined) pr.description = (prData.body || '').slice(0, 500);
|
|
707
|
-
|
|
735
|
+
// Populate the real author identity from PR metadata. Never overwrite
|
|
736
|
+
// pr.agent (the Minions author-agent) with a human login.
|
|
737
|
+
const ghAuthor = shared.buildGithubAuthorIdentity(prData.user);
|
|
738
|
+
if (ghAuthor) pr.author = shared.mergePrAuthorIdentity(pr.author, ghAuthor);
|
|
708
739
|
if (!pr.branch && prData.head?.ref) {
|
|
709
740
|
// P-a7c4d2e8 (F3): validate API-derived branch ref before
|
|
710
741
|
// persistence. Defensive degrade — log and skip on invalid ref
|
|
@@ -757,7 +788,7 @@ async function forEachActiveGhPr(config, callback) {
|
|
|
757
788
|
}
|
|
758
789
|
|
|
759
790
|
/**
|
|
760
|
-
* Issue #633 (mirror of engine/ado.js isStaleAdoRejectVote) — GitHub reviews
|
|
791
|
+
* Issue #633 (mirror of engine/ado/index.js isStaleAdoRejectVote) — GitHub reviews
|
|
761
792
|
* generally get dismissed/replaced when a reviewer re-reviews, but a stale
|
|
762
793
|
* CHANGES_REQUESTED review from a login that never re-reviews after a fix can
|
|
763
794
|
* still pin `reviewStatus` at 'changes-requested' forever (the reviewer's
|
|
@@ -796,19 +827,36 @@ async function pollPrStatus(config) {
|
|
|
796
827
|
if (next >= confirmCount) {
|
|
797
828
|
if (pr.status !== PR_STATUS.ABANDONED) {
|
|
798
829
|
pr.status = PR_STATUS.ABANDONED;
|
|
830
|
+
// W-msav5edk00g81710: anchor the bounded re-probe window. This path
|
|
831
|
+
// never sets `closedAt` (there is no platform record to read it from),
|
|
832
|
+
// so without the stamp a 404-abandoned record would have no age at all
|
|
833
|
+
// and `isPrPollable` would fail closed on it forever.
|
|
834
|
+
shared.stampPrAbandonedAt(pr, ts());
|
|
799
835
|
delete pr._consecutive404s;
|
|
800
836
|
log('info', `PR ${pr.id} returned 404 ${next} consecutive polls — marking abandoned`);
|
|
801
837
|
return true;
|
|
802
838
|
}
|
|
803
|
-
// Already abandoned
|
|
839
|
+
// Already abandoned and still 404ing under a healthy base-repo probe —
|
|
840
|
+
// that is exactly the evidence reconcileAbandonedPrs uses to confirm a
|
|
841
|
+
// genuine deletion (W-msav5edk00g81710). Record it so the PR drops out
|
|
842
|
+
// of the bounded re-probe window immediately instead of sawtoothing the
|
|
843
|
+
// counter for the window's full duration.
|
|
844
|
+
let changed = false;
|
|
804
845
|
if (pr._consecutive404s) {
|
|
805
846
|
delete pr._consecutive404s;
|
|
806
|
-
|
|
847
|
+
changed = true;
|
|
848
|
+
}
|
|
849
|
+
if (!pr._reconciliation404Confirmed) {
|
|
850
|
+
pr._reconciliation404Confirmed = true;
|
|
851
|
+
log('info', `PR ${pr.id} confirmed deleted after ${next} consecutive 404s — closing the abandoned re-probe window`);
|
|
852
|
+
changed = true;
|
|
807
853
|
}
|
|
808
|
-
return
|
|
854
|
+
return changed;
|
|
809
855
|
}
|
|
810
856
|
pr._consecutive404s = next;
|
|
811
|
-
log('warn',
|
|
857
|
+
log('warn', pr.status === PR_STATUS.ABANDONED
|
|
858
|
+
? `PR ${pr.id} returned 404 (${next}/${confirmCount}) — confirming deletion before closing the abandoned re-probe window`
|
|
859
|
+
: `PR ${pr.id} returned 404 (${next}/${confirmCount}) — deferring abandonment until threshold`);
|
|
812
860
|
return true; // counter increment is a state change worth persisting
|
|
813
861
|
}
|
|
814
862
|
|
|
@@ -855,9 +903,17 @@ async function pollPrStatus(config) {
|
|
|
855
903
|
pr.description = (prData.body || '').slice(0, 500);
|
|
856
904
|
updated = true;
|
|
857
905
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
906
|
+
// Populate the real author identity from PR metadata. Never overwrite
|
|
907
|
+
// pr.agent (the Minions author-agent) with a human login.
|
|
908
|
+
{
|
|
909
|
+
const ghAuthor = shared.buildGithubAuthorIdentity(prData.user);
|
|
910
|
+
if (ghAuthor) {
|
|
911
|
+
const mergedAuthor = shared.mergePrAuthorIdentity(pr.author, ghAuthor);
|
|
912
|
+
if (JSON.stringify(mergedAuthor) !== JSON.stringify(pr.author || null)) {
|
|
913
|
+
pr.author = mergedAuthor;
|
|
914
|
+
updated = true;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
861
917
|
}
|
|
862
918
|
|
|
863
919
|
// Map GitHub PR state to minions status
|
|
@@ -904,7 +960,7 @@ async function pollPrStatus(config) {
|
|
|
904
960
|
}
|
|
905
961
|
|
|
906
962
|
// P-w1a3f9b2 — Phase 1.1: plumb mergeable / isDraft / mergeStateStatus /
|
|
907
|
-
// headRefOid onto the PR object so watches captureState (engine/watches.js)
|
|
963
|
+
// headRefOid onto the PR object so watches captureState (engine/watches/index.js)
|
|
908
964
|
// and future predicates (Phase 2.1: head-commit-change, mergeable-flipped,
|
|
909
965
|
// ready-for-merge, draft-flipped) can read them. We mirror GitHub's
|
|
910
966
|
// tri-state contract for `mergeable` (true|false|null — see
|
|
@@ -949,9 +1005,9 @@ async function pollPrStatus(config) {
|
|
|
949
1005
|
// needing a separate counter. When skipped (flag off, PR closed,
|
|
950
1006
|
// missing refs, cross-repo fork, or compare-API error), behindBy is
|
|
951
1007
|
// explicitly set to null so the `behind-master` predicate
|
|
952
|
-
// (engine/watches.js BEHIND_MASTER) treats it as "no signal" and does
|
|
1008
|
+
// (engine/watches/index.js BEHIND_MASTER) treats it as "no signal" and does
|
|
953
1009
|
// not over-fire on legacy / stale state. ADO PRs are handled in
|
|
954
|
-
// engine/ado.js with the same null contract.
|
|
1010
|
+
// engine/ado/index.js with the same null contract.
|
|
955
1011
|
const includeBehindBy = config.engine?.watchesIncludeBehindBy === true;
|
|
956
1012
|
let newBehindBy = null;
|
|
957
1013
|
if (includeBehindBy && prData.state === 'open' && prData.base?.ref && prData.head?.sha) {
|
|
@@ -987,6 +1043,12 @@ async function pollPrStatus(config) {
|
|
|
987
1043
|
if ((newStatus === PR_STATUS.ABANDONED || newStatus === PR_STATUS.CLOSED) && !pr.closedAt) {
|
|
988
1044
|
pr.closedAt = prData.closed_at || ts();
|
|
989
1045
|
}
|
|
1046
|
+
// W-msav5edk00g81710: anchor the bounded re-probe window on every entry
|
|
1047
|
+
// into `abandoned`, including a re-close after a reopen (the window must
|
|
1048
|
+
// restart, not inherit the first closure's age).
|
|
1049
|
+
if (newStatus === PR_STATUS.ABANDONED) {
|
|
1050
|
+
shared.stampPrAbandonedAt(pr, prData.closed_at || pr.closedAt || ts());
|
|
1051
|
+
}
|
|
990
1052
|
|
|
991
1053
|
if (newStatus === PR_STATUS.MERGED || newStatus === PR_STATUS.ABANDONED) {
|
|
992
1054
|
// Resolve stale 'waiting' review status — won't be polled again after this
|
|
@@ -1022,7 +1084,7 @@ async function pollPrStatus(config) {
|
|
|
1022
1084
|
// references it. Invariant #14 ('approved is permanent terminal') is
|
|
1023
1085
|
// preserved: we keep reviewStatus when it was 'approved' rather than
|
|
1024
1086
|
// carving a wasReopened exception into the 8+ guard sites in
|
|
1025
|
-
// engine/lifecycle.js, engine.js, engine/github.js, and engine/ado.js.
|
|
1087
|
+
// engine/orchestration/lifecycle.js, engine.js, engine/providers/github.js, and engine/ado/index.js.
|
|
1026
1088
|
// Re-review safety on new commits is handled by the pre-existing
|
|
1027
1089
|
// pr.lastPushedAt > pr.lastReviewedAt path (engine.js alreadyReviewed
|
|
1028
1090
|
// gate) plus the changes-requested override in lifecycle.js.
|
|
@@ -1035,6 +1097,11 @@ async function pollPrStatus(config) {
|
|
|
1035
1097
|
// F6 not yet shipped (field absent).
|
|
1036
1098
|
delete pr.humanFeedback.editsSeen;
|
|
1037
1099
|
pr.fixDispatched = false;
|
|
1100
|
+
// W-msav5edk00g81710: the record is no longer abandoned, so the
|
|
1101
|
+
// re-probe anchor (and the stale close timestamp it falls back to)
|
|
1102
|
+
// must not survive — a later re-close re-stamps a fresh window.
|
|
1103
|
+
shared.clearPrAbandonedAt(pr);
|
|
1104
|
+
delete pr.closedAt;
|
|
1038
1105
|
if (pr.reviewStatus !== REVIEW_STATUS.APPROVED) pr.reviewStatus = REVIEW_STATUS.PENDING;
|
|
1039
1106
|
log('info', `PR ${pr.id} reopened — reset transient state (reviewStatus=${pr.reviewStatus})`);
|
|
1040
1107
|
}
|
|
@@ -1215,6 +1282,11 @@ async function pollPrStatus(config) {
|
|
|
1215
1282
|
}
|
|
1216
1283
|
|
|
1217
1284
|
return updated;
|
|
1285
|
+
}, {
|
|
1286
|
+
// W-msav5edk00g81710 — only the STATUS poll re-probes recently-abandoned
|
|
1287
|
+
// records; that is where the host reports the reopen. `0` (operator opt-out)
|
|
1288
|
+
// restores the strict PR_POLLABLE_STATUSES filter.
|
|
1289
|
+
abandonedRepollWindowMs: shared.resolveAbandonedPrRepollWindowMs(config),
|
|
1218
1290
|
});
|
|
1219
1291
|
|
|
1220
1292
|
if (totalUpdated > 0) {
|
|
@@ -1228,13 +1300,13 @@ async function pollPrHumanComments(config) {
|
|
|
1228
1300
|
// Load dispatch state once per poll cycle (cached for ~2s by queries.getDispatch).
|
|
1229
1301
|
// (Previously used by `_isAgentSelfReviewDeclinedComment` to verify dispatch ids;
|
|
1230
1302
|
// kept loaded in case downstream phases need dispatch context — cheap on cache hit.)
|
|
1231
|
-
const queries = require('
|
|
1303
|
+
const queries = require('../core/queries');
|
|
1232
1304
|
const dispatch = (() => {
|
|
1233
1305
|
try { return queries.getDispatch(); }
|
|
1234
1306
|
catch { return { pending: [], active: [], completed: [] }; }
|
|
1235
1307
|
})();
|
|
1236
1308
|
// Viewer login is resolved per-PR (cheap on cache hit) because each slug
|
|
1237
|
-
// can route to a different gh account via `engine/gh-token.js`. The cache
|
|
1309
|
+
// can route to a different gh account via `engine/providers/gh-token.js`. The cache
|
|
1238
1310
|
// is keyed by the resolved account name, so multiple PRs sharing one
|
|
1239
1311
|
// account pay only the first shell-out.
|
|
1240
1312
|
const totalUpdated = await forEachActiveGhPr(config, async (project, pr, prNum, slug) => {
|
|
@@ -1463,7 +1535,7 @@ async function pollPrHumanComments(config) {
|
|
|
1463
1535
|
// `${threadId}:${commentId}` shape so the guard compares a single key
|
|
1464
1536
|
// form. Falls back to the bare id when `_type` is absent (defensive;
|
|
1465
1537
|
// every entry above sets it, but the pollPrHumanComments path in
|
|
1466
|
-
// engine/ado.js writes the legacy format too).
|
|
1538
|
+
// engine/ado/index.js writes the legacy format too).
|
|
1467
1539
|
lastProcessedCommentKey: newComments[newComments.length - 1].commentKey || _entryCommentKey(newComments[newComments.length - 1]),
|
|
1468
1540
|
pendingFix: true,
|
|
1469
1541
|
feedbackContent,
|
|
@@ -1567,7 +1639,8 @@ async function reconcilePrs(config) {
|
|
|
1567
1639
|
id: prId,
|
|
1568
1640
|
prNumber: ghPr.number,
|
|
1569
1641
|
title: (ghPr.title || `PR #${ghPr.number}`).slice(0, 120),
|
|
1570
|
-
agent: (linkedItem?.dispatched_to ||
|
|
1642
|
+
agent: (linkedItem?.dispatched_to || shared.PR_AGENT_HUMAN).toLowerCase(),
|
|
1643
|
+
author: shared.buildGithubAuthorIdentity(ghPr.user),
|
|
1571
1644
|
branch,
|
|
1572
1645
|
reviewStatus: REVIEW_STATUS.PENDING,
|
|
1573
1646
|
status: PR_STATUS.ACTIVE,
|
|
@@ -1611,7 +1684,8 @@ async function reconcilePrs(config) {
|
|
|
1611
1684
|
id: prId,
|
|
1612
1685
|
prNumber: ghPr.number,
|
|
1613
1686
|
title: (ghPr.title || `PR #${ghPr.number}`).slice(0, 120),
|
|
1614
|
-
agent: (linkedItem?.dispatched_to ||
|
|
1687
|
+
agent: (linkedItem?.dispatched_to || shared.PR_AGENT_HUMAN).toLowerCase(),
|
|
1688
|
+
author: shared.buildGithubAuthorIdentity(ghPr.user),
|
|
1615
1689
|
branch,
|
|
1616
1690
|
reviewStatus: REVIEW_STATUS.PENDING,
|
|
1617
1691
|
status: PR_STATUS.ACTIVE,
|
|
@@ -1883,12 +1957,12 @@ async function checkLiveBuildAndConflict(pr, project) {
|
|
|
1883
1957
|
// ─── One-Shot Startup Reconciliation for Abandoned PRs (W-mp60tw0u000j3931) ───
|
|
1884
1958
|
//
|
|
1885
1959
|
// Pairs with the W-mp5trwh60008386d 404-hardening fix in pollPrStatus.
|
|
1886
|
-
//
|
|
1887
|
-
//
|
|
1888
|
-
//
|
|
1889
|
-
// historical false-flips (e.g. the
|
|
1890
|
-
// gh-auth-flip incident)
|
|
1891
|
-
//
|
|
1960
|
+
// W-msav5edk00g81710 gave `abandoned` records a BOUNDED re-probe window in the
|
|
1961
|
+
// normal poll (`shared.isPrPollable`), so a recently-abandoned PR that gets
|
|
1962
|
+
// reopened now self-heals within one poll cycle. This pass remains the only
|
|
1963
|
+
// heal path for records OUTSIDE that window — historical false-flips (e.g. the
|
|
1964
|
+
// 16 PRs marked abandoned by the 2026-05-14 gh-auth-flip incident) and anything
|
|
1965
|
+
// abandoned longer ago than `engine.abandonedPrRepollWindowMs`.
|
|
1892
1966
|
//
|
|
1893
1967
|
// This function is the explicit re-probe. Same base-repo probe gate as
|
|
1894
1968
|
// pollPrStatus (cached per slug for the duration of the pass), then a single
|
|
@@ -1897,7 +1971,7 @@ async function checkLiveBuildAndConflict(pr, project) {
|
|
|
1897
1971
|
// `_reconciliation404Confirmed: true` so we don't re-probe deleted PRs on
|
|
1898
1972
|
// every future restart.
|
|
1899
1973
|
//
|
|
1900
|
-
// Caller: engine/abandoned-pr-reconciliation.js (version-gated).
|
|
1974
|
+
// Caller: engine/providers/abandoned-pr-reconciliation.js (version-gated).
|
|
1901
1975
|
async function reconcileAbandonedPrs(config) {
|
|
1902
1976
|
const projects = shared.getProjects(config).filter(isGitHub);
|
|
1903
1977
|
let flipped = 0, confirmedDeleted = 0, skipped = 0, errored = 0;
|
|
@@ -2010,6 +2084,9 @@ async function reconcileAbandonedPrs(config) {
|
|
|
2010
2084
|
delete pr._consecutive404s;
|
|
2011
2085
|
delete pr._404Count;
|
|
2012
2086
|
delete pr._404FirstAt;
|
|
2087
|
+
// W-msav5edk00g81710: the record left `abandoned`, so drop the
|
|
2088
|
+
// bounded-re-probe anchor with it.
|
|
2089
|
+
if (upd.newStatus !== PR_STATUS.ABANDONED) shared.clearPrAbandonedAt(pr);
|
|
2013
2090
|
pr._reconciledAt = reconciledAt;
|
|
2014
2091
|
pr._reconciledFrom = 'startup-pass';
|
|
2015
2092
|
} else if (upd.action === 'confirm404') {
|
|
@@ -2035,7 +2112,7 @@ module.exports = {
|
|
|
2035
2112
|
dismissPriorViewerChangesRequestedReviews, // W-mp7b1g8q000fea45 — clear stale CHANGES_REQUESTED on verdict flip
|
|
2036
2113
|
isGhThrottled,
|
|
2037
2114
|
getGhThrottleState,
|
|
2038
|
-
ghApi, // P-8c1b6e45 — used by engine/shared-branch-pr-reconcile.js to list open PRs on a feature branch
|
|
2115
|
+
ghApi, // P-8c1b6e45 — used by engine/providers/shared-branch-pr-reconcile.js to list open PRs on a feature branch
|
|
2039
2116
|
prExists, // issue #246 — confirm a loose ref points at a PR (not an issue) before stamping
|
|
2040
2117
|
getPrChangedFiles, // W-mr9jo2n2000667ad — stale-PR-pruner scope-overlap verification
|
|
2041
2118
|
// Exported for testing
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* engine/pr-action.js — Projectless PR action validator/handle + read-only
|
|
2
|
+
* engine/providers/pr-action.js — Projectless PR action validator/handle + read-only
|
|
3
3
|
* dispatch path.
|
|
4
4
|
*
|
|
5
5
|
* Plan: projectless-pr-actions, items P-ppa10002 (endpoint + handle) and
|
|
6
6
|
* P-ppa10003 (the read-only projectless dispatch path). Backs the
|
|
7
7
|
* user-initiated `POST /api/pr-action { url, action }` endpoint (dashboard.js).
|
|
8
8
|
* It validates the action against an explicit allowlist, resolves the PR
|
|
9
|
-
* reference via the P-ppa10001 resolver (engine/pr-resolve.js#normalizePrRef),
|
|
9
|
+
* reference via the P-ppa10001 resolver (engine/providers/pr-resolve.js#normalizePrRef),
|
|
10
10
|
* and mints a dispatch handle the caller (CC, dashboard) can observe.
|
|
11
11
|
*
|
|
12
12
|
* P-ppa10003 — the read-only dispatch path — lives in `runPrAction`:
|
|
13
13
|
* - fetches `{ title, body, diff, comments, author }` with NO configured
|
|
14
|
-
* project and NO clone (engine/pr-resolve.js#fetchPrPayload),
|
|
14
|
+
* project and NO clone (engine/providers/pr-resolve.js#fetchPrPayload),
|
|
15
15
|
* - fences all external PR content (body / diff / comments) via
|
|
16
|
-
* engine/untrusted-fence.js#wrapUntrusted so it is treated as data,
|
|
17
|
-
* - runs a read-only DIRECT LLM call (engine/llm.js#callLLM `{ direct:true }`)
|
|
16
|
+
* engine/core/untrusted-fence.js#wrapUntrusted so it is treated as data,
|
|
17
|
+
* - runs a read-only DIRECT LLM call (engine/agents/llm.js#callLLM `{ direct:true }`)
|
|
18
18
|
* — no worktree, no repo cloning, no engine.spawnAgent — and
|
|
19
19
|
* - returns a lightweight dispatch record (status / output / failure_class)
|
|
20
20
|
* for dashboard observability, mapping a reported injection attempt to a
|
|
@@ -29,15 +29,15 @@
|
|
|
29
29
|
* (P-ppa10005+) and intentionally NOT reachable from here.
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
|
-
const shared = require('
|
|
32
|
+
const shared = require('../core/shared');
|
|
33
33
|
const prResolve = require('./pr-resolve');
|
|
34
34
|
const ghComment = require('./gh-comment');
|
|
35
|
-
const adoToken = require('
|
|
36
|
-
const { wrapUntrusted, buildSource } = require('
|
|
35
|
+
const adoToken = require('../ado/token');
|
|
36
|
+
const { wrapUntrusted, buildSource } = require('../core/untrusted-fence');
|
|
37
37
|
|
|
38
38
|
// Marker attribution for a posted `comment` action. The drafted text is posted
|
|
39
39
|
// under the shared gh identity, so it MUST carry the minions marker (agentId +
|
|
40
|
-
// kind) so engine/github.js classifies it as minions-authored rather than a
|
|
40
|
+
// kind) so engine/providers/github.js classifies it as minions-authored rather than a
|
|
41
41
|
// human comment. gh-comment validates these against /^[a-z][a-z0-9-]{0,30}$/.
|
|
42
42
|
const PR_ACTION_COMMENT_AGENT_ID = 'minions';
|
|
43
43
|
const PR_ACTION_COMMENT_KIND = 'pr-action';
|
|
@@ -121,7 +121,7 @@ function buildPrActionFollowups(record, opts = {}) {
|
|
|
121
121
|
const messages = {
|
|
122
122
|
'comment': `Post a PR comment on ${target} — call POST /api/pr-action with {"url":"${target}","action":"comment","execute":true}; this drafts the comment AND posts it to the PR, then show me what was posted (and the comment URL if returned). To preview without posting, add "draftOnly":true.`,
|
|
123
123
|
'fix-once': `Fix ${target} once — call POST /api/pr-action/fix with {"url":"${target}"}. If it returns status "awaiting-target-choice", show me the execution-target options (clone-keep / temp-clone / remote-patch / devbox) and DO NOT clone until I pick one; if it returns "project-ready", say it will use the existing project fix path. Once I choose, call the same endpoint again with {"url":"${target}","target":"<choice>"}.`,
|
|
124
|
-
'track-auto-fix': `Track ${target} for auto-fix — call POST /api/pr-action/track with {"url":"${target}"}. If it returns status "awaiting-target-choice", show me the execution-target options (Clone & keep is recommended for ongoing tracking — temp clone is discarded after one push) and DO NOT clone until I pick; once "Clone & keep" registers the project, call POST /api/pr-action/track again to finish enrollment. If it returns status "enrolled", tell me the PR
|
|
124
|
+
'track-auto-fix': `Track ${target} for auto-fix — call POST /api/pr-action/track with {"url":"${target}"}. If it returns status "awaiting-target-choice", show me the execution-target options (Clone & keep is recommended for ongoing tracking — temp clone is discarded after one push) and DO NOT clone until I pick; once "Clone & keep" registers the project, call POST /api/pr-action/track again to finish enrollment. If it returns status "enrolled", tell me the PR now has Auto Fix and Auto Review enabled and the existing pollers → review/fix/re-review → auto-merge loop will track it automatically.`,
|
|
125
125
|
};
|
|
126
126
|
return PR_ACTION_FOLLOWUPS.map((f) => ({
|
|
127
127
|
kind: f.kind,
|
|
@@ -393,7 +393,7 @@ function buildPrActionPrompt(action, payload) {
|
|
|
393
393
|
/**
|
|
394
394
|
* POST a single PR thread comment to ADO. ADO has no `gh`-equivalent CLI for PR
|
|
395
395
|
* comments, so this hits the REST threads endpoint directly with a bearer token
|
|
396
|
-
* from engine/ado
|
|
396
|
+
* from engine/ado/token.js. The body is marker-prepended by the caller.
|
|
397
397
|
* `opts._adoPost` / `opts.adoToken` / `opts._acquireAdoToken` are test seams.
|
|
398
398
|
*/
|
|
399
399
|
async function _defaultAdoPost(url, token, payload) {
|
|
@@ -546,7 +546,7 @@ async function runPrAction(input = {}, opts = {}) {
|
|
|
546
546
|
}
|
|
547
547
|
|
|
548
548
|
const prompt = buildPrActionPrompt(act, payload);
|
|
549
|
-
const callLLM = opts.callLLM || (() => require('
|
|
549
|
+
const callLLM = opts.callLLM || (() => require('../agents/llm').callLLM)();
|
|
550
550
|
|
|
551
551
|
let res;
|
|
552
552
|
try {
|
|
@@ -555,6 +555,7 @@ async function runPrAction(input = {}, opts = {}) {
|
|
|
555
555
|
label: `pr-action:${act}`,
|
|
556
556
|
maxTurns: 1,
|
|
557
557
|
allowedTools: '', // read-only: no tools, only the fenced payload
|
|
558
|
+
disableTools: true,
|
|
558
559
|
timeout: opts.timeout || 180000,
|
|
559
560
|
engineConfig: opts.engineConfig,
|
|
560
561
|
});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* engine/pr-clone-keep.js — Clone & keep fix executor: auto-clone a PR's repo and
|
|
2
|
+
* engine/providers/pr-clone-keep.js — Clone & keep fix executor: auto-clone a PR's repo and
|
|
3
3
|
* register it as a PERSISTENT project so ongoing tracking / auto-fix works.
|
|
4
4
|
*
|
|
5
5
|
* Plan: projectless-pr-actions, Phase 2, item P-ppa10007. This is the second
|
|
6
6
|
* mutating executor in the chain (after the ephemeral temp-clone, P-ppa10006).
|
|
7
|
-
* The execution-target choice surface (engine/pr-fix-target.js) routes
|
|
7
|
+
* The execution-target choice surface (engine/providers/pr-fix-target.js) routes
|
|
8
8
|
* `clone-keep` here once the user has explicitly picked it — the "never clone
|
|
9
9
|
* silently" invariant lives upstream; by the time we run, the user has chosen to
|
|
10
10
|
* clone AND keep.
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* cloned.
|
|
21
21
|
* executeCloneKeep(plan, opts) — clone the BASE repo into the persistent dir
|
|
22
22
|
* and register it as a project via
|
|
23
|
-
* engine/projects.js#addProject. The repo then
|
|
23
|
+
* engine/planning/projects.js#addProject. The repo then
|
|
24
24
|
* becomes `project-ready` so the fix itself
|
|
25
25
|
* flows through the engine's normal project fix
|
|
26
26
|
* path. On registration failure the partial
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
const fs = require('fs');
|
|
52
52
|
const path = require('path');
|
|
53
53
|
|
|
54
|
-
const shared = require('
|
|
54
|
+
const shared = require('../core/shared');
|
|
55
55
|
const prResolve = require('./pr-resolve');
|
|
56
56
|
const ghToken = require('./gh-token');
|
|
57
57
|
const prFixTarget = require('./pr-fix-target');
|
|
@@ -107,7 +107,7 @@ function _baseCloneUrl(ref, branches) {
|
|
|
107
107
|
* the persistent project dir + name. NOTHING is created or cloned here.
|
|
108
108
|
*
|
|
109
109
|
* `input` — `{ url }`, a normalized ref, or a routed plan from
|
|
110
|
-
* engine/pr-fix-target.js#resolvePrFixTarget.
|
|
110
|
+
* engine/providers/pr-fix-target.js#resolvePrFixTarget.
|
|
111
111
|
* `opts.fetchPrBranches` — override the branch fetch (test seam).
|
|
112
112
|
* `opts.prUrl` — original user URL (preferred for surfacing).
|
|
113
113
|
* `opts.cloneRoot` — override the persistent-clone root.
|
|
@@ -158,7 +158,7 @@ async function _resolveToken(plan, opts = {}) {
|
|
|
158
158
|
return resolve(plan.ref.slug) || null;
|
|
159
159
|
}
|
|
160
160
|
if (plan.ref.host === 'ado') {
|
|
161
|
-
const acquire = opts._acquireAdoToken || require('
|
|
161
|
+
const acquire = opts._acquireAdoToken || require('../ado/token').acquireAdoToken;
|
|
162
162
|
const acquired = await acquire();
|
|
163
163
|
return (acquired && acquired.token) || null;
|
|
164
164
|
}
|
|
@@ -296,7 +296,7 @@ async function executeCloneKeep(planOrInput = {}, opts = {}) {
|
|
|
296
296
|
}
|
|
297
297
|
|
|
298
298
|
const gitClone = opts.gitClone || ((p, token) => _defaultGitClone(p, token, opts));
|
|
299
|
-
const addProject = opts.addProject || ((target, registerOptions) => require('
|
|
299
|
+
const addProject = opts.addProject || ((target, registerOptions) => require('../planning/projects').addProject(target, registerOptions));
|
|
300
300
|
const mkdir = opts._mkdir || ((dir) => fs.promises.mkdir(dir, { recursive: true }));
|
|
301
301
|
|
|
302
302
|
const rec = _record(plan, {});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* engine/pr-devbox.js — DevBox execution target (the "never touches your machine"
|
|
2
|
+
* engine/providers/pr-devbox.js — DevBox execution target (the "never touches your machine"
|
|
3
3
|
* option for a projectless one-off PR fix).
|
|
4
4
|
*
|
|
5
5
|
* Plan: projectless-pr-actions, Phase 4 (stretch), item P-ppa10011. Like
|
|
6
|
-
* engine/pr-temp-clone.js this is an EXECUTOR the choice surface
|
|
7
|
-
* (engine/pr-fix-target.js) routes to once the user has EXPLICITLY picked it —
|
|
6
|
+
* engine/providers/pr-temp-clone.js this is an EXECUTOR the choice surface
|
|
7
|
+
* (engine/providers/pr-fix-target.js) routes to once the user has EXPLICITLY picked it —
|
|
8
8
|
* the "never clone silently" invariant lives upstream; by the time we run, the
|
|
9
9
|
* user has chosen the DevBox target.
|
|
10
10
|
*
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
* success, failure, or noop — so nothing persists on the DevBox either.
|
|
40
40
|
* - EXPLICIT CONSENT ONLY: DevBox is never the default target and is never
|
|
41
41
|
* auto-selected; it only runs when the user picks `devbox` at the choice
|
|
42
|
-
* surface (engine/pr-fix-target.js).
|
|
42
|
+
* surface (engine/providers/pr-fix-target.js).
|
|
43
43
|
*/
|
|
44
44
|
|
|
45
|
-
const shared = require('
|
|
45
|
+
const shared = require('../core/shared');
|
|
46
46
|
const prResolve = require('./pr-resolve');
|
|
47
47
|
const { PrActionError } = require('./pr-action');
|
|
48
48
|
|
|
@@ -86,7 +86,7 @@ function _remoteJoin(root, name) {
|
|
|
86
86
|
* created on the local machine and NOTHING is cloned here.
|
|
87
87
|
*
|
|
88
88
|
* `input` — `{ url }`, a normalized ref, or a routed plan from
|
|
89
|
-
* engine/pr-fix-target.js#resolvePrFixTarget.
|
|
89
|
+
* engine/providers/pr-fix-target.js#resolvePrFixTarget.
|
|
90
90
|
* `opts.fetchPrBranches` — override the branch fetch (test seam).
|
|
91
91
|
* `opts.prUrl` — original user URL (preferred for surfacing).
|
|
92
92
|
* `opts.remoteTmpRoot` — override the DevBox-side ephemeral-clone root.
|