@yemi33/minions 0.1.2447 → 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 +66 -7
- package/dashboard/js/detail-panel.js +36 -0
- package/dashboard/js/memory-panel.js +59 -12
- package/dashboard/js/qa.js +186 -45
- 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 +52 -6
- 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,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* engine/pr-fix-target.js — Execution-target choice surface for projectless PR
|
|
2
|
+
* engine/providers/pr-fix-target.js — Execution-target choice surface for projectless PR
|
|
3
3
|
* fixes ("never clone silently").
|
|
4
4
|
*
|
|
5
5
|
* Plan: projectless-pr-actions, Phase 2, item P-ppa10005. This module is the
|
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
* unconfigured repo until the user explicitly picks an execution target. It does
|
|
8
8
|
* NOT execute anything — the executors land in:
|
|
9
9
|
* - temp-clone → P-ppa10006 (ephemeral clone, fix, push, discard) [DEFAULT]
|
|
10
|
-
* — IMPLEMENTED in engine/pr-temp-clone.js
|
|
10
|
+
* — IMPLEMENTED in engine/providers/pr-temp-clone.js
|
|
11
11
|
* - clone-keep → P-ppa10007 (auto-clone + persistent project registration)
|
|
12
|
-
* — IMPLEMENTED in engine/pr-clone-keep.js
|
|
12
|
+
* — IMPLEMENTED in engine/providers/pr-clone-keep.js
|
|
13
13
|
* - remote-patch → P-ppa10008 (GitHub Contents API, trivial single-file, NO test run)
|
|
14
|
-
* — IMPLEMENTED in engine/pr-remote-patch.js
|
|
14
|
+
* — IMPLEMENTED in engine/providers/pr-remote-patch.js
|
|
15
15
|
* - devbox → P-ppa10011 (ephemeral clone+fix+push ON A DEVBOX, zero local
|
|
16
|
-
* footprint) — IMPLEMENTED in engine/pr-devbox.js. Never default,
|
|
16
|
+
* footprint) — IMPLEMENTED in engine/providers/pr-devbox.js. Never default,
|
|
17
17
|
* never auto-selected; explicit consent only.
|
|
18
18
|
*
|
|
19
19
|
* Load-bearing invariant (read before extending): "never clone silently". A fix
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
* P-ppa10006/7/8 executors land).
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
|
-
const shared = require('
|
|
37
|
+
const shared = require('../core/shared');
|
|
38
38
|
const prResolve = require('./pr-resolve');
|
|
39
39
|
const { PrActionError } = require('./pr-action');
|
|
40
|
-
const prFixTargetStore = require('
|
|
40
|
+
const prFixTargetStore = require('../persistence/pr-fix-target-store');
|
|
41
41
|
|
|
42
42
|
// ── Execution targets ─────────────────────────────────────────────────────────
|
|
43
43
|
|
|
@@ -126,9 +126,9 @@ function _routedRecord(plan, target, extra = {}) {
|
|
|
126
126
|
module: desc.executorModule || null,
|
|
127
127
|
validates: desc.validates,
|
|
128
128
|
trivialOnly: desc.trivialOnly,
|
|
129
|
-
// temp-clone's executor landed in P-ppa10006 (engine/pr-temp-clone.js),
|
|
130
|
-
// clone-keep's in P-ppa10007 (engine/pr-clone-keep.js), and remote-patch's
|
|
131
|
-
// in P-ppa10008 (engine/pr-remote-patch.js). `implemented` reflects whether
|
|
129
|
+
// temp-clone's executor landed in P-ppa10006 (engine/providers/pr-temp-clone.js),
|
|
130
|
+
// clone-keep's in P-ppa10007 (engine/providers/pr-clone-keep.js), and remote-patch's
|
|
131
|
+
// in P-ppa10008 (engine/providers/pr-remote-patch.js). `implemented` reflects whether
|
|
132
132
|
// a real executor backs the choice.
|
|
133
133
|
implemented: !!desc.implemented,
|
|
134
134
|
},
|
|
@@ -181,7 +181,7 @@ function findConfiguredProjectForRef(ref, config) {
|
|
|
181
181
|
//
|
|
182
182
|
// P-ppa10009: optionally remember the user's execution-target pick keyed by repo
|
|
183
183
|
// scope so a repeat fix on the same repo doesn't re-prompt. Persisted SQL-first
|
|
184
|
-
// via engine/pr-fix-target-store.js. Absence of a remembered choice still
|
|
184
|
+
// via engine/persistence/pr-fix-target-store.js. Absence of a remembered choice still
|
|
185
185
|
// prompts — the safe "never clone silently" default is preserved.
|
|
186
186
|
|
|
187
187
|
/** The remembered execution-target id for a ref's repo scope, or null. */
|
|
@@ -338,8 +338,8 @@ function planPrFix(input = {}, opts = {}) {
|
|
|
338
338
|
*
|
|
339
339
|
* IMPORTANT: this still does NOT clone or spawn — selection only routes. The
|
|
340
340
|
* returned `routed` record carries `executor.implemented` (true once a real
|
|
341
|
-
* executor backs the choice: temp-clone → engine/pr-temp-clone.js; clone-keep →
|
|
342
|
-
* engine/pr-clone-keep.js; remote-patch → engine/pr-remote-patch.js). The caller is
|
|
341
|
+
* executor backs the choice: temp-clone → engine/providers/pr-temp-clone.js; clone-keep →
|
|
342
|
+
* engine/providers/pr-clone-keep.js; remote-patch → engine/providers/pr-remote-patch.js). The caller is
|
|
343
343
|
* responsible for invoking the executor; this surface never clones, keeping
|
|
344
344
|
* "never clone silently" absolute.
|
|
345
345
|
*
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* engine/pr-remote-patch.js — Remote patch fast path (the "no validation"
|
|
2
|
+
* engine/providers/pr-remote-patch.js — Remote patch fast path (the "no validation"
|
|
3
3
|
* execution target for a projectless one-off PR fix).
|
|
4
4
|
*
|
|
5
5
|
* Plan: projectless-pr-actions, Phase 2, item P-ppa10008. The execution-target
|
|
6
|
-
* choice surface (engine/pr-fix-target.js) routes `remote-patch` here once the
|
|
6
|
+
* choice surface (engine/providers/pr-fix-target.js) routes `remote-patch` here once the
|
|
7
7
|
* user has EXPLICITLY picked it (the "never clone silently" invariant lives
|
|
8
8
|
* upstream — by the time we run, the user has consented to a no-clone push).
|
|
9
9
|
*
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
* repo; the defaults are real (shellSafeGh + fetchPrBranches).
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
|
-
const shared = require('
|
|
41
|
+
const shared = require('../core/shared');
|
|
42
42
|
const prResolve = require('./pr-resolve');
|
|
43
43
|
const ghToken = require('./gh-token');
|
|
44
44
|
const { PrActionError } = require('./pr-action');
|
|
@@ -136,7 +136,7 @@ function _normalizePatch(patch) {
|
|
|
136
136
|
* branch, and normalize/validate the single-file patch. NOTHING is mutated here.
|
|
137
137
|
*
|
|
138
138
|
* `input` — `{ url }`, a normalized ref, or a routed plan from
|
|
139
|
-
* engine/pr-fix-target.js#resolvePrFixTarget.
|
|
139
|
+
* engine/providers/pr-fix-target.js#resolvePrFixTarget.
|
|
140
140
|
* `opts.patch` — REQUIRED single-file patch (see `_normalizePatch`).
|
|
141
141
|
* `opts.fetchPrBranches` — override the branch fetch (test seam).
|
|
142
142
|
* `opts.prUrl` — original user URL (preferred for surfacing).
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* engine/pr-resolve.js — Projectless PR reference resolver + diff/metadata fetcher.
|
|
2
|
+
* engine/providers/pr-resolve.js — Projectless PR reference resolver + diff/metadata fetcher.
|
|
3
3
|
*
|
|
4
4
|
* Plan: projectless-pr-actions, item P-ppa10001. Foundation for all projectless
|
|
5
5
|
* PR actions (review / summarize / comment / triage with no configured project
|
|
@@ -14,20 +14,20 @@
|
|
|
14
14
|
* for that PR WITHOUT a configured project or clone, via `gh` (GitHub) and the
|
|
15
15
|
* ADO REST API (`_apis/git/...`).
|
|
16
16
|
*
|
|
17
|
-
* Token routing is per-slug through engine/gh-token.js#resolveTokenForSlug; for an
|
|
17
|
+
* Token routing is per-slug through engine/providers/gh-token.js#resolveTokenForSlug; for an
|
|
18
18
|
* unknown GitHub slug (no mapping, no fleet default) it falls back across the
|
|
19
19
|
* authed accounts configured in `engine.ghAccounts` rather than relying on the
|
|
20
20
|
* active `gh auth` profile. It NEVER runs `gh auth switch`. ADO tokens come from
|
|
21
|
-
* engine/ado
|
|
21
|
+
* engine/ado/token.js (`az account get-access-token` / azureauth).
|
|
22
22
|
*
|
|
23
23
|
* All shell-outs use argv form (shell:false) and slug/number are validated before
|
|
24
24
|
* interpolation, so poisoned PR refs cannot inject shell metacharacters.
|
|
25
25
|
*/
|
|
26
26
|
|
|
27
27
|
const path = require('path');
|
|
28
|
-
const shared = require('
|
|
28
|
+
const shared = require('../core/shared');
|
|
29
29
|
const ghToken = require('./gh-token');
|
|
30
|
-
const adoToken = require('
|
|
30
|
+
const adoToken = require('../ado/token');
|
|
31
31
|
|
|
32
32
|
const { safeJson, MINIONS_DIR, log } = shared;
|
|
33
33
|
|
|
@@ -174,7 +174,7 @@ function normalizePrRef(input) {
|
|
|
174
174
|
* Shared by the 6 PR-action executor modules (pr-clone-keep, pr-devbox,
|
|
175
175
|
* pr-remote-patch, pr-temp-clone, pr-track, pr-fix-target), which previously
|
|
176
176
|
* each defined a byte-identical copy of this function. `PrActionError` must be
|
|
177
|
-
* passed in by the caller — pr-resolve.js does not import engine/pr-action.js
|
|
177
|
+
* passed in by the caller — pr-resolve.js does not import engine/providers/pr-action.js
|
|
178
178
|
* (that would introduce a new cross-module dependency in that direction).
|
|
179
179
|
*/
|
|
180
180
|
function resolvePrActionRef(input, PrActionError) {
|
|
@@ -412,7 +412,7 @@ function _stripRefsHeads(ref) {
|
|
|
412
412
|
|
|
413
413
|
/**
|
|
414
414
|
* Fetch the source/target branch names + a clone URL for a PR with NO configured
|
|
415
|
-
* project and NO local clone. The temp-clone fix executor (engine/pr-temp-clone.js)
|
|
415
|
+
* project and NO local clone. The temp-clone fix executor (engine/providers/pr-temp-clone.js)
|
|
416
416
|
* needs the PR's HEAD branch to clone + push the fix back to, plus the clone URL
|
|
417
417
|
* (fork-aware on GitHub). Token routing mirrors `fetchPrPayload` (per-slug GH
|
|
418
418
|
* tokens / ADO bearer); shell-outs are argv-form and validated.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* engine/pr-temp-clone.js — Temp-clone fix executor (the DEFAULT execution target
|
|
2
|
+
* engine/providers/pr-temp-clone.js — Temp-clone fix executor (the DEFAULT execution target
|
|
3
3
|
* for a projectless one-off PR fix).
|
|
4
4
|
*
|
|
5
5
|
* Plan: projectless-pr-actions, Phase 2, item P-ppa10006. This is the first
|
|
6
6
|
* executor in the chain that actually MUTATES code. The execution-target choice
|
|
7
|
-
* surface (engine/pr-fix-target.js) routes `temp-clone` here once the user has
|
|
7
|
+
* surface (engine/providers/pr-fix-target.js) routes `temp-clone` here once the user has
|
|
8
8
|
* explicitly picked it (the "never clone silently" invariant lives upstream — by
|
|
9
9
|
* the time we run, the user has chosen to clone).
|
|
10
10
|
*
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
const fs = require('fs');
|
|
41
41
|
const path = require('path');
|
|
42
42
|
|
|
43
|
-
const shared = require('
|
|
43
|
+
const shared = require('../core/shared');
|
|
44
44
|
const prResolve = require('./pr-resolve');
|
|
45
45
|
const ghToken = require('./gh-token');
|
|
46
46
|
const { PrActionError } = require('./pr-action');
|
|
@@ -78,7 +78,7 @@ function _tmpRoot(opts = {}) {
|
|
|
78
78
|
* and compute the ephemeral clone dir. NOTHING is created or cloned here.
|
|
79
79
|
*
|
|
80
80
|
* `input` — `{ url }`, a normalized ref, or a routed plan from
|
|
81
|
-
* engine/pr-fix-target.js#resolvePrFixTarget.
|
|
81
|
+
* engine/providers/pr-fix-target.js#resolvePrFixTarget.
|
|
82
82
|
* `opts.fetchPrBranches` — override the branch fetch (test seam).
|
|
83
83
|
* `opts.prUrl` — original user URL (preferred for surfacing).
|
|
84
84
|
* `opts.tmpRoot` — override the ephemeral-clone root.
|
|
@@ -129,7 +129,7 @@ async function _resolveToken(plan, opts = {}) {
|
|
|
129
129
|
return resolve(plan.sourceRepoSlug || plan.ref.slug) || null;
|
|
130
130
|
}
|
|
131
131
|
if (plan.ref.host === 'ado') {
|
|
132
|
-
const acquire = opts._acquireAdoToken || require('
|
|
132
|
+
const acquire = opts._acquireAdoToken || require('../ado/token').acquireAdoToken;
|
|
133
133
|
const acquired = await acquire();
|
|
134
134
|
return (acquired && acquired.token) || null;
|
|
135
135
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* engine/pr-track.js — Promote a one-off PR to TRACKED auto-fix (enrollment
|
|
2
|
+
* engine/providers/pr-track.js — Promote a one-off PR to TRACKED auto-fix (enrollment
|
|
3
3
|
* wiring).
|
|
4
4
|
*
|
|
5
5
|
* Plan: projectless-pr-actions, Phase 3, item P-ppa10010. The "Track for auto-fix"
|
|
@@ -8,14 +8,13 @@
|
|
|
8
8
|
* pollers → PR discovery → review / fix / re-review → auto-merge.
|
|
9
9
|
*
|
|
10
10
|
* There is NO new fix engine here — enrollment is "mostly wiring":
|
|
11
|
-
* 1.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* PR-discovery auto-managed gate flips on.
|
|
11
|
+
* 1. Enrolling with `contextOnly:false` enables both per-PR automation
|
|
12
|
+
* controls through the manual-link path, so the existing discovery gate
|
|
13
|
+
* admits review and fix dispatches.
|
|
15
14
|
* 2. If the PR's repo is NOT a configured project, ongoing auto-fix needs a
|
|
16
15
|
* PERSISTENT checkout, so promotion surfaces the SAME execution-target
|
|
17
|
-
* choice as a one-off fix (engine/pr-fix-target.js, P-ppa10005) but with
|
|
18
|
-
* "Clone & keep" (engine/pr-clone-keep.js, P-ppa10007 — the persistent
|
|
16
|
+
* choice as a one-off fix (engine/providers/pr-fix-target.js, P-ppa10005) but with
|
|
17
|
+
* "Clone & keep" (engine/providers/pr-clone-keep.js, P-ppa10007 — the persistent
|
|
19
18
|
* project executor) as the implied/default selection. NOTHING is cloned
|
|
20
19
|
* until the user picks.
|
|
21
20
|
* 3. Once the repo is project-ready (already configured, or after Clone & keep
|
|
@@ -43,7 +42,7 @@ const prFixTarget = require('./pr-fix-target');
|
|
|
43
42
|
const { PrActionError } = require('./pr-action');
|
|
44
43
|
|
|
45
44
|
// For ongoing auto-fix the checkout must persist across ticks, so "Clone & keep"
|
|
46
|
-
// (engine/pr-clone-keep.js, P-ppa10007) is the implied selection when promoting a
|
|
45
|
+
// (engine/providers/pr-clone-keep.js, P-ppa10007) is the implied selection when promoting a
|
|
47
46
|
// PR whose repo is not yet a configured project. temp-clone discards its clone
|
|
48
47
|
// after one push, so it can never back ongoing tracking.
|
|
49
48
|
const TRACK_RECOMMENDED_TARGET = 'clone-keep';
|
|
@@ -92,7 +91,7 @@ function buildPrTrackTargetPrompt(plan) {
|
|
|
92
91
|
* point — it does NOT write anything.
|
|
93
92
|
*
|
|
94
93
|
* Delegates configured-project detection + the choice surface to
|
|
95
|
-
* engine/pr-fix-target.js (so the "never clone silently" invariant + the
|
|
94
|
+
* engine/providers/pr-fix-target.js (so the "never clone silently" invariant + the
|
|
96
95
|
* configured-vs-unconfigured logic live in exactly one place), then re-flavors
|
|
97
96
|
* the unconfigured branch for ongoing tracking (Clone & keep recommended).
|
|
98
97
|
*
|
|
@@ -151,10 +150,9 @@ function planPrTrack(input = {}, opts = {}) {
|
|
|
151
150
|
}
|
|
152
151
|
|
|
153
152
|
/**
|
|
154
|
-
* Build the canonical enrollment signal for a PR (or plan/ref).
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
* feeds `{ url, contextOnly }` to the existing manual-link path
|
|
153
|
+
* Build the canonical enrollment signal for a PR (or plan/ref). The caller
|
|
154
|
+
* feeds `{ url, contextOnly:false }` to the existing manual-link path, which
|
|
155
|
+
* enables Auto Fix and Auto Review together
|
|
158
156
|
* (dashboard.js#linkPullRequestForTracking), which creates the record if missing
|
|
159
157
|
* and promotes an existing context-only record (upsert refuses to demote).
|
|
160
158
|
*
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
|
|
24
24
|
const fs = require('fs');
|
|
25
25
|
const path = require('path');
|
|
26
|
-
const shared = require('
|
|
27
|
-
const queries = require('
|
|
26
|
+
const shared = require('../core/shared');
|
|
27
|
+
const queries = require('../core/queries');
|
|
28
28
|
const github = require('./github');
|
|
29
|
-
const ado = require('
|
|
29
|
+
const ado = require('../ado');
|
|
30
30
|
|
|
31
31
|
const { log } = shared;
|
|
32
32
|
const PRD_DIR = queries.PRD_DIR;
|
|
@@ -143,7 +143,7 @@ async function _reconcilePrdForProject(plan, file, project, planIds) {
|
|
|
143
143
|
* the sweep.
|
|
144
144
|
*/
|
|
145
145
|
async function reconcileSharedBranchPrs(config) {
|
|
146
|
-
const planFiles = require('
|
|
146
|
+
const planFiles = require('../planning/prd-store').listPrdRows().filter(row => !row.archived);
|
|
147
147
|
|
|
148
148
|
for (const { filename: file, plan } of planFiles) {
|
|
149
149
|
try {
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* engine/qa/auto-prd-qa.js — automatic QA for successfully verified PRDs.
|
|
3
|
+
*
|
|
4
|
+
* W-msal3ser01q4ccb3. Opt-in via `engine.autoQaCompletedPrds` (default false).
|
|
5
|
+
* When ON, a PRD whose verification actually SUCCEEDED gets a PRD-driven QA
|
|
6
|
+
* Session automatically, through the exact same orchestration the manual
|
|
7
|
+
* `POST /api/qa/from-prd` bridge uses (engine/qa/prd-session.js).
|
|
8
|
+
*
|
|
9
|
+
* ── Why the trigger is verify-completion, not `status: completed` ───────────
|
|
10
|
+
* `checkPlanCompletion` flips the PRD to `completed` and only THEN creates the
|
|
11
|
+
* verify work item(s). Triggering on the status flip would therefore fire
|
|
12
|
+
* before verification has run and before the verify agent has written the
|
|
13
|
+
* canonical `prd/guides/verify-<plan>.md` — the guide the spec builder prefers
|
|
14
|
+
* as its flow source. So the trigger is: a verify work item completed, its
|
|
15
|
+
* guide exists on disk, and EVERY verify work item belonging to that plan is
|
|
16
|
+
* `done`.
|
|
17
|
+
*
|
|
18
|
+
* ── Idempotency ────────────────────────────────────────────────────────────
|
|
19
|
+
* The dedupe key is a "verify generation": a hash of the plan filename plus
|
|
20
|
+
* every verify WI's `id@completedAt`. It is durable (stamped on the PRD row in
|
|
21
|
+
* SQL via prd-store) so it survives ticks, engine restarts, lifecycle retries,
|
|
22
|
+
* and duplicate completion callbacks — an in-memory flag would not. A reopened
|
|
23
|
+
* PRD re-verified later produces DIFFERENT `completedAt` values, hence a new
|
|
24
|
+
* generation and a legitimately new QA session; the same generation always
|
|
25
|
+
* collapses to exactly one.
|
|
26
|
+
*
|
|
27
|
+
* The generation is CLAIMED inside the prd-store mutation before any session is
|
|
28
|
+
* created, and the claim is never released — a failed launch records its error
|
|
29
|
+
* and stops, so a broken QA path cannot become an unbounded auto-retry loop.
|
|
30
|
+
* PRD completion state is never touched: QA failure stays visible in QA state
|
|
31
|
+
* only.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
const fs = require('fs');
|
|
35
|
+
const path = require('path');
|
|
36
|
+
const crypto = require('crypto');
|
|
37
|
+
|
|
38
|
+
const shared = require('../core/shared');
|
|
39
|
+
const queries = require('../core/queries');
|
|
40
|
+
const prdStore = require('../planning/prd-store');
|
|
41
|
+
const qaFromPrd = require('./from-prd');
|
|
42
|
+
const qaPrdSession = require('./prd-session');
|
|
43
|
+
|
|
44
|
+
const { log, ts, WORK_TYPE, DONE_STATUSES } = shared;
|
|
45
|
+
|
|
46
|
+
// Bounded per-PRD launch history. Keeps the dedupe durable across the handful
|
|
47
|
+
// of generations a re-opened PRD can accumulate without growing the PRD row.
|
|
48
|
+
const AUTO_QA_LAUNCH_HISTORY = 5;
|
|
49
|
+
|
|
50
|
+
// Reasons returned when no session is launched. Stable strings so tests and
|
|
51
|
+
// callers can assert on them instead of on log text.
|
|
52
|
+
const SKIP_REASON = Object.freeze({
|
|
53
|
+
DISABLED: 'disabled',
|
|
54
|
+
NOT_VERIFY: 'not-verify-work-item',
|
|
55
|
+
NO_SOURCE_PLAN: 'no-source-plan',
|
|
56
|
+
UNSAFE_PLAN_FILE: 'unsafe-plan-filename',
|
|
57
|
+
PRD_NOT_FOUND: 'prd-not-found',
|
|
58
|
+
VERIFY_GUIDE_MISSING: 'verify-guide-missing',
|
|
59
|
+
NO_VERIFY_ITEMS: 'no-verify-work-items',
|
|
60
|
+
VERIFY_INCOMPLETE: 'verify-incomplete',
|
|
61
|
+
ALREADY_LAUNCHED: 'already-launched',
|
|
62
|
+
PRD_NOT_QA_READY: 'prd-not-qa-ready',
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
function _isNonEmptyString(v) {
|
|
66
|
+
return typeof v === 'string' && v.trim().length > 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function _planSlug(planFile) {
|
|
70
|
+
return String(planFile).replace(/\.json$/i, '');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function verifyGuidePath(planFile) {
|
|
74
|
+
return path.join(shared.MINIONS_DIR, 'prd', 'guides', `verify-${_planSlug(planFile)}.md`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Has the verify agent persisted the canonical guide for this plan? Presence,
|
|
79
|
+
* not content, is the gate — the spec builder does its own emptiness check and
|
|
80
|
+
* falls back through manual-qa-<plan>.md → acceptance criteria, preserving the
|
|
81
|
+
* documented flow priority.
|
|
82
|
+
*/
|
|
83
|
+
function hasVerifyGuide(planFile) {
|
|
84
|
+
try { return fs.statSync(verifyGuidePath(planFile)).isFile(); }
|
|
85
|
+
catch { return false; }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Every verify work item that belongs to `planFile`, across all project scopes
|
|
90
|
+
* and central. Mirrors the lookup in lifecycle.js#checkPlanCompletion.
|
|
91
|
+
*/
|
|
92
|
+
function collectVerifyItems(planFile, config) {
|
|
93
|
+
const all = queries.getWorkItems(config);
|
|
94
|
+
if (!Array.isArray(all)) return [];
|
|
95
|
+
return all.filter(w => w
|
|
96
|
+
&& w.sourcePlan === planFile
|
|
97
|
+
&& (w.itemType === WORK_TYPE.VERIFY || w.type === WORK_TYPE.VERIFY));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Cross-repo fan-in gate. A plan fans verification out to one verify WI per
|
|
102
|
+
* touched project; QA covers the plan as a whole, so it must wait for ALL of
|
|
103
|
+
* them and launch ONCE — not once per verify completion.
|
|
104
|
+
*
|
|
105
|
+
* Anything that is not a DONE status blocks: a pending/dispatched sibling means
|
|
106
|
+
* the generation is still forming, and a failed/cancelled sibling means the
|
|
107
|
+
* verification did not succeed, so no QA runs at all.
|
|
108
|
+
*/
|
|
109
|
+
function verifyItemsAllSucceeded(items) {
|
|
110
|
+
if (!Array.isArray(items) || items.length === 0) return false;
|
|
111
|
+
return items.every(w => w && DONE_STATUSES.has(w.status));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Stable identity for one completion generation of a plan's verification.
|
|
116
|
+
* `completedAt` is what makes a re-verified PRD a new generation:
|
|
117
|
+
* shared.reopenWorkItem deletes it, and the next successful completion stamps a
|
|
118
|
+
* fresh timestamp.
|
|
119
|
+
*/
|
|
120
|
+
function computeVerifyGeneration(planFile, items) {
|
|
121
|
+
const parts = items
|
|
122
|
+
.map(w => `${w.id || ''}@${w.completedAt || ''}`)
|
|
123
|
+
.sort();
|
|
124
|
+
return crypto.createHash('sha256')
|
|
125
|
+
.update(`${planFile}\n${parts.join('\n')}`)
|
|
126
|
+
.digest('hex')
|
|
127
|
+
.slice(0, 16);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function _readLaunches(prd) {
|
|
131
|
+
const autoQa = prd && prd._autoQa && typeof prd._autoQa === 'object' && !Array.isArray(prd._autoQa)
|
|
132
|
+
? prd._autoQa
|
|
133
|
+
: null;
|
|
134
|
+
return autoQa && Array.isArray(autoQa.launches) ? autoQa.launches : [];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Compare-and-set the generation claim under the prd-store transaction. Returns
|
|
139
|
+
* true only for the caller that actually inserted it, so concurrent verify
|
|
140
|
+
* completions (cross-repo fan-in landing in the same tick) and duplicate
|
|
141
|
+
* completion callbacks collapse to a single launch.
|
|
142
|
+
*/
|
|
143
|
+
function claimGeneration(planFile, generation) {
|
|
144
|
+
let claimed = false;
|
|
145
|
+
prdStore.mutatePrd(planFile, (data) => {
|
|
146
|
+
const launches = _readLaunches(data);
|
|
147
|
+
if (launches.some(l => l && l.generation === generation)) return data;
|
|
148
|
+
const next = launches.concat([{
|
|
149
|
+
generation,
|
|
150
|
+
status: 'claimed',
|
|
151
|
+
sessionId: null,
|
|
152
|
+
setupWorkItemId: null,
|
|
153
|
+
claimedAt: ts(),
|
|
154
|
+
}]);
|
|
155
|
+
data._autoQa = { launches: next.slice(-AUTO_QA_LAUNCH_HISTORY) };
|
|
156
|
+
claimed = true;
|
|
157
|
+
return data;
|
|
158
|
+
});
|
|
159
|
+
return claimed;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function recordLaunchOutcome(planFile, generation, patch) {
|
|
163
|
+
try {
|
|
164
|
+
prdStore.mutatePrd(planFile, (data) => {
|
|
165
|
+
const launches = _readLaunches(data);
|
|
166
|
+
const next = launches.map(l => (l && l.generation === generation ? { ...l, ...patch } : l));
|
|
167
|
+
data._autoQa = { launches: next.slice(-AUTO_QA_LAUNCH_HISTORY) };
|
|
168
|
+
return data;
|
|
169
|
+
});
|
|
170
|
+
} catch (e) {
|
|
171
|
+
log('warn', `auto-qa: recording launch outcome for ${planFile}/${generation} failed: ${e.message}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Post-verify hook. Called from lifecycle.js#runPostCompletionHooks after a
|
|
177
|
+
* work item completes successfully.
|
|
178
|
+
*
|
|
179
|
+
* Never throws: every failure degrades to `{ launched: false }` plus a warn, so
|
|
180
|
+
* an auto-QA problem can never regress the verify work item or the PRD.
|
|
181
|
+
*
|
|
182
|
+
* @param {object} item the completed work item (meta.item)
|
|
183
|
+
* @param {object} config live engine config
|
|
184
|
+
* @returns {{ launched: boolean, reason?: string, generation?: string, sessionId?: string,
|
|
185
|
+
* setupWorkItemId?: string|null, error?: string }}
|
|
186
|
+
*/
|
|
187
|
+
function maybeAutoQaAfterVerify(item, config) {
|
|
188
|
+
if (!shared.resolveAutoQaCompletedPrds(config && config.engine)) {
|
|
189
|
+
return { launched: false, reason: SKIP_REASON.DISABLED };
|
|
190
|
+
}
|
|
191
|
+
if (!item || (item.itemType !== WORK_TYPE.VERIFY && item.type !== WORK_TYPE.VERIFY)) {
|
|
192
|
+
return { launched: false, reason: SKIP_REASON.NOT_VERIFY };
|
|
193
|
+
}
|
|
194
|
+
const planFile = _isNonEmptyString(item.sourcePlan) ? item.sourcePlan.trim() : null;
|
|
195
|
+
if (!planFile) return { launched: false, reason: SKIP_REASON.NO_SOURCE_PLAN };
|
|
196
|
+
// The plan filename reaches path.join for the guide lookup and prd-store for
|
|
197
|
+
// the claim; reuse the builder's traversal guard rather than trusting it.
|
|
198
|
+
if (!qaFromPrd._isSafePrdFileName(planFile)) {
|
|
199
|
+
return { launched: false, reason: SKIP_REASON.UNSAFE_PLAN_FILE };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
let prd = null;
|
|
203
|
+
try { prd = prdStore.readPrd(planFile); }
|
|
204
|
+
catch (e) {
|
|
205
|
+
log('warn', `auto-qa: reading PRD ${planFile} failed: ${e.message}`);
|
|
206
|
+
return { launched: false, reason: SKIP_REASON.PRD_NOT_FOUND };
|
|
207
|
+
}
|
|
208
|
+
if (!prd || typeof prd !== 'object') {
|
|
209
|
+
return { launched: false, reason: SKIP_REASON.PRD_NOT_FOUND };
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Gate on the canonical verify guide, not on the PRD status flip: the guide
|
|
213
|
+
// is the artifact that proves verification actually ran to completion.
|
|
214
|
+
if (!hasVerifyGuide(planFile)) {
|
|
215
|
+
return { launched: false, reason: SKIP_REASON.VERIFY_GUIDE_MISSING };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
let verifyItems;
|
|
219
|
+
try { verifyItems = collectVerifyItems(planFile, config); }
|
|
220
|
+
catch (e) {
|
|
221
|
+
log('warn', `auto-qa: collecting verify work items for ${planFile} failed: ${e.message}`);
|
|
222
|
+
return { launched: false, reason: SKIP_REASON.NO_VERIFY_ITEMS };
|
|
223
|
+
}
|
|
224
|
+
if (verifyItems.length === 0) {
|
|
225
|
+
return { launched: false, reason: SKIP_REASON.NO_VERIFY_ITEMS };
|
|
226
|
+
}
|
|
227
|
+
if (!verifyItemsAllSucceeded(verifyItems)) {
|
|
228
|
+
return { launched: false, reason: SKIP_REASON.VERIFY_INCOMPLETE };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const generation = computeVerifyGeneration(planFile, verifyItems);
|
|
232
|
+
|
|
233
|
+
// Cheap pre-check outside the claim so the steady-state re-scan does no write.
|
|
234
|
+
if (_readLaunches(prd).some(l => l && l.generation === generation)) {
|
|
235
|
+
return { launched: false, reason: SKIP_REASON.ALREADY_LAUNCHED, generation };
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
let claimed = false;
|
|
239
|
+
try { claimed = claimGeneration(planFile, generation); }
|
|
240
|
+
catch (e) {
|
|
241
|
+
log('warn', `auto-qa: claiming generation ${generation} for ${planFile} failed: ${e.message}`);
|
|
242
|
+
return { launched: false, reason: SKIP_REASON.ALREADY_LAUNCHED, generation, error: e.message };
|
|
243
|
+
}
|
|
244
|
+
if (!claimed) {
|
|
245
|
+
return { launched: false, reason: SKIP_REASON.ALREADY_LAUNCHED, generation };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
const created = qaPrdSession.createQaSessionFromPrd(planFile, {
|
|
250
|
+
// `auto` runs SETUP → DRAFT → EXECUTE without waiting for approval; an
|
|
251
|
+
// automatic session has no operator standing by to approve the draft.
|
|
252
|
+
mode: 'auto',
|
|
253
|
+
createdBy: 'engine:auto-qa-verified-prd',
|
|
254
|
+
provenanceExtra: {
|
|
255
|
+
autoTrigger: {
|
|
256
|
+
reason: 'verified-prd',
|
|
257
|
+
generation,
|
|
258
|
+
verifyWorkItemIds: verifyItems.map(w => w.id).filter(Boolean).sort(),
|
|
259
|
+
triggeredBy: item.id || null,
|
|
260
|
+
at: ts(),
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
resolveTargets: (spec) => qaPrdSession.resolveTargetsFromConfig(spec, config),
|
|
264
|
+
});
|
|
265
|
+
recordLaunchOutcome(planFile, generation, {
|
|
266
|
+
status: 'launched',
|
|
267
|
+
sessionId: created.sessionId,
|
|
268
|
+
setupWorkItemId: created.setupWorkItemId || null,
|
|
269
|
+
launchedAt: ts(),
|
|
270
|
+
});
|
|
271
|
+
log('info', `auto-qa: launched QA session ${created.sessionId} for verified PRD ${planFile} `
|
|
272
|
+
+ `(generation ${generation}, verify WIs: ${verifyItems.map(w => w.id).join(', ')})`);
|
|
273
|
+
return {
|
|
274
|
+
launched: true,
|
|
275
|
+
generation,
|
|
276
|
+
sessionId: created.sessionId,
|
|
277
|
+
setupWorkItemId: created.setupWorkItemId || null,
|
|
278
|
+
};
|
|
279
|
+
} catch (e) {
|
|
280
|
+
// The claim is intentionally NOT released: recording the error and stopping
|
|
281
|
+
// is what keeps a broken QA path from re-firing every tick. QA failure is
|
|
282
|
+
// visible in QA/PRD auto-qa state and never regresses PRD completion.
|
|
283
|
+
const reason = e && e.reason ? e.reason : e.message;
|
|
284
|
+
const isNotReady = qaFromPrd.isQaFromPrdError(e) && /not QA-ready/i.test(String(reason));
|
|
285
|
+
recordLaunchOutcome(planFile, generation, {
|
|
286
|
+
status: 'error',
|
|
287
|
+
error: String(reason).slice(0, 500),
|
|
288
|
+
sessionId: (e && e.sessionId) || null,
|
|
289
|
+
erroredAt: ts(),
|
|
290
|
+
});
|
|
291
|
+
log('warn', `auto-qa: QA session for verified PRD ${planFile} not created: ${reason}`);
|
|
292
|
+
return {
|
|
293
|
+
launched: false,
|
|
294
|
+
reason: isNotReady ? SKIP_REASON.PRD_NOT_QA_READY : 'launch-failed',
|
|
295
|
+
generation,
|
|
296
|
+
error: String(reason),
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
module.exports = {
|
|
302
|
+
maybeAutoQaAfterVerify,
|
|
303
|
+
// Exported for tests / callers that need the individual gates.
|
|
304
|
+
verifyGuidePath,
|
|
305
|
+
hasVerifyGuide,
|
|
306
|
+
collectVerifyItems,
|
|
307
|
+
verifyItemsAllSucceeded,
|
|
308
|
+
computeVerifyGeneration,
|
|
309
|
+
claimGeneration,
|
|
310
|
+
recordLaunchOutcome,
|
|
311
|
+
SKIP_REASON,
|
|
312
|
+
AUTO_QA_LAUNCH_HISTORY,
|
|
313
|
+
};
|