@ryanfw/prompt-orchestration-pipeline 0.14.1 → 0.14.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryanfw/prompt-orchestration-pipeline",
3
- "version": "0.14.1",
3
+ "version": "0.14.2",
4
4
  "description": "A Prompt-orchestration pipeline (POP) is a framework for building, running, and experimenting with complex chains of LLM tasks.",
5
5
  "type": "module",
6
6
  "main": "src/ui/server.js",
@@ -829,16 +829,80 @@ export async function handleTaskStart(
829
829
  return;
830
830
  }
831
831
 
832
- // Validate task existence
833
- if (!snapshot.tasks || !snapshot.tasks[taskId]) {
834
- sendJson(res, 400, {
832
+ // Read job pipeline config (needed for both task validation and dependency checking)
833
+ const jobPipelinePath = getJobPipelinePath(dataDir, jobId, "current");
834
+ let jobPipeline;
835
+ try {
836
+ const content = await fs.promises.readFile(jobPipelinePath, "utf8");
837
+ jobPipeline = JSON.parse(content);
838
+ } catch (error) {
839
+ sendJson(res, 500, {
835
840
  ok: false,
836
- code: "task_not_found",
837
- message: "Task not found in job",
841
+ code: "pipeline_config_not_found",
842
+ message: "Pipeline configuration not found",
838
843
  });
839
844
  return;
840
845
  }
841
846
 
847
+ // Helper function to extract task name from string or object
848
+ const getTaskName = (t) => (typeof t === "string" ? t : t.name);
849
+
850
+ // Check if task exists in pipeline.json
851
+ const pipelineTaskNames = (jobPipeline.tasks || []).map(getTaskName);
852
+ const taskExistsInPipeline = pipelineTaskNames.includes(taskId);
853
+
854
+ // Validate task existence - check both tasks-status.json and pipeline.json
855
+ if (!snapshot.tasks || !snapshot.tasks[taskId]) {
856
+ if (!taskExistsInPipeline) {
857
+ // Task not found in either file - true error case
858
+ sendJson(res, 400, {
859
+ ok: false,
860
+ code: "task_not_found",
861
+ message: "Task not found in pipeline definition",
862
+ });
863
+ return;
864
+ }
865
+
866
+ // Task exists in pipeline.json but not in tasks-status.json
867
+ // Auto-add it with default pending state
868
+ console.log(
869
+ `[handleTaskStart] Auto-adding missing task "${taskId}" to tasks-status.json for job ${jobId}`
870
+ );
871
+
872
+ await writeJobStatus(jobDir, (s) => {
873
+ if (!s.tasks) {
874
+ s.tasks = {};
875
+ }
876
+ if (!s.tasks[taskId]) {
877
+ s.tasks[taskId] = {
878
+ state: "pending",
879
+ currentStage: null,
880
+ attempts: 0,
881
+ refinementAttempts: 0,
882
+ files: {
883
+ artifacts: [],
884
+ logs: [],
885
+ tmp: [],
886
+ },
887
+ };
888
+ }
889
+ return s;
890
+ });
891
+
892
+ // Re-read the updated snapshot
893
+ try {
894
+ const content = await fs.promises.readFile(statusPath, "utf8");
895
+ snapshot = JSON.parse(content);
896
+ } catch (error) {
897
+ sendJson(res, 500, {
898
+ ok: false,
899
+ code: "internal_error",
900
+ message: "Failed to read updated job status",
901
+ });
902
+ return;
903
+ }
904
+ }
905
+
842
906
  // Validate task state is Pending
843
907
  if (snapshot.tasks[taskId].state !== "pending") {
844
908
  sendJson(res, 400, {
@@ -849,21 +913,6 @@ export async function handleTaskStart(
849
913
  return;
850
914
  }
851
915
 
852
- // Read job pipeline config
853
- const jobPipelinePath = getJobPipelinePath(dataDir, jobId, "current");
854
- let jobPipeline;
855
- try {
856
- const content = await fs.promises.readFile(jobPipelinePath, "utf8");
857
- jobPipeline = JSON.parse(content);
858
- } catch (error) {
859
- sendJson(res, 500, {
860
- ok: false,
861
- code: "pipeline_config_not_found",
862
- message: "Pipeline configuration not found",
863
- });
864
- return;
865
- }
866
-
867
916
  // Validate dependencies via validateUpstreamDone
868
917
  const depCheck = validateUpstreamDone({
869
918
  jobPipelineTasks: jobPipeline.tasks,