taskplane 0.24.23 → 0.24.25

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.
@@ -2908,6 +2908,45 @@ export async function executeOrchBatch(
2908
2908
  return hasSucceeded && hasHardFailure;
2909
2909
  });
2910
2910
 
2911
+ // ── Safety net: auto-commit uncommitted artifacts before merge ──
2912
+ // Workers should commit at step boundaries, but may leave uncommitted
2913
+ // files (especially for level-0 / fast tasks). Check each merge-candidate
2914
+ // lane worktree and auto-commit any remaining changes so they're included
2915
+ // in the merge. Skips lanes with only failed/stalled tasks (no merge).
2916
+ for (const lane of waveResult.allocatedLanes) {
2917
+ if (!lane.worktreePath || !existsSync(lane.worktreePath)) continue;
2918
+ // Only check lanes that have at least one succeeded task (merge candidates)
2919
+ const laneOutcome = laneOutcomeByNumber.get(lane.laneNumber);
2920
+ if (!laneOutcome) continue;
2921
+ const hasSucceeded = laneOutcome.tasks.some(t => t.status === "succeeded");
2922
+ if (!hasSucceeded) continue;
2923
+ try {
2924
+ const addResult = runGit(["add", "-A"], lane.worktreePath);
2925
+ if (!addResult.ok) {
2926
+ execLog("merge", batchState.batchId, `safety-net: git add failed in ${lane.laneId}`, { stderr: addResult.stderr });
2927
+ continue;
2928
+ }
2929
+ const statusResult = runGit(["status", "--porcelain"], lane.worktreePath);
2930
+ if (!statusResult.ok || !statusResult.stdout?.trim()) continue;
2931
+ const taskIds = lane.tasks.map(t => t.taskId).join(", ");
2932
+ const commitResult = runGit(
2933
+ ["commit", "-m", `safety-net: uncommitted artifacts for ${taskIds}`],
2934
+ lane.worktreePath,
2935
+ );
2936
+ if (commitResult.ok) {
2937
+ execLog("merge", batchState.batchId, `safety-net: auto-committed uncommitted files in ${lane.laneId}`, {
2938
+ worktree: lane.worktreePath,
2939
+ taskIds,
2940
+ files: statusResult.stdout.trim(),
2941
+ });
2942
+ } else {
2943
+ execLog("merge", batchState.batchId, `safety-net: commit failed in ${lane.laneId}`, { stderr: commitResult.stderr });
2944
+ }
2945
+ } catch (err: any) {
2946
+ execLog("merge", batchState.batchId, `safety-net: unexpected error in ${lane.laneId}`, { error: err?.message });
2947
+ }
2948
+ }
2949
+
2911
2950
  if (succeededSegmentTaskIdsForMerge.length > 0) {
2912
2951
  const mergeableLaneCount = waveResult.allocatedLanes.filter(lane => {
2913
2952
  const outcome = laneOutcomeByNumber.get(lane.laneNumber);
@@ -611,18 +611,9 @@ export async function spawnMergeAgentV2(
611
611
  packet: null,
612
612
  env: {
613
613
  ORCH_BATCH_ID: bid,
614
- // Isolate merge agent from operator's global preferences to prevent
615
- // verification test contamination (e.g., stale reviewerModel pref
616
- // overriding schema defaults in deep-equal assertions).
617
- PI_CODING_AGENT_DIR: join(sidecarRoot, "runtime", bid, "merge-agent-env"),
618
614
  },
619
615
  };
620
616
 
621
- // Ensure isolated agent dir exists with empty preferences
622
- const isolatedAgentDir = join(sidecarRoot, "runtime", bid, "merge-agent-env", "taskplane");
623
- mkdirSync(isolatedAgentDir, { recursive: true });
624
- try { writeFileSync(join(isolatedAgentDir, "preferences.json"), "{}\n", { flag: "wx" }); } catch { /* already exists */ }
625
-
626
617
  const { promise, kill } = spawnAgent(opts);
627
618
 
628
619
  // Store the kill handle for external cleanup (pause/abort).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskplane",
3
- "version": "0.24.23",
3
+ "version": "0.24.25",
4
4
  "description": "AI agent orchestration for pi — parallel task execution with checkpoint discipline",
5
5
  "keywords": [
6
6
  "pi-package",