Summary
validateMeta rejects meta.phases entries that are plain strings with the opaque error each meta phase must have a title string. Models very frequently write phases: ['Setup', 'Fan-out', 'Synthesis'] (an array of strings) instead of phases: [{ title: 'Setup' }, ...], so the first workflow call fails validation and a whole model roundtrip is wasted before any sub-agent spawns.
Evidence
In a benchmark of the same task run with the workflow tool required, 4 of 6 workflow invocations lost their first call to exactly this error. The models then retried the identical script with the phases field dropped, which validated and ran (spawning agentCount: 5). The failure is fast (it happens before any sub-agent starts), so it does not blow the budget — but it costs one roundtrip and latency on the majority of first attempts, and the error text does not tell the model what shape was expected or what it actually sent.
Root cause
src/workflow.ts:378-385:
if (value.phases !== undefined) {
if (!Array.isArray(value.phases)) throw new Error("meta.phases must be an array");
for (const phase of value.phases) {
if (!phase || typeof phase !== "object" || typeof (phase as WorkflowMetaPhase).title !== "string") {
throw new Error("each meta phase must have a title string");
}
}
}
A string entry fails the typeof phase !== "object" guard. meta.phases is documented as optional presentation metadata, so a bare title string carries all the information the object form does.
Suggested fix
Either (or both):
-
Accept strings as shorthand — normalize phases: string[] to phases: {title: string}[]. Since phases are only a display outline, 'Setup' and {title: 'Setup'} are equivalent, and this removes the most common first-call failure entirely:
value.phases = value.phases.map((p) =>
typeof p === "string" ? { title: p } : p
);
for (const phase of value.phases) {
if (!phase || typeof phase !== "object" || typeof phase.title !== "string") {
throw new Error(
`each meta.phases entry must be a string or { title: string }; got ${JSON.stringify(phase)}`
);
}
}
-
Improve the error message regardless — include the expected shape and the offending value so a model can self-correct in one step instead of guessing.
Environment: pi-dynamic-workflows v1.0.1.
Summary
validateMetarejectsmeta.phasesentries that are plain strings with the opaque erroreach meta phase must have a title string. Models very frequently writephases: ['Setup', 'Fan-out', 'Synthesis'](an array of strings) instead ofphases: [{ title: 'Setup' }, ...], so the firstworkflowcall fails validation and a whole model roundtrip is wasted before any sub-agent spawns.Evidence
In a benchmark of the same task run with the workflow tool required, 4 of 6 workflow invocations lost their first call to exactly this error. The models then retried the identical script with the
phasesfield dropped, which validated and ran (spawningagentCount: 5). The failure is fast (it happens before any sub-agent starts), so it does not blow the budget — but it costs one roundtrip and latency on the majority of first attempts, and the error text does not tell the model what shape was expected or what it actually sent.Root cause
src/workflow.ts:378-385:A string entry fails the
typeof phase !== "object"guard.meta.phasesis documented as optional presentation metadata, so a bare title string carries all the information the object form does.Suggested fix
Either (or both):
Accept strings as shorthand — normalize
phases: string[]tophases: {title: string}[]. Since phases are only a display outline,'Setup'and{title: 'Setup'}are equivalent, and this removes the most common first-call failure entirely:Improve the error message regardless — include the expected shape and the offending value so a model can self-correct in one step instead of guessing.
Environment:
pi-dynamic-workflowsv1.0.1.