@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/queries.js — Shared read-only state queries for Minions engine + dashboard.
|
|
2
|
+
* engine/core/queries.js — Shared read-only state queries for Minions engine + dashboard.
|
|
3
3
|
* Single source of truth for all data reading/aggregation.
|
|
4
4
|
* Both engine.js and dashboard.js require() this module.
|
|
5
5
|
*/
|
|
@@ -9,7 +9,7 @@ const fsp = require('fs').promises;
|
|
|
9
9
|
const path = require('path');
|
|
10
10
|
const os = require('os');
|
|
11
11
|
const shared = require('./shared');
|
|
12
|
-
const steering = require('
|
|
12
|
+
const steering = require('../agents/steering');
|
|
13
13
|
const { normalizeExecutionModel, resolveWorkItemModel } = require('./execution-model');
|
|
14
14
|
|
|
15
15
|
const { safeRead, safeReadDir, safeJson, safeJsonObj, safeWrite, getProjects, mutateJsonFileLocked,
|
|
@@ -32,7 +32,7 @@ const _prEnrichmentInFlight = new Set();
|
|
|
32
32
|
let _lifecycleModule = null;
|
|
33
33
|
function _lifecycle() {
|
|
34
34
|
if (!_lifecycleModule) {
|
|
35
|
-
try { _lifecycleModule = require('
|
|
35
|
+
try { _lifecycleModule = require('../orchestration/lifecycle'); }
|
|
36
36
|
catch { _lifecycleModule = null; }
|
|
37
37
|
}
|
|
38
38
|
return _lifecycleModule;
|
|
@@ -248,7 +248,7 @@ function getDispatch() {
|
|
|
248
248
|
const now = Date.now();
|
|
249
249
|
if (_dispatchCache && (now - _dispatchCacheAt) < 2000) return _dispatchCache;
|
|
250
250
|
// SQL is the canonical (and only) dispatch store after Phase 9.
|
|
251
|
-
const store = require('
|
|
251
|
+
const store = require('../persistence/dispatch-store');
|
|
252
252
|
_dispatchCache = store.readDispatchSectioned();
|
|
253
253
|
_dispatchCacheAt = now;
|
|
254
254
|
return _dispatchCache;
|
|
@@ -388,7 +388,7 @@ function getDispatchQueue() {
|
|
|
388
388
|
const d = getDispatch();
|
|
389
389
|
const allCompleted = d.completed || [];
|
|
390
390
|
// Lifetime total from metrics (dispatch.completed is capped at 100)
|
|
391
|
-
const metrics = require('
|
|
391
|
+
const metrics = require('../observability/metrics-store').readMetrics() || {};
|
|
392
392
|
// Periodically prune cache entries for dispatches that have rotated out of the queue.
|
|
393
393
|
if (_completionReportCache.size >= 200) {
|
|
394
394
|
const activeIds = new Set();
|
|
@@ -420,18 +420,18 @@ function getNotesWithMeta() {
|
|
|
420
420
|
|
|
421
421
|
function getEngineLog() {
|
|
422
422
|
// SQL is the canonical (and only) log store after Phase 9.
|
|
423
|
-
const store = require('
|
|
423
|
+
const store = require('../observability/logs-store');
|
|
424
424
|
return store.readRecentLogsChronological(50) || [];
|
|
425
425
|
}
|
|
426
426
|
|
|
427
427
|
function getMetrics() {
|
|
428
428
|
// SQL is the canonical (and only) metrics store after Phase 9.
|
|
429
|
-
const store = require('
|
|
429
|
+
const store = require('../observability/metrics-store');
|
|
430
430
|
const metrics = store.readMetrics() || {};
|
|
431
431
|
const config = getConfig();
|
|
432
432
|
const engineConfig = config.engine || {};
|
|
433
433
|
const configuredAgents = config.agents || {};
|
|
434
|
-
const { resolveRuntime } = require('
|
|
434
|
+
const { resolveRuntime } = require('../runtimes');
|
|
435
435
|
|
|
436
436
|
for (const [agentId, m] of Object.entries(metrics)) {
|
|
437
437
|
if (agentId.startsWith('_')) continue;
|
|
@@ -448,7 +448,7 @@ function getMetrics() {
|
|
|
448
448
|
const prRejectedByAgent = {};
|
|
449
449
|
for (const pr of allPrs) {
|
|
450
450
|
const agent = (pr.agent || '').toLowerCase();
|
|
451
|
-
if (!agent || agent.startsWith('temp-')) continue;
|
|
451
|
+
if (!agent || agent === shared.PR_AGENT_HUMAN || agent.startsWith('temp-')) continue;
|
|
452
452
|
prCountByAgent[agent] = (prCountByAgent[agent] || 0) + 1;
|
|
453
453
|
if (pr.reviewStatus === shared.REVIEW_STATUS.APPROVED || pr.status === shared.PR_STATUS.MERGED) prApprovedByAgent[agent] = (prApprovedByAgent[agent] || 0) + 1;
|
|
454
454
|
// BUG-H13 / P-h13-rejected: this used to compare to the literal 'rejected',
|
|
@@ -519,7 +519,7 @@ function getInboxFiles() {
|
|
|
519
519
|
// P-34fa5d79 — Note ↔ Work Item ↔ KB Entry linkage.
|
|
520
520
|
//
|
|
521
521
|
// Parse the YAML frontmatter from a note body and return the parsed fields.
|
|
522
|
-
// Same minimal contract as engine/kb-sweep.js#_parseFrontmatter — a single
|
|
522
|
+
// Same minimal contract as engine/memory/kb-sweep.js#_parseFrontmatter — a single
|
|
523
523
|
// shared regex would have been nicer but importing kb-sweep from queries
|
|
524
524
|
// would form a circular dep with lifecycle (queries → kb-sweep → … →
|
|
525
525
|
// queries), so we keep a local copy of the four-line parser.
|
|
@@ -737,7 +737,7 @@ function resolveRuntimeModelDisplay({ runtime, configuredModel, agentId, dispatc
|
|
|
737
737
|
* model). An active dispatch is the live run and always beats a completed one;
|
|
738
738
|
* among entries of the same kind the most recently started wins.
|
|
739
739
|
*
|
|
740
|
-
* Note `dispatch.completed` is capped (engine/dispatch.js), so an old work item
|
|
740
|
+
* Note `dispatch.completed` is capped (engine/orchestration/dispatch.js), so an old work item
|
|
741
741
|
* can legitimately have no entry here — callers must handle that.
|
|
742
742
|
*/
|
|
743
743
|
function buildWorkItemDispatchIndex(dispatch) {
|
|
@@ -773,7 +773,7 @@ function resolveConfiguredAgentModel(config, agentId, cache) {
|
|
|
773
773
|
const configured = shared.resolveAgentModel(agentConfig, engine) || null;
|
|
774
774
|
let model = configured;
|
|
775
775
|
try {
|
|
776
|
-
const runtime = require('
|
|
776
|
+
const runtime = require('../runtimes').resolveRuntime(runtimeName);
|
|
777
777
|
if (typeof runtime?.resolveModel === 'function') model = runtime.resolveModel(configured) || configured;
|
|
778
778
|
} catch { /* registry unavailable — fall back to the raw configured value */ }
|
|
779
779
|
const resolved = { runtimeName, model };
|
|
@@ -1017,7 +1017,7 @@ function getAgentDetail(id) {
|
|
|
1017
1017
|
|
|
1018
1018
|
function getPrs(project) {
|
|
1019
1019
|
if (project) {
|
|
1020
|
-
const store = require('
|
|
1020
|
+
const store = require('../persistence/pull-requests-store');
|
|
1021
1021
|
const prs = store.readPullRequestsForScope(project.name) || [];
|
|
1022
1022
|
shared.normalizePrRecords(prs, project);
|
|
1023
1023
|
return prs;
|
|
@@ -1137,7 +1137,7 @@ function _pickPrDedupeWinner(group, projects) {
|
|
|
1137
1137
|
// W-mr3mvgfe — terminal PR statuses (done being polled/reviewed) get pushed
|
|
1138
1138
|
// to the bottom of the PR tab's default sort. Includes `CLOSED` (a PR closed
|
|
1139
1139
|
// without merging, distinct from `ABANDONED` but equally inactive — see
|
|
1140
|
-
// engine/github.js `newStatus = PR_STATUS.CLOSED` on `prData.state === 'closed'`
|
|
1140
|
+
// engine/providers/github.js `newStatus = PR_STATUS.CLOSED` on `prData.state === 'closed'`
|
|
1141
1141
|
// without `merged`) alongside `MERGED`/`ABANDONED`.
|
|
1142
1142
|
function _isTerminalPrStatus(status) {
|
|
1143
1143
|
return status === PR_STATUS.MERGED || status === PR_STATUS.ABANDONED || status === PR_STATUS.CLOSED;
|
|
@@ -1152,7 +1152,7 @@ function getPullRequests(config) {
|
|
|
1152
1152
|
const allPrs = [];
|
|
1153
1153
|
|
|
1154
1154
|
// SQL is the canonical (and only) PR store after Phase 9.
|
|
1155
|
-
const store = require('
|
|
1155
|
+
const store = require('../persistence/pull-requests-store');
|
|
1156
1156
|
const sqlPrs = store.readAllPullRequests() || [];
|
|
1157
1157
|
|
|
1158
1158
|
// W-mqba5ulq000nd255 — Read-side de-dupe across project scopes. When the
|
|
@@ -1361,6 +1361,92 @@ let _getSkillsResultCacheTs = 0;
|
|
|
1361
1361
|
let _getSkillsResultCacheKey = null;
|
|
1362
1362
|
const SKILLS_CACHE_TTL = 30000; // 30s — skill files change rarely (agent extraction, manual authoring)
|
|
1363
1363
|
|
|
1364
|
+
// ── Nested project-embedded skill discovery (W-ms3a68j2) ────────────────────
|
|
1365
|
+
// A configured project can be a massive monorepo (e.g. `src` = C:/office/src),
|
|
1366
|
+
// and project-embedded skills can live in deeply nested `.claude/skills` /
|
|
1367
|
+
// `.github/skills` / `.agents/skills` dirs far below the repo root (observed:
|
|
1368
|
+
// `loop/app/android/android/.claude/skills/*`). The repo-root-only project skill
|
|
1369
|
+
// roots exposed by the runtime adapters miss these. `collectSkillFiles` does a
|
|
1370
|
+
// single bounded walk per project to find every nested skill root. The defensive
|
|
1371
|
+
// caps mirror engine/agents/harness-context.js's "Defensive caps" pattern so the walk
|
|
1372
|
+
// can never balloon or wander into node_modules/.git/build output.
|
|
1373
|
+
const SKILL_WALK_MAX_DEPTH = 12; // directory levels below the project root before we stop descending
|
|
1374
|
+
const SKILL_WALK_MAX_ENTRIES = 40000; // directory dirents inspected across one project walk (files are free)
|
|
1375
|
+
|
|
1376
|
+
// Test seam: allow lowering the directory-dirent budget so the entry cap can be
|
|
1377
|
+
// exercised without materializing 40k directories. Falls back to the real cap.
|
|
1378
|
+
function _skillWalkMaxEntries() {
|
|
1379
|
+
const override = Number(process.env.MINIONS_SKILL_WALK_MAX_ENTRIES);
|
|
1380
|
+
return Number.isFinite(override) && override > 0 ? override : SKILL_WALK_MAX_ENTRIES;
|
|
1381
|
+
}
|
|
1382
|
+
const SKILL_WALK_PRUNE_DIRS = new Set([
|
|
1383
|
+
'.git', 'node_modules', 'bin', 'obj', 'build', 'dist', 'out', 'output',
|
|
1384
|
+
'target', '.gradle', '.venv', 'venv', 'vendor', '.next', '.nuxt', 'coverage',
|
|
1385
|
+
'.idea', '.vs', '.cache', '__pycache__', '.terraform', 'Pods', 'DerivedData',
|
|
1386
|
+
]);
|
|
1387
|
+
|
|
1388
|
+
// Sort skill dirs shallowest-first so repo-root skills keep priority over deeper
|
|
1389
|
+
// ones during the per-project name dedup.
|
|
1390
|
+
function _sortSkillDirsShallowFirst(dirs) {
|
|
1391
|
+
return dirs.sort((a, b) => {
|
|
1392
|
+
const da = a.split(/[\\/]/).length;
|
|
1393
|
+
const db = b.split(/[\\/]/).length;
|
|
1394
|
+
return da - db || a.localeCompare(b);
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
// Bounded recursive walk of a project checkout that returns every nested
|
|
1399
|
+
// skill-root directory (absolute path) of the form `**/<marker>/skills`, where
|
|
1400
|
+
// `<marker>` is one of `markerNames` (e.g. `.claude`, `.github`, `.agents`).
|
|
1401
|
+
// Marker names are derived from the runtime adapters' own project skill roots so
|
|
1402
|
+
// discovery stays runtime-agnostic (no hard-coded runtime name checks). The walk
|
|
1403
|
+
// is bounded by depth, total dirents, and a heavy-dir prune set. Marker dirs are
|
|
1404
|
+
// never descended into, so we never surface skills nested inside build output.
|
|
1405
|
+
function _findNestedProjectSkillDirs(projectRoot, markerNames) {
|
|
1406
|
+
const found = [];
|
|
1407
|
+
if (!projectRoot || !markerNames || markerNames.size === 0) return found;
|
|
1408
|
+
const seenDirs = new Set();
|
|
1409
|
+
const maxEntries = _skillWalkMaxEntries();
|
|
1410
|
+
let dirsScanned = 0;
|
|
1411
|
+
// Breadth-first drain (FIFO via an index cursor) so the directory budget is
|
|
1412
|
+
// spent shallowest-first — matching _sortSkillDirsShallowFirst's declared
|
|
1413
|
+
// priority. A depth-first walk would let a single fat subtree exhaust the
|
|
1414
|
+
// budget before sibling subtrees are ever visited, so nested discovery would
|
|
1415
|
+
// silently no-op depending on directory-name ordering (e.g. a monorepo whose
|
|
1416
|
+
// skills sort after a 40k-file tree). Only *directory* dirents count toward
|
|
1417
|
+
// SKILL_WALK_MAX_ENTRIES — a file can never be a skill root, so files are free
|
|
1418
|
+
// and don't starve the walk.
|
|
1419
|
+
const queue = [{ dir: projectRoot, depth: 0 }];
|
|
1420
|
+
for (let head = 0; head < queue.length; head++) {
|
|
1421
|
+
const { dir, depth } = queue[head];
|
|
1422
|
+
let entries;
|
|
1423
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { continue; }
|
|
1424
|
+
for (const dirent of entries) {
|
|
1425
|
+
let isDir = false;
|
|
1426
|
+
try { isDir = dirent.isDirectory(); } catch { isDir = false; }
|
|
1427
|
+
if (!isDir) continue;
|
|
1428
|
+
if (dirsScanned >= maxEntries) return _sortSkillDirsShallowFirst(found);
|
|
1429
|
+
dirsScanned++;
|
|
1430
|
+
const name = dirent.name;
|
|
1431
|
+
const childPath = path.join(dir, name);
|
|
1432
|
+
if (markerNames.has(name)) {
|
|
1433
|
+
// `<marker>/skills` — record if present; never descend into the marker dir.
|
|
1434
|
+
const skillsDir = path.join(childPath, 'skills');
|
|
1435
|
+
let st = null;
|
|
1436
|
+
try { st = fs.statSync(skillsDir); } catch { st = null; }
|
|
1437
|
+
if (st && st.isDirectory()) {
|
|
1438
|
+
const abs = path.resolve(skillsDir);
|
|
1439
|
+
if (!seenDirs.has(abs)) { seenDirs.add(abs); found.push(abs); }
|
|
1440
|
+
}
|
|
1441
|
+
continue;
|
|
1442
|
+
}
|
|
1443
|
+
if (SKILL_WALK_PRUNE_DIRS.has(name)) continue;
|
|
1444
|
+
if (depth + 1 <= SKILL_WALK_MAX_DEPTH) queue.push({ dir: childPath, depth: depth + 1 });
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
return _sortSkillDirsShallowFirst(found);
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1364
1450
|
function invalidateSkillsCache() {
|
|
1365
1451
|
_skillsCache = null;
|
|
1366
1452
|
_skillsCacheTs = 0;
|
|
@@ -1422,7 +1508,7 @@ function _collectHarnessesScope(scopeFilter, runtimeOpts) {
|
|
|
1422
1508
|
const mcpsByPath = new Map();
|
|
1423
1509
|
let runtimeNames = [];
|
|
1424
1510
|
try {
|
|
1425
|
-
const { listRuntimes, resolveRuntime } = require('
|
|
1511
|
+
const { listRuntimes, resolveRuntime } = require('../runtimes');
|
|
1426
1512
|
runtimeNames = listRuntimes();
|
|
1427
1513
|
for (const runtimeName of runtimeNames) {
|
|
1428
1514
|
let runtime;
|
|
@@ -1511,7 +1597,7 @@ function collectSkillFiles(config) {
|
|
|
1511
1597
|
return seenByScope.get(key);
|
|
1512
1598
|
}
|
|
1513
1599
|
try {
|
|
1514
|
-
const { listRuntimes, resolveRuntime } = require('
|
|
1600
|
+
const { listRuntimes, resolveRuntime } = require('../runtimes');
|
|
1515
1601
|
for (const runtimeName of listRuntimes()) {
|
|
1516
1602
|
const runtime = resolveRuntime(runtimeName);
|
|
1517
1603
|
if (typeof runtime.getSkillRoots !== 'function') continue;
|
|
@@ -1525,15 +1611,42 @@ function collectSkillFiles(config) {
|
|
|
1525
1611
|
|
|
1526
1612
|
// Project-scope skill roots: aggregate across runtimes via the shared
|
|
1527
1613
|
// getProjectHarnesses helper so dedup-by-absolute-path happens once
|
|
1528
|
-
// (e.g. `<repo>/.agents/skills` is exposed by both Codex and Copilot).
|
|
1614
|
+
// (e.g. `<repo>/.agents/skills` is exposed by both Codex and Copilot). In
|
|
1615
|
+
// addition to the repo-root roots the adapters expose, walk each checkout for
|
|
1616
|
+
// nested project-embedded skill dirs (`**/.claude/skills`, `**/.github/skills`,
|
|
1617
|
+
// `**/.agents/skills`) so deeply-nested skills (e.g.
|
|
1618
|
+
// `loop/app/android/android/.claude/skills/*` in a monorepo) are discovered too.
|
|
1529
1619
|
for (const project of projects) {
|
|
1530
1620
|
if (!project.localPath) continue;
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1621
|
+
const projectSkillRoots = getProjectHarnesses(project).skills.filter(r => r.scope === 'project');
|
|
1622
|
+
if (projectSkillRoots.length === 0) continue;
|
|
1623
|
+
const projectName = projectSkillRoots[0].projectName;
|
|
1624
|
+
const dedup = seenFor('project', projectName);
|
|
1625
|
+
|
|
1626
|
+
// Marker dir names (`.claude` / `.github` / `.agents`) come from the adapter
|
|
1627
|
+
// roots themselves, keeping nested discovery runtime-agnostic. Track the
|
|
1628
|
+
// repo-root skill dirs so the nested walk doesn't re-enumerate them.
|
|
1629
|
+
const markerNames = new Set();
|
|
1630
|
+
const rootLevelDirs = new Set();
|
|
1631
|
+
for (const root of projectSkillRoots) {
|
|
1632
|
+
rootLevelDirs.add(path.resolve(root.dir));
|
|
1633
|
+
const rel = path.relative(project.localPath, root.dir);
|
|
1634
|
+
const firstSeg = rel.split(/[\\/]/)[0];
|
|
1635
|
+
if (firstSeg && !firstSeg.startsWith('..')) markerNames.add(firstSeg);
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
// 1. Repo-root skill roots exposed by the adapters (`<repo>/.claude/skills`).
|
|
1639
|
+
for (const root of projectSkillRoots) {
|
|
1640
|
+
_collectNativeSkillsDir(root.dir, root.scope, dedup, skillFiles, {
|
|
1534
1641
|
projectName: root.projectName,
|
|
1535
1642
|
});
|
|
1536
1643
|
}
|
|
1644
|
+
|
|
1645
|
+
// 2. Nested project-embedded skill roots anywhere under the checkout.
|
|
1646
|
+
for (const nestedDir of _findNestedProjectSkillDirs(project.localPath, markerNames)) {
|
|
1647
|
+
if (rootLevelDirs.has(path.resolve(nestedDir))) continue; // already enumerated above
|
|
1648
|
+
_collectNativeSkillsDir(nestedDir, 'project', dedup, skillFiles, { projectName });
|
|
1649
|
+
}
|
|
1537
1650
|
}
|
|
1538
1651
|
|
|
1539
1652
|
// 1b. Installed plugin skills: ~/.claude/plugins/installed_plugins.json
|
|
@@ -1717,7 +1830,7 @@ function collectCommandFiles(config) {
|
|
|
1717
1830
|
// `~/.claude/commands`; Copilot exposes `~/.copilot/commands` and Codex
|
|
1718
1831
|
// currently returns nothing — adapters fall in/out as their CLIs evolve).
|
|
1719
1832
|
try {
|
|
1720
|
-
const { listRuntimes, resolveRuntime } = require('
|
|
1833
|
+
const { listRuntimes, resolveRuntime } = require('../runtimes');
|
|
1721
1834
|
for (const runtimeName of listRuntimes()) {
|
|
1722
1835
|
const runtime = resolveRuntime(runtimeName);
|
|
1723
1836
|
if (typeof runtime.getCommandRoots !== 'function') continue;
|
|
@@ -1993,7 +2106,7 @@ function invalidateWorkItemsCache() {
|
|
|
1993
2106
|
_workItemsLeanCacheAt = 0;
|
|
1994
2107
|
}
|
|
1995
2108
|
|
|
1996
|
-
// getWorkItems(config, { enrich })
|
|
2109
|
+
// getWorkItems(config, { enrich, onlyId })
|
|
1997
2110
|
// enrich (default true): also cross-reference PRs and populate the
|
|
1998
2111
|
// detail-modal _artifacts/_notes fields. The /api/work-items list payload
|
|
1999
2112
|
// needs these (PR badges + note chips).
|
|
@@ -2003,11 +2116,26 @@ function invalidateWorkItemsCache() {
|
|
|
2003
2116
|
// never read _artifacts/_notes (getAgentStatus fallback, count-only callers)
|
|
2004
2117
|
// so the agent roster / CC preamble don't pay the enrichment cost on every
|
|
2005
2118
|
// cold rebuild.
|
|
2119
|
+
// onlyId (W-mscmqk6b03ha5673, enrich-only) — narrow the per-item detail
|
|
2120
|
+
// enrichment to the row(s) carrying this work-item id. The single-record
|
|
2121
|
+
// GET /api/work-items/<id> handler needs ONE fully-enriched row, but paid
|
|
2122
|
+
// the whole-history enrichment on every detail-modal open (at ~2.3k items
|
|
2123
|
+
// that's the same multi-second synchronous block the polled list endpoint
|
|
2124
|
+
// was fixed for, just triggered by a click). The full list is still
|
|
2125
|
+
// returned and the cheap pre-enrich dispatch cross-reference still runs
|
|
2126
|
+
// over everything, so _pendingReason/_skipReason/_blockedBy resolve
|
|
2127
|
+
// identically and the caller can still resolve the complete id-matched set
|
|
2128
|
+
// for its ambiguity check before reading the enriched row.
|
|
2006
2129
|
function getWorkItems(config, opts) {
|
|
2007
2130
|
const enrich = !(opts && opts.enrich === false);
|
|
2131
|
+
const onlyId = enrich && opts && typeof opts.onlyId === 'string' && opts.onlyId !== ''
|
|
2132
|
+
? opts.onlyId
|
|
2133
|
+
: null;
|
|
2008
2134
|
const now = Date.now();
|
|
2009
2135
|
config = config || getConfig();
|
|
2010
2136
|
if (enrich) {
|
|
2137
|
+
// An onlyId caller can be served the fully-enriched cache verbatim — it is
|
|
2138
|
+
// a strict superset of what the caller asked for.
|
|
2011
2139
|
if (_workItemsCache && (now - _workItemsCacheAt) < 1000) return _workItemsCache;
|
|
2012
2140
|
} else if (_workItemsLeanCache && (now - _workItemsLeanCacheAt) < 1000) {
|
|
2013
2141
|
return _workItemsLeanCache;
|
|
@@ -2015,7 +2143,7 @@ function getWorkItems(config, opts) {
|
|
|
2015
2143
|
let allItems = [];
|
|
2016
2144
|
|
|
2017
2145
|
// SQL is the canonical (and only) work-items store after Phase 9.
|
|
2018
|
-
const store = require('
|
|
2146
|
+
const store = require('../planning/work-items-store');
|
|
2019
2147
|
allItems = store.readAllWorkItems() || [];
|
|
2020
2148
|
|
|
2021
2149
|
// Cross-reference with dispatch (fill in agent from active dispatch if missing on work item)
|
|
@@ -2063,13 +2191,22 @@ function getWorkItems(config, opts) {
|
|
|
2063
2191
|
}
|
|
2064
2192
|
}
|
|
2065
2193
|
|
|
2194
|
+
// Rows that pay the per-item detail enrichment below. Without `onlyId` this
|
|
2195
|
+
// is the whole list, exactly as before.
|
|
2196
|
+
const enrichTargets = onlyId ? allItems.filter(it => it && it.id === onlyId) : allItems;
|
|
2197
|
+
// An `onlyId` lookup that matches nothing (the caller's 404 path) skips the
|
|
2198
|
+
// block wholesale, so a miss pays none of the once-per-call prep either.
|
|
2199
|
+
// Callers without `onlyId` keep today's behaviour exactly, empty store
|
|
2200
|
+
// included.
|
|
2201
|
+
const skipEnrichment = onlyId !== null && enrichTargets.length === 0;
|
|
2202
|
+
|
|
2066
2203
|
// Detail-modal enrichment (PR cross-ref + _artifacts/_notes). Skipped for
|
|
2067
2204
|
// enrich:false callers — see the getWorkItems header.
|
|
2068
|
-
if (enrich) {
|
|
2205
|
+
if (enrich && !skipEnrichment) {
|
|
2069
2206
|
const projects = getProjects(config);
|
|
2070
2207
|
// Cross-reference with PRs
|
|
2071
2208
|
const allPrs = getPullRequests(config);
|
|
2072
|
-
for (const item of
|
|
2209
|
+
for (const item of enrichTargets) {
|
|
2073
2210
|
if (item._pr && !item._prUrl) {
|
|
2074
2211
|
const project = shared.resolveProjectSource(item.project || item._source, projects, { allowCentral: false }).project || null;
|
|
2075
2212
|
const canonicalPrId = shared.getCanonicalPrId(project, item._pr);
|
|
@@ -2135,11 +2272,14 @@ function getWorkItems(config, opts) {
|
|
|
2135
2272
|
// P-34fa5d79 — pre-build the wiId → notes map so each item's _notes lookup
|
|
2136
2273
|
// is O(1) instead of re-scanning inbox+archive per item.
|
|
2137
2274
|
const _notesByWi = _buildNotesByWiMap(dispatch);
|
|
2275
|
+
// `_workItemIdCounts` stays a full-list tally even under `onlyId` — the
|
|
2276
|
+
// `hasUniqueId` note-linkage guard below asks whether an id collides
|
|
2277
|
+
// ANYWHERE, so narrowing it would change the enriched row's output.
|
|
2138
2278
|
const _workItemIdCounts = new Map();
|
|
2139
2279
|
for (const item of allItems) {
|
|
2140
2280
|
_workItemIdCounts.set(item.id, (_workItemIdCounts.get(item.id) || 0) + 1);
|
|
2141
2281
|
}
|
|
2142
|
-
for (const item of
|
|
2282
|
+
for (const item of enrichTargets) {
|
|
2143
2283
|
const arts = shared.isPlainObject(item._artifacts) ? { ...item._artifacts } : {};
|
|
2144
2284
|
const agentId = item.dispatched_to || item.agent;
|
|
2145
2285
|
// W-ms0ytfdd000d5a26 — the model this item actually ran on (or, for items
|
|
@@ -2235,10 +2375,14 @@ function getWorkItems(config, opts) {
|
|
|
2235
2375
|
// >= 1 000 ms and miss the cache, defeating the cache entirely.
|
|
2236
2376
|
// (W-mr00aooc000i1d6b — queries-work-items-cache CI failure on slow runners)
|
|
2237
2377
|
const cacheSetAt = Date.now();
|
|
2238
|
-
|
|
2378
|
+
// An `onlyId` result is only PARTIALLY enriched, so it must never be
|
|
2379
|
+
// published as either module cache: as the enriched cache it would serve
|
|
2380
|
+
// unenriched rows to the next list caller, and as the lean cache it would
|
|
2381
|
+
// carry enrichment the lean shape does not promise.
|
|
2382
|
+
if (enrich && !onlyId) {
|
|
2239
2383
|
_workItemsCache = allItems;
|
|
2240
2384
|
_workItemsCacheAt = cacheSetAt;
|
|
2241
|
-
} else {
|
|
2385
|
+
} else if (!enrich) {
|
|
2242
2386
|
_workItemsLeanCache = allItems;
|
|
2243
2387
|
_workItemsLeanCacheAt = cacheSetAt;
|
|
2244
2388
|
}
|
|
@@ -2258,7 +2402,7 @@ let _prdResultInputHash = ''; // SQL event version + authored-plan mtim
|
|
|
2258
2402
|
function _getPrdInputHash() {
|
|
2259
2403
|
const mtimes = [];
|
|
2260
2404
|
try {
|
|
2261
|
-
const row = require('
|
|
2405
|
+
const row = require('../db').getDb().prepare('SELECT COALESCE(MAX(id), 0) AS version FROM events').get();
|
|
2262
2406
|
mtimes.push(Number(row?.version) || 0);
|
|
2263
2407
|
} catch { mtimes.push(0); }
|
|
2264
2408
|
// W-mqacrzis0003df4a — Source-plan markdown mtimes. Without this, the
|
|
@@ -2294,6 +2438,10 @@ function getPrdInfo(config) {
|
|
|
2294
2438
|
// filename so render-prd.js#renderE2eSection can merge them with the live
|
|
2295
2439
|
// tracker and keep rendering a merged aggregate after its live record is swept.
|
|
2296
2440
|
const verifyPrsByPlan = {};
|
|
2441
|
+
// W-msa0mrus00mh466f — live (non-archived) PRD objects keyed by filename so
|
|
2442
|
+
// the revision-state derivation below can run once, server-side, after the
|
|
2443
|
+
// work-item index is built.
|
|
2444
|
+
const livePlansByFile = {};
|
|
2297
2445
|
let latestUpdatedAt = 0;
|
|
2298
2446
|
|
|
2299
2447
|
// Phase 10 step 4.3 — FK-based WI↔PRD-item join scaffolding. `prdItemIdByKey`
|
|
@@ -2332,6 +2480,7 @@ function getPrdInfo(config) {
|
|
|
2332
2480
|
completedAt: plan.completedAt || '',
|
|
2333
2481
|
_archived: isArch,
|
|
2334
2482
|
});
|
|
2483
|
+
if (!isArch) livePlansByFile[pf] = plan;
|
|
2335
2484
|
if (Array.isArray(plan.verifyPrs) && plan.verifyPrs.length > 0) {
|
|
2336
2485
|
verifyPrsByPlan[pf] = plan.verifyPrs.map(r => ({
|
|
2337
2486
|
id: r.id, url: r.url || '', title: r.title || '',
|
|
@@ -2365,7 +2514,7 @@ function getPrdInfo(config) {
|
|
|
2365
2514
|
// feature-id string match.
|
|
2366
2515
|
if (useFkJoin) {
|
|
2367
2516
|
try {
|
|
2368
|
-
const db = require('
|
|
2517
|
+
const db = require('../db').getDb();
|
|
2369
2518
|
const map = new Map();
|
|
2370
2519
|
for (const r of db.prepare('SELECT p.filename AS filename, p.archived AS archived, pi.feature_id AS feature_id, pi.id AS id FROM prd_items pi JOIN prds p ON p.id = pi.prd_id WHERE pi.feature_id IS NOT NULL').all()) {
|
|
2371
2520
|
map.set(`${r.archived ? 1 : 0} ${r.filename} ${r.feature_id}`, r.id);
|
|
@@ -2375,7 +2524,7 @@ function getPrdInfo(config) {
|
|
|
2375
2524
|
}
|
|
2376
2525
|
|
|
2377
2526
|
// SQLite is the unconditional PRD source.
|
|
2378
|
-
const prdStore = require('
|
|
2527
|
+
const prdStore = require('../planning/prd-store');
|
|
2379
2528
|
for (const { filename, archived, plan, updatedAt } of prdStore.listPrdRows()) {
|
|
2380
2529
|
try {
|
|
2381
2530
|
latestUpdatedAt = Math.max(latestUpdatedAt, updatedAt);
|
|
@@ -2394,7 +2543,7 @@ function getPrdInfo(config) {
|
|
|
2394
2543
|
// so they don't masquerade as a 2nd PRD item in the aggregate guard (W-mpem52qn).
|
|
2395
2544
|
const wiById = {};
|
|
2396
2545
|
const allWiById = {};
|
|
2397
|
-
for (const wi of require('
|
|
2546
|
+
for (const wi of require('../planning/work-items-store').readAllWorkItems()) {
|
|
2398
2547
|
if (!wi?.id) {
|
|
2399
2548
|
console.warn(`[queries] Skipping work item without id in ${wi?._source || 'unknown'}:`, JSON.stringify(wi).slice(0, 120));
|
|
2400
2549
|
continue;
|
|
@@ -2403,13 +2552,27 @@ function getPrdInfo(config) {
|
|
|
2403
2552
|
if (wi.sourcePlan && !wiById[wi.id]) wiById[wi.id] = wi;
|
|
2404
2553
|
}
|
|
2405
2554
|
|
|
2555
|
+
// W-msa0mrus00mh466f — canonical PRD revision state, derived ONCE here.
|
|
2556
|
+
// `revision-requested` and `planStale` can both be true on the same PRD; the
|
|
2557
|
+
// dashboard must not offer competing recovery paths, so the server decides
|
|
2558
|
+
// which state applies and the renderer just paints it.
|
|
2559
|
+
const planRevisions = {};
|
|
2560
|
+
const allWorkItemList = Object.values(allWiById);
|
|
2561
|
+
for (const [pf, plan] of Object.entries(livePlansByFile)) {
|
|
2562
|
+
try {
|
|
2563
|
+
const revisionWi = shared.findPrdRevisionWorkItem(plan, pf, allWorkItemList);
|
|
2564
|
+
const revision = shared.derivePrdRevisionState(plan, revisionWi);
|
|
2565
|
+
if (revision.state !== shared.PRD_REVISION_STATE.NONE) planRevisions[pf] = revision;
|
|
2566
|
+
} catch { /* a malformed PRD must not break the whole PRD read */ }
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2406
2569
|
// Index work items by their stable SQL FK so the per-item join can resolve
|
|
2407
2570
|
// PRD item → WI by prd_items.id rather than the feature-id string.
|
|
2408
2571
|
// Best-effort: null map → the join falls back to the legacy string match.
|
|
2409
2572
|
let wiByPrdItemId = null;
|
|
2410
2573
|
if (useFkJoin && prdItemIdByKey) {
|
|
2411
2574
|
try {
|
|
2412
|
-
const db = require('
|
|
2575
|
+
const db = require('../db').getDb();
|
|
2413
2576
|
const fkByWiId = new Map();
|
|
2414
2577
|
for (const r of db.prepare('SELECT id, prd_item_id FROM work_items WHERE prd_item_id IS NOT NULL').all()) {
|
|
2415
2578
|
if (!fkByWiId.has(r.id)) fkByWiId.set(r.id, r.prd_item_id);
|
|
@@ -2578,7 +2741,7 @@ function getPrdInfo(config) {
|
|
|
2578
2741
|
|
|
2579
2742
|
const progress = {
|
|
2580
2743
|
total, complete, inProgress, paused, missing, donePercent, planTimings,
|
|
2581
|
-
verifyPrsByPlan,
|
|
2744
|
+
verifyPrsByPlan, planRevisions,
|
|
2582
2745
|
items: items.map(i => ({
|
|
2583
2746
|
id: i.id, name: i.name || i.title, priority: i.priority,
|
|
2584
2747
|
complexity: i.estimated_complexity || i.size, status: i.status || 'missing',
|
|
@@ -2733,7 +2896,9 @@ function _gitExec(localPath, args) {
|
|
|
2733
2896
|
// Belt-and-suspenders: explicitly kill the stuck child so it can't
|
|
2734
2897
|
// linger as an orphaned git.exe. SIGKILL on Unix, forced termination
|
|
2735
2898
|
// on Windows (Node maps both to TerminateProcess via libuv).
|
|
2736
|
-
try {
|
|
2899
|
+
try {
|
|
2900
|
+
if (child) shared.terminateProcess('engine.query.git-probe-timeout', child);
|
|
2901
|
+
}
|
|
2737
2902
|
catch { /* child may already be dead */ }
|
|
2738
2903
|
reject(new Error(`git probe watchdog: \`git ${args.join(' ')}\` did not settle within ${watchdogMs}ms`));
|
|
2739
2904
|
}, watchdogMs);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* engine/safe-expr.js — Hand-rolled recursive-descent expression evaluator.
|
|
2
|
+
* engine/core/safe-expr.js — Hand-rolled recursive-descent expression evaluator.
|
|
3
3
|
*
|
|
4
4
|
* Phase 3.1 of feat/plan-resilient-watches (P-w6f4a3e8). Used by watch action
|
|
5
5
|
* guards (P-w7c5d8b3) and predicate-based watches (Phase 2.x) to evaluate
|