{"record":{"id":"e27de13a57a590a6","repo":"coleam00/Archon","slug":"node-loop-group-until-deprecated","errorCode":"node_loop_group_until_deprecated","errorMessage":"Node '${id}': the prose 'loop_group.until' completion signal is deprecated. Declare 'loop_group.until_bash' instead — it can read a body node's structured output (e.g. 'test $body-node.output.field = \"true\"') (#2707 step 3). While supported, emit legacy signals as '<promise>SIGNAL</promise>' or a final standalone signal line.","messagePattern":"Node '(.+?)': the prose 'loop_group\\.until' completion signal is deprecated\\. Declare 'loop_group\\.until_bash' instead — it can read a body node's structured output \\(e\\.g\\. 'test \\$body-node\\.output\\.field = \"true\"'\\) \\(#2707 step 3\\)\\. While supported, emit legacy signals as '<promise>SIGNAL</promise>' or a final standalone signal line\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/workflows/src/loader.ts","lineNumber":498,"sourceCode":"  // and (loop: only) `until_field` are the declared, structured replacements. This\n  // keeps running exactly as before; only the guidance is new.\n  if (isLoopNode(node) && node.loop.until !== undefined) {\n    const message =\n      `Node '${id}': the prose 'loop.until' completion signal is deprecated. Declare ` +\n      \"'loop.until_bash' (deterministic check) or 'loop.until_field' (a declared boolean \" +\n      'in output_format) instead (#2707 step 3). While supported, emit legacy signals as ' +\n      \"'<promise>SIGNAL</promise>' or a final standalone signal line.\";\n    warnings.push(message);\n    getLog().warn({ id, warning: message }, 'node_loop_until_deprecated');\n  } else if (isLoopGroupNode(node) && node.loop_group.until !== undefined) {\n    const message =\n      `Node '${id}': the prose 'loop_group.until' completion signal is deprecated. ` +\n      \"Declare 'loop_group.until_bash' instead — it can read a body node's structured \" +\n      'output (e.g. \\'test $body-node.output.field = \"true\"\\') (#2707 step 3). While ' +\n      \"supported, emit legacy signals as '<promise>SIGNAL</promise>' or a final standalone \" +\n      'signal line.';\n    warnings.push(message);\n    getLog().warn({ id, warning: message }, 'node_loop_group_until_deprecated');\n  }\n\n  // A gate node inside a loop_group body only pauses the enclosing loop when it is\n  // the body's SOLE terminal sink (#2707 step 3, target-model decision (b)) — a\n  // mid-body or co-terminal gate has no defined resume semantics and silently does\n  // not stop iteration (mid-body resume-to-node is deferred to #2708). This is\n  // guidance for the new authoring pattern, not a rejection: the file keeps loading\n  // either way, matching the grow-then-deprecate posture used throughout this\n  // function — including for a gate placed here via the legacy `on_reject`\n  // mechanism, which predates and is unrelated to this pattern but is equally\n  // unable to stop the loop from this position.\n  if (isLoopGroupNode(node)) {\n    // Every body entry — including an unexpanded `include:` directive, which has\n    // the identical `depends_on` shape (both extend dagNodeBaseSchema) and is a\n    // real graph participant here, not yet inlined — contributes to and can BE a\n    // terminal sink. Excluding it would silently misclassify a gate a downstream\n    // include node depends on as \"terminal\", and miss an include node that is\n    // itself a second, co-terminal sink.","sourceCodeStart":480,"sourceCodeEnd":516,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/workflows/src/loader.ts#L480-L516","documentation":"The prose `loop_group.until` completion signal is deprecated the same way as `loop.until`: declare `loop_group.until_bash` instead, which deterministically reads a body node's structured output (e.g. `test $body-node.output.field = \"true\"`). The loader warns; while supported, the legacy signal must be emitted as `<promise>SIGNAL</promise>` or a final standalone signal line.","triggerScenarios":"collectGateAndLoopDeprecationWarnings on a loop_group node whose `loop_group.until` is defined (loader.ts:498); fires at every parse of the workflow.","commonSituations":"Multi-node loop bodies whose completion was declared in prose; workflows predating until_bash; intermittent non-termination when the model phrases the signal differently than expected.","solutions":["Replace `loop_group.until:` with `loop_group.until_bash:` — e.g. `test $body-node.output.field = \"true\"` reading a body node's declared structured output.","Declare the boolean the predicate reads in the body node's output_format so completion is deterministic.","If keeping prose temporarily, use the legacy emission form: `<promise>SIGNAL</promise>` or a final standalone signal line.","Migrate all loop_group.until usages before the prose channel is removed (#2707 step 3)."],"exampleFix":"# before\nloop_group:\n  body: [run_tests, fix]\n  until: ALL_PASS\n# after\nloop_group:\n  body: [run_tests, fix]\n  until_bash: 'test \"$run_tests.output.all_passed\" = \"true\"'","handlingStrategy":"validation","validationCode":"// Require deterministic loop_group completion before loading:\nfor (const node of workflow.nodes) {\n  if (node.type === 'loop_group' && node.loop_group?.until !== undefined) {\n    throw new Error(`Node '${node.id}': replace prose loop_group.until with loop_group.until_bash`);\n  }\n}","typeGuard":"function usesProseGroupUntil(node: { type: string; loop_group?: { until?: unknown } }): boolean {\n  return node.type === 'loop_group' && node.loop_group?.until !== undefined;\n}","tryCatchPattern":null,"preventionTips":["Author loop_group completion with until_bash reading a body node's declared structured output.","Declare the boolean the predicate reads in the body node's output_format.","If keeping prose until temporarily, enforce the <promise>SIGNAL</promise> or standalone-final-line emission form.","Sweep multi-node loop bodies for prose until before the prose channel is deleted."],"tags":["workflow","deprecation","loop-group","completion-signal"],"backgroundTag":"deprecated-workflow-field","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}