Critique: workflow-execution-infographic.png¶
This critique reviews the infographic embedded immediately under the opening paragraph of docs/workflow_spec.md (and
above ## Table of Contents). The image is a 1672×941 16:9 landscape diagram with three vertical zones: a left column
of two source blocks (Inputs, Environment); a center execution lane of six rows labeled agent, bash, python,
hidden, parallel, HITL, surrounded top-and-bottom by thin bands labeled if, for, while, repeat/until; a
floating prompt_part document fragment that arrows into the agent row; and a right zone with a "Parallel Branches"
column (Branch A–D), a "Join (merge results)" panel listing object, array, text, lastOf, and a single "Continue
Workflow" exit arrow.
The original prompt is preserved in workflow-execution-infographic.prompt.md; deterministic ImageMagick labels were
composited on top of a no-text background to lock left-column terminology to the spec. This critique is grounded in the
current docs/workflow_spec.md and the shared docs/images/infographic-style-brief.md.
Summary¶
The diagram correctly captures the headline shape — inputs and environment flowing into an ordered step lane with
control wrappers, parallel fan-out, four join modes, and a HITL gate — and the deterministic left-column relabel keeps
the input/environment terminology aligned with the spec. The biggest semantic problem is the center lane treats
hidden and HITL as peer execution types alongside agent/bash/python/parallel, which directly contradicts
the prompt sidecar's own guidance ("Make HITL read as a hitl: true human approval gate … not as a standalone execution
type") and the spec's modifier semantics for hidden: true. Several visual choices also imply unsupported or misleading
nesting, drop the finally: cleanup concept entirely, and label step outputs with awkward composite strings (e.g.
output_agent_out, output_hidden_out) instead of the spec's {{ step.field }} model.
Clarity issues a new user would hit¶
hiddenandHITLlook like execution types. A first-time reader sees a six-row vertical lane of equally weighted icons —agent,bash,python,hidden,parallel,HITL— and concludes there are six step types. The spec is explicit thathidden: trueis a per-step modifier ("Any step can be markedhidden: true") and thathitl: trueis a directive that can attach toagent,bash, orpythonsteps. Treating them as their own rows in the same column as the four real execution types is the central clarity bug of the diagram.- Control wrappers wrap the entire lane, suggesting global scope. The
if/for/while/repeat-untilthin bands stretch from the top to the bottom of the central column, encompassingagent/bash/python/hidden/parallel/HITLsimultaneously. This visually implies that the four control wrappers apply to the whole workflow at once, when in reality each wrapper is a per-step field on a single step (if:andfor:can combine;while:andrepeat:are mutually exclusive on a step). A new reader cannot infer "I attachfor:to one bash step", they read "I wrap the whole sequence in aforloop". prompt_partis shown injecting into the workflow's own agent row. The arrow from the floatingprompt_partdocument fragment terminates at the in-laneagentstep. The spec's actual semantic is thatprompt_partis expanded inline into the calling/containing prompt at the#name(args)reference site — not necessarily into any agent step within the same workflow. A new reader walks away thinkingprompt_partis a way to prepend text to a downstreamagentstep in this workflow, missing the load-bearing case where a.mdxprompt is implicitly a singleprompt_partstep expanded at the user's call site.- HITL pause does not name the user's actions. The HITL row's annotation reads "Pause for review and approval" but
the spec defines three distinct user actions on the gate — Accept, Edit (modify the output), and Reject
(abort the workflow) — and a downstream
step.approvedfield forbash/pythonHITL steps. Without these labels, a new reader cannot distinguish HITL from an unconditional pause. - Output arrows are labeled with composite junk strings. The arrows leaving each row carry the literal labels
output_agent_out,output,output,output_hidden_out, plus an "artifacts (optional)" tag on bash/python.output_agent_outandoutput_hidden_outare not real terms in the spec; they read like a templating accident ("output_<row>_out") rather than deterministic post-processing. The spec's mental model is the named-output schema (output: { field: type }) referenced downstream as{{ step_name.field }}; none of that is depicted. - The "Continue Workflow" exit arrow lives only on the parallel/join column. The single rightmost arrow leaves from
the join panel, with no equivalent on the linear lane, suggesting that "continuing the workflow" is a property of
parallelrather than the default control flow. Parallel is just a step type; the workflow continues after every step. - Inputs and Environment merge visually but are described separately in the spec. The two left blocks are stacked
with similar styling and a shared down-arrow into the lane. The spec keeps a sharper line:
inputis typed user parameters validated at invocation, whileenvironmentis Jinja2-rendered key/value strings injected intoos.environfor the whole session. A reader might infer they are interchangeable channels into the same template context, which under-counts environment's process-env side effect. - No depiction of cleanup /
finally: true. The spec dedicates a whole section tofinally: truecleanup steps that run even after failures or HITL rejection. The infographic has no equivalent visual — no "always-runs" lane, no teardown badge, no rejection path off the HITL gate. A new reader cannot answer "what happens to myrm -rf /tmp/xstep if the agent before it fails?" from this picture. - Parallel-branch restrictions are invisible. The right-side "Parallel Branches" depicts four arbitrary nested
branches (A–D) without signaling that nested steps inside
parallel:cannot usefor/repeat/while, cannot nest anotherparallel, and cannot behitl: true. Combined with point (2), the diagram suggests a reader can wrap the parallel block in aforloop and nest HITL inside parallel branches; the first is allowed, the second is not. - Label hygiene at GitHub width. At the embedded width, the four control-wrapper bands plus the right join panel
plus the parallel column compete for horizontal space. The bottom
repeat/untilandforbands run very close to the HITL row's pause caption; theContinue Workflowarrow head overlaps the "lastOf" entry in the join panel. There is little slack on a narrow theme or smaller viewport.
Accuracy errors against current spec¶
These are checked against docs/workflow_spec.md (the embedding doc) and the shared infographic-style-brief.md.
hiddenis a modifier, not an execution type.workflow_spec.md§"Hidden Steps" says: "Any step can be markedhidden: trueto suppress it from the ACE TUI Agents tab. Hidden steps execute normally but don't appear as visible agents." The brief enumerateshiddenin its step-type list, but the brief is itself imprecise here; the spec is authoritative. Showinghiddenas a distinct row with its own output arrow is wrong against the spec's semantics.- HITL is a directive (
hitl: true), not an execution type. The spec puts HITL under "Human-in-the-Loop" as a directive that pauses execution after anagent,bash, orpythonstep. The prompt sidecar explicitly instructed "not as a standalone execution type"; the rendered image violates its own brief by giving HITL a peer row in the execution lane. - Five real execution types, not six rows. The spec lists exactly five execution types as mutually exclusive on a
single step:
agent,prompt_part,bash,python,parallel. The image's central column shows four of those (agent,bash,python,parallel) plus two non-types (hidden,HITL).prompt_partis the fifth real type and is depicted only as a floating fragment, not as a peer in the execution lane. prompt_partinjection target is wrong. The spec: "When a workflow is referenced via#name(args), theprompt_partcontent is expanded inline into the calling prompt." The arrow in the image goes fromprompt_partinto the workflow's ownagentrow, which mis-locates the injection. The standalone case (a.mdxprompt is internally a singleprompt_partstep expanded into the user's prompt) is not depictable from the current arrow.artifact: stdoutis conflated withoutput. The spec separates two output channels: structuredoutput: { field: type }(parsed via JSON /key=value/ text fallback) andartifact: stdout(captures stdout to{artifacts_dir}/{step_name}.stdoutand exposes{{ step_name._artifact }}). The image's "artifacts (optional)" tag on bash/python is the only nod, and it sits next tooutputwithout distinguishing the file-path vs. structured-fields semantic. The_artifactreference path is not surfaced anywhere.- Cross-step references via named outputs are not visualised. The spec's data-flow model is
{{ step_name.field }}lookups against an output schema, validated at load time (§"Cross-Step Field Type Checking"). The image shows anonymous output arrows leaving each row with no named-field references between rows, so a reader cannot derive how step B reads step A's data. finally: truecleanup is missing entirely. The spec dedicates a section tofinally: truesemantics (still runs after failure or HITL-reject; must be placed after all non-finally steps; can combine withif:). No element of the diagram represents this. The brief is silent onfinally:, but it is a documented top-level concept on par with control flow and HITL.use:step imports are not visualised. The spec §"Step Imports" defines ause: shared/foosyntax that pulls a step definition fromsteps/directories with override semantics. This is a real, top-level reuse mechanism in the workflow format. The image shows no symbol for it. Acceptable simplification only if the omission is consistent — right now it sits next toprompt_part(which IS shown), creating an asymmetry.on_error: stop/continueforfor:loops is missing. The spec §"For-Loop Error Handling" calls out per-step-type defaults (agent→ continue,bash/python→ stop) and an explicit override. The diagram'sforwrapper has no failure-path branch to represent this; readers usingfor:over agent steps in production will wonder where the "skip-and-continue" semantic comes from.- Join modes are correctly listed but un-anchored to context. The four labels (
object,array,text,lastOf) match the spec's table. But the spec also encodes which mode is the default for which producer (object→ default forparallel:,array→ default forfor:); the diagram lists them as a flat menu, missing the default-binding that determines what users get when they omitjoin:. - Parallel join is shown as the workflow's terminus. The "Continue Workflow" arrow leaves the join panel as if the
workflow ends there. In the spec,
parallelis just one step type — its result feeds downstream steps via{{ parallel_step.nested.field }}exactly like any other step. The terminal arrow placement misframes the role of the parallel block. - No depiction of
hitlpost-state (step.approved). The spec: "After an acceptedbashorpythonHITL step,step.approvedis set totruefor downstream conditions". This is a load-bearing pattern for "ask for permission then act" workflows, and the diagram has no symbol for theapprovedfield flowing forward. - Prompt sidecar QA note (Phase 7) and current image disagree on HITL framing. The sidecar's post-processing
section claims the HITL framing was audited as a
hitl: truehuman approval gate "not as a standalone execution type", but the rendered image shows HITL as a row in the execution lane. Either the audit pre-dated the most recent regen, or the audit accepted the row-style depiction; in either case the documented intent and the rendered visual are out of sync.
Concrete suggested changes for the regenerated image¶
These are change requests the regen agent should pass into its GPT image prompt and/or its post-processing label script, scoped to the diagram only.
- Cut the central lane from six rows to five execution types in this order:
agent,prompt_part,bash,python,parallel.prompt_partbecomes a peer row (still styled as a "text fragment" icon, not an LLM/process icon) so all five mutually-exclusive types are visible together. - Render
hiddenas a small badge ("hidden: true — suppressed from ACE TUI") attached to one example row (e.g. thepythonrow), demonstrating that any step can carry the modifier rather than depicting it as its own row. - Render
HITLas an approval gate symbol straddling the boundary between a step row and its downstream arrow — ideally a small "pause" diamond hooked onto theagent,bash, andpythonrows (one symbol, three dotted anchors) with the user-action labels Accept / Edit / Reject spelled out, plus anapproved → downstreamribbon for thebash/pythonpost-state. - Move the four control wrappers (
if,for,while,repeat/until) from full-lane bands to per-step brackets. Show one example bracket on a single representative step (e.g.if:onagent,for:onbash,while:/repeat:onpython) so readers see "modifier on one step" instead of "wrapper around the whole workflow". Note in a small caption thatwhile:andrepeat:are mutually exclusive on a step. - Re-target the
prompt_partarrow. Instead of arrowing into the workflow's ownagentrow, draw it leaving the workflow boundary toward an external "calling prompt" tile labeled#workflow_name(args). Add a small caption: "expanded inline into the calling prompt at the#namereference site". - Replace
output_agent_out/output_hidden_outarrow labels with the spec's named-output model: each step's exit arrow should carryoutput: { field: type }and a small{{ step.field }}example flowing into the next step. Introduce a separateartifact: stdout → {{ step._artifact }}arrow onbash/pythonto distinguish structured outputs from file-captured stdout. - Add a
finally:lane as a horizontal strip below the main lane with one or two example "always-runs" cleanup steps and a dotted "runs even after failure or HITL-reject" connector from the failure paths. - Add a small
use:import badge on one example step (or dropprompt_part's peer treatment for symmetry — either bothprompt_part-as-step-type anduse:-as-import are shown, or both are omitted; the current asymmetry is the bug). - Drop the terminal "Continue Workflow" arrow from the join panel and instead route the joined parallel result into
a downstream "next step" tile that consumes
{{ parallel_step.branch.field }}, mirroring how every other step feeds forward. - Annotate join modes with their default bindings. Next to each of the four modes show the producer they default
to:
object→ default forparallel:,array→ default forfor:,textandlastOf→ opt-in. This is the information that determines user behavior whenjoin:is omitted. - Surface the parallel-nesting restrictions as a small caption inside the "Parallel Branches" column: "nested
steps cannot use
for/while/repeat, cannot nestparallel, and cannot behitl: true". This forecloses the most common misreads. - Clarify Inputs vs Environment with a one-line caption per block:
Inputs→ "typed user-supplied parameters, validated at invocation";Environment→ "Jinja2-rendered key/value strings, injected intoos.environfor the session". Keep them visually adjacent but clearly distinct, not stacked-as-one source. - Update the prompt sidecar's Phase-7 QA note if the HITL row stays as a row in any future regen — currently the sidecar's claim ("not as a standalone execution type") and the rendered image disagree. The audit log should match the rendered artifact.
Out of scope for this critique¶
- The PNG, the prompt sidecar, and
docs/workflow_spec.mditself are unchanged by this phase per the epic plan insdd/epics/202605/diagram_review.md. Phase 19 (sase-2s.19, "Regenerate diagram: workflow-execution") owns the regenerated image, the updated prompt sidecar if needed, and any related label-composition tooling. - Style consistency across all infographics in the diagram-review epic is the regen phase's concern; this critique only addresses what the workflow-execution diagram alone gets right and wrong against its embedding doc and shared style brief.