@yemi33/minions 0.1.1961 → 0.1.1962
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/engine/pipeline.js +22 -7
- package/engine/shared.js +6 -4
- package/engine.js +13 -34
- package/package.json +1 -1
package/engine/pipeline.js
CHANGED
|
@@ -8,7 +8,7 @@ const fs = require('fs');
|
|
|
8
8
|
const path = require('path');
|
|
9
9
|
const shared = require('./shared');
|
|
10
10
|
const queries = require('./queries');
|
|
11
|
-
const { safeJson, safeJsonNoRestore, safeWrite, safeRead, safeReadDir, uid, log, ts, dateStamp, mutateJsonFileLocked, mutateWorkItems, slugify, formatTranscriptEntry, WI_STATUS, WORK_TYPE, PLAN_STATUS, PR_STATUS, PIPELINE_STATUS, STAGE_TYPE, MEETING_STATUS, ENGINE_DEFAULTS, MINIONS_DIR } = shared;
|
|
11
|
+
const { safeJson, safeJsonNoRestore, safeWrite, safeRead, safeReadDir, uid, log, ts, dateStamp, mutateJsonFileLocked, mutateWorkItems, slugify, formatTranscriptEntry, WI_STATUS, WORK_TYPE, PLAN_STATUS, PR_STATUS, PIPELINE_STATUS, STAGE_TYPE, MEETING_STATUS, READ_ONLY_ROOT_TASK_TYPES, ENGINE_DEFAULTS, MINIONS_DIR } = shared;
|
|
12
12
|
const routing = require('./routing');
|
|
13
13
|
const http = require('http');
|
|
14
14
|
const { parseCronExpr, shouldRunNow } = require('./scheduler');
|
|
@@ -388,13 +388,20 @@ function executeTaskStage(stage, stageState, run, config, pipeline = {}) {
|
|
|
388
388
|
const projectSlug = _pipelineProjectSlug(project);
|
|
389
389
|
const id = `PL-${run.runId.slice(4, 12)}-${stage.id}-${i}${projectResolution.projects.length > 1 || project ? '-' + projectSlug : ''}`;
|
|
390
390
|
const wiPath = _pipelineWorkItemsPath(project);
|
|
391
|
+
const wiType = routing.normalizeWorkType(item.type || stage.taskType, WORK_TYPE.EXPLORE);
|
|
392
|
+
// W-mp8ho6w500034a58: read-only stages don't commit, so a pipeline
|
|
393
|
+
// branch label is meaningless to them — omit it entirely so the
|
|
394
|
+
// dispatcher's read-only fast-path runs without ceremony.
|
|
395
|
+
const wiBranch = READ_ONLY_ROOT_TASK_TYPES.has(wiType)
|
|
396
|
+
? null
|
|
397
|
+
: `pipeline/${run.pipelineId}/${stage.id}`;
|
|
391
398
|
mutateWorkItems(wiPath, workItems => {
|
|
392
399
|
if (workItems.some(w => w.id === id)) { createdIds.push(id); return workItems; }
|
|
393
400
|
workItems.push({
|
|
394
401
|
id,
|
|
395
402
|
title: item.title || stage.title,
|
|
396
403
|
description: item.description || stage.description || '',
|
|
397
|
-
type:
|
|
404
|
+
type: wiType,
|
|
398
405
|
priority: item.priority || stage.priority || 'medium',
|
|
399
406
|
// Agent is a soft routing hint unless agentLock/hardAgent is set.
|
|
400
407
|
...(item.agent || stage.agent ? { agent: item.agent || stage.agent } : {}),
|
|
@@ -403,7 +410,7 @@ function executeTaskStage(stage, stageState, run, config, pipeline = {}) {
|
|
|
403
410
|
status: WI_STATUS.PENDING,
|
|
404
411
|
created: ts(),
|
|
405
412
|
createdBy: 'pipeline:' + run.pipelineId,
|
|
406
|
-
branch:
|
|
413
|
+
...(wiBranch ? { branch: wiBranch } : {}),
|
|
407
414
|
_pipelineRun: run.runId,
|
|
408
415
|
_pipelineStage: stage.id,
|
|
409
416
|
});
|
|
@@ -430,11 +437,18 @@ function executeTaskStageLegacy(stage, stageState, run, config) {
|
|
|
430
437
|
const item = items[i % items.length];
|
|
431
438
|
const id = `PL-${run.runId.slice(4, 12)}-${stage.id}-${i}`;
|
|
432
439
|
if (workItems.some(w => w.id === id)) { createdIds.push(id); continue; }
|
|
440
|
+
const wiType = routing.normalizeWorkType(item.type || stage.taskType, WORK_TYPE.EXPLORE);
|
|
441
|
+
// W-mp8ho6w500034a58: read-only stages don't commit, so the branch
|
|
442
|
+
// label is meaningless — omit it so dispatch takes the read-only path
|
|
443
|
+
// without recomputing a worktree placement that will never be used.
|
|
444
|
+
const wiBranch = READ_ONLY_ROOT_TASK_TYPES.has(wiType)
|
|
445
|
+
? null
|
|
446
|
+
: `pipeline/${run.pipelineId}/${stage.id}`;
|
|
433
447
|
workItems.push({
|
|
434
448
|
id,
|
|
435
449
|
title: item.title || stage.title,
|
|
436
450
|
description: item.description || stage.description || '',
|
|
437
|
-
type:
|
|
451
|
+
type: wiType,
|
|
438
452
|
priority: item.priority || stage.priority || 'medium',
|
|
439
453
|
// Agent is a soft routing hint unless agentLock/hardAgent is set.
|
|
440
454
|
...(item.agent || stage.agent ? { agent: item.agent || stage.agent } : {}),
|
|
@@ -442,7 +456,7 @@ function executeTaskStageLegacy(stage, stageState, run, config) {
|
|
|
442
456
|
status: WI_STATUS.PENDING,
|
|
443
457
|
created: ts(),
|
|
444
458
|
createdBy: 'pipeline:' + run.pipelineId,
|
|
445
|
-
branch:
|
|
459
|
+
...(wiBranch ? { branch: wiBranch } : {}),
|
|
446
460
|
_pipelineRun: run.runId,
|
|
447
461
|
_pipelineStage: stage.id,
|
|
448
462
|
});
|
|
@@ -571,7 +585,8 @@ async function executePlanStage(stage, stageState, run, config, pipeline = {}) {
|
|
|
571
585
|
id: wiId, title: `Convert plan to PRD: ${existingPlanFile}`,
|
|
572
586
|
type: WORK_TYPE.PLAN_TO_PRD, priority: 'high', status: WI_STATUS.PENDING,
|
|
573
587
|
planFile: existingPlanFile, created: ts(), createdBy: 'pipeline:' + run.pipelineId,
|
|
574
|
-
|
|
588
|
+
// W-mp8ho6w500034a58: PLAN_TO_PRD is read-only — no branch needed.
|
|
589
|
+
_pipelineRun: run.runId, _pipelineStage: stage.id,
|
|
575
590
|
...(project ? { project: project.name } : {}),
|
|
576
591
|
});
|
|
577
592
|
}
|
|
@@ -665,7 +680,7 @@ async function executePlanStage(stage, stageState, run, config, pipeline = {}) {
|
|
|
665
680
|
planFile: path.basename(filePath),
|
|
666
681
|
created: ts(),
|
|
667
682
|
createdBy: 'pipeline:' + run.pipelineId,
|
|
668
|
-
|
|
683
|
+
// W-mp8ho6w500034a58: PLAN_TO_PRD is read-only — no branch needed.
|
|
669
684
|
_pipelineRun: run.runId,
|
|
670
685
|
_pipelineStage: stage.id,
|
|
671
686
|
...(project ? { project: project.name } : {}),
|
package/engine/shared.js
CHANGED
|
@@ -2883,10 +2883,12 @@ const READ_ONLY_ROOT_TASK_TYPES = new Set(['meeting', 'ask', 'explore', 'plan-to
|
|
|
2883
2883
|
* the drive-root preflight that fires when MINIONS_DIR sits one level
|
|
2884
2884
|
* below a filesystem root (resolveProjectRootDir's collapse case).
|
|
2885
2885
|
*
|
|
2886
|
-
* NOTE: Pipeline branches
|
|
2887
|
-
*
|
|
2888
|
-
*
|
|
2889
|
-
* branch
|
|
2886
|
+
* NOTE (W-mp8ho6w500034a58): Pipeline branches no longer override this.
|
|
2887
|
+
* Read-only pipeline stages don't commit, so a `pipeline/...` branch is a
|
|
2888
|
+
* meaningless label for them — the dispatcher short-circuits read-only WIs
|
|
2889
|
+
* regardless of branch name, and `engine/pipeline.js` now omits the branch
|
|
2890
|
+
* field for read-only stages. Only code-mutating pipeline stages need a
|
|
2891
|
+
* worktree, and they take the normal code-mutating path below.
|
|
2890
2892
|
*
|
|
2891
2893
|
* @param {{ localPath?: string|null }|null|undefined} project
|
|
2892
2894
|
* @param {string} type — work type (e.g. 'fix', 'explore', 'meeting')
|
package/engine.js
CHANGED
|
@@ -108,10 +108,6 @@ const CHECKPOINT_CAP_FAIL_REASON = 'Exceeded 3 checkpoint-resumes; manual interv
|
|
|
108
108
|
// re-aliased here for the existing call sites in this file.
|
|
109
109
|
const READ_ONLY_ROOT_TASK_TYPES = shared.READ_ONLY_ROOT_TASK_TYPES;
|
|
110
110
|
|
|
111
|
-
function isPipelineBranchName(branchName) {
|
|
112
|
-
return typeof branchName === 'string' && branchName.startsWith('pipeline/');
|
|
113
|
-
}
|
|
114
|
-
|
|
115
111
|
// ─── Dispatch Management (extracted to engine/dispatch.js) ───────────────────
|
|
116
112
|
|
|
117
113
|
const { mutateDispatch, addToDispatch, addToDispatchWithValidation, isRetryableFailureReason, completeDispatch,
|
|
@@ -777,8 +773,13 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
777
773
|
// (caller defaults cwd to worktreeRootDir; drive-root collapse throws
|
|
778
774
|
// WORKTREE_ROOTDIR_COLLAPSED_TO_DRIVE_ROOT — same fail-fast behavior as
|
|
779
775
|
// the legacy resolveProjectRootDir call this replaced).
|
|
780
|
-
//
|
|
781
|
-
//
|
|
776
|
+
// W-mp8ho6w500034a58: read-only task types (meeting/ask/explore/plan/plan-to-prd)
|
|
777
|
+
// never need a worktree — even when carrying a pipeline branch. Pipeline branches
|
|
778
|
+
// on read-only stages are a no-op label; the stage doesn't commit anything, so
|
|
779
|
+
// the worktree had no functional purpose and was only there to absorb a drive-
|
|
780
|
+
// root preflight that fired against MINIONS_DIR's parent. Read-only pipeline
|
|
781
|
+
// stages now short-circuit alongside any other read-only WI (see the gate at
|
|
782
|
+
// `if (branchName && READ_ONLY_ROOT_TASK_TYPES.has(type))` below).
|
|
782
783
|
const _preBranchName = meta?.branch ? sanitizeBranch(meta.branch) : null;
|
|
783
784
|
let cwd, worktreeRootDir;
|
|
784
785
|
try {
|
|
@@ -799,29 +800,6 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
799
800
|
}
|
|
800
801
|
throw rootErr;
|
|
801
802
|
}
|
|
802
|
-
// Pipeline branches need a worktree even for read-only types (the worktree
|
|
803
|
-
// IS the pipeline's isolated workspace). When we detect a pipeline branch
|
|
804
|
-
// on a read-only type, recompute worktreeRootDir so the worktree creation
|
|
805
|
-
// block has a placement parent — and so the drive-root preflight still fires.
|
|
806
|
-
if (worktreeRootDir === null && isPipelineBranchName(_preBranchName)) {
|
|
807
|
-
try {
|
|
808
|
-
worktreeRootDir = shared.resolveProjectRootDir(project.localPath, MINIONS_DIR);
|
|
809
|
-
} catch (rootErr) {
|
|
810
|
-
if (rootErr?.code === 'WORKTREE_ROOTDIR_COLLAPSED_TO_DRIVE_ROOT' || rootErr?.code === 'WORKTREE_ROOTDIR_MISSING_BASE') {
|
|
811
|
-
log('error', `spawnAgent: pipeline-branch rootDir resolution failed for ${id}: ${rootErr.message}`);
|
|
812
|
-
completeDispatch(
|
|
813
|
-
id,
|
|
814
|
-
DISPATCH_RESULT.ERROR,
|
|
815
|
-
rootErr.message.slice(0, 800),
|
|
816
|
-
'Pre-spawn worktree preflight rejected — see failure_class for the specific cause.',
|
|
817
|
-
{ failureClass: FAILURE_CLASS.WORKTREE_PREFLIGHT, agentRetryable: false },
|
|
818
|
-
);
|
|
819
|
-
cleanupTempAgent(agentId);
|
|
820
|
-
return null;
|
|
821
|
-
}
|
|
822
|
-
throw rootErr;
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
803
|
// Legacy local alias: downstream git ops (worktree add, prune, fetch) and
|
|
826
804
|
// the `cwd === rootDir` safety warn at line ~1387 reference `rootDir`. For
|
|
827
805
|
// read-only rootless tasks (no worktree, no branch) this is null — the
|
|
@@ -910,14 +888,15 @@ async function spawnAgent(dispatchItem, config) {
|
|
|
910
888
|
};
|
|
911
889
|
_phaseT.afterPrompt = Date.now();
|
|
912
890
|
|
|
913
|
-
if (branchName && READ_ONLY_ROOT_TASK_TYPES.has(type)
|
|
891
|
+
if (branchName && READ_ONLY_ROOT_TASK_TYPES.has(type)) {
|
|
914
892
|
// W-mp7havqf0007ce6b: read-only types (meeting/ask/explore/plan/plan-to-prd)
|
|
915
893
|
// short-circuit BEFORE the worktree-creation block. resolveSpawnPaths returns
|
|
916
894
|
// worktreeRootDir=null for read-only types, and path.resolve(null, ...) throws
|
|
917
|
-
// ("paths[0] must be of type string. Received null"). Pipeline branches
|
|
918
|
-
// exempt — they
|
|
919
|
-
//
|
|
920
|
-
//
|
|
895
|
+
// ("paths[0] must be of type string. Received null"). Pipeline branches used
|
|
896
|
+
// to be exempt — they don't need to be (W-mp8ho6w500034a58): read-only stages
|
|
897
|
+
// never commit, so a pipeline-branch label is meaningless for them and the
|
|
898
|
+
// forced worktree only existed to feed the drive-root preflight that this
|
|
899
|
+
// short-circuit now correctly avoids.
|
|
921
900
|
log('info', `${type}: read-only task with branch ${branchName} — skipping worktree, running in cwd ${cwd}`);
|
|
922
901
|
branchName = null;
|
|
923
902
|
worktreePath = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1962",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|