Human Steps
ImplementedSome pipelines need a person in the loop. A human should sign off before a risky deploy; a human should pick which of three draft emails to send; a human should answer a freeform question the model isn't qualified to answer on its own.
JigSpec models these as two first-class actions:
human/approve— pause and ask the human to approve or reject before continuing.human/input— pause and collect a value from the human (freeform or multi-choice).
Both actions make the pipeline stop cleanly, collect the human's response, write a structured output file to the step's workspace, and then hand control back to downstream steps as if nothing unusual happened.
Where they run
These actions work in two environments:
- CLI —
jigspec runreads from stdin. The pipeline prompts on the terminal. Requires an interactive TTY — if stdin is piped or the process is headless, the step fails with a clear error rather than silently auto-answering. - App / viewer —
jigspec appexposes an approval panel in the UI. The pipeline pauses; the human clicks a button in the browser; execution resumes.
The spec is identical between the two. Where the response is collected (terminal vs. browser) is a property of how the run was launched, not of the step itself.
human/approve
Gate downstream work behind explicit human sign-off.
- name: confirm_deploy
action: human/approve
message: "About to deploy v1.2.3 to production. Proceed?"
data:
version: "1.2.3"
target: "prod-cluster-east"
breaking_changes: 0
timeout: 300 # auto-reject after 300 seconds (optional)Fields:
| Field | Required | Description |
|---|---|---|
message | yes | Prompt shown to the human. |
data | no | Key/value map displayed alongside the prompt for context. |
timeout | no | Seconds before the step auto-rejects. If omitted, the step waits indefinitely. |
Output — written to response.json in the step's workspace:
{ "approved": true, "feedback": "Looks good, ship it" }Output references from downstream steps:
{{ confirm_deploy.approved }}— boolean (convenient forwhen: { file, value }gates){{ confirm_deploy.feedback }}— freeform follow-up text the user optionally provides
Downstream steps can gate on the approval:
- name: deploy
action: code
run: |
// actually ship it
when:
file: "{{ confirm_deploy['response.json'] }}"
value: '{"approved":true,"feedback":""}'(For cleaner gating, prefer reading a specific field — confirm_deploy also writes a plain approved.txt containing "true" or "false" that's easier to gate on.)
human/input
Collect a value from the human. Use this when you need text or a choice, not just a yes/no.
- name: pick_tone
action: human/input
message: "What tone should the email use?"
choices: [formal, friendly, urgent]
timeout: 120Omit choices for freeform text:
- name: name_the_release
action: human/input
message: "Give this release a codename:"Set multi: true to allow multiple selections:
- name: pick_recipients
action: human/input
message: "Who should this alert go to?"
choices: [ops, oncall, platform, security]
multi: trueFields:
| Field | Required | Description |
|---|---|---|
message | yes | Prompt shown to the human. |
choices | no | List of allowed values. When present, the response is validated to be one of them (or a subset, with multi: true). |
multi | no | When true, the human may select more than one choice from choices. Ignored if choices is absent. |
timeout | no | Seconds before the step fails with a timeout error. |
Output: value.txt in the step's workspace. With multi: true, the value is a JSON array of selected strings; otherwise it's the raw string.
Reference the value downstream:
- name: write_email
action: ai
prompt: |
Write a {{ pick_tone.text }}-toned email about the incident.Agent-initiated approval (advanced)
There is a separate mechanism — the requestApproval tool — that lets an ai agent step pause itself mid-execution to ask a human for approval. That is documented alongside the agent tools in AI Action — Agent mode. human/approve and human/input are the top-level step-scoped version; requestApproval is the inline, model-driven version. Use whichever fits the shape of your pipeline.
TTY required for CLI mode
If you run a pipeline containing human steps with jigspec run and stdin is not a TTY (for example, piping in JSON or running in CI), the step will fail with a clear "requires an interactive terminal" error. This is intentional — better to fail loudly than to let a headless run silently skip a human gate. For automated runs, either remove the human step or drive the pipeline through jigspec app with a browser responder.