mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-08-28 05:27:41 +00:00
* arc(01KK7524KNGTPS4090QMF87FJN): implement (success) Arc-Run: 01KK7524KNGTPS4090QMF87FJN Arc-Completed: 2 Arc-Checkpoint: 1ff03c704805dfe8e7c37b37f97bd06dfa0e5dc5 * Fix: restore trailing newlines stripped by previous commit * arc(01KK7524KNGTPS4090QMF87FJN): simplify (success) Arc-Run: 01KK7524KNGTPS4090QMF87FJN Arc-Completed: 3 Arc-Checkpoint: 21771adfd26283a1d1e6b8a123a83a0c4277db48 --------- Co-authored-by: arc <arc@local> Co-authored-by: Arc Assistant <assistant@arc.dev> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
115 lines
5.1 KiB
Text
115 lines
5.1 KiB
Text
---
|
|
title: "Interviews"
|
|
description: "How Arc collects human input during workflow execution"
|
|
---
|
|
|
|
When a workflow reaches a [human gate](/workflows/human-in-the-loop), it needs to pause and wait for a person to respond. The **interviewer** is the abstraction that makes this work — it presents a question, collects an answer, and returns it to the engine so execution can continue.
|
|
|
|
Arc ships with several interviewer implementations for different environments: an interactive terminal prompt for the CLI, a web-based queue for the API server and web UI, an auto-approve mode for CI, and record/replay support for testing.
|
|
|
|
## Question types
|
|
|
|
Every human interaction is modeled as a `Question` with a type that determines how it's presented:
|
|
|
|
| Type | Description | CLI presentation |
|
|
|---|---|---|
|
|
| `YesNo` | Binary yes/no decision | `[Y/N]` prompt |
|
|
| `Confirmation` | Confirm an action (like YesNo) | `[Y/N]` prompt |
|
|
| `MultipleChoice` | Pick one option from a list | Arrow-key selector or numbered list |
|
|
| `MultiSelect` | Pick one or more from a list | Checkbox selector |
|
|
| `Freeform` | Open-ended text input | `>` prompt |
|
|
|
|
### Question structure
|
|
|
|
Each question carries metadata beyond the prompt text:
|
|
|
|
| Field | Description |
|
|
|---|---|
|
|
| `text` | The question displayed to the user |
|
|
| `question_type` | One of the types above |
|
|
| `options` | List of `{key, label}` pairs for choice questions |
|
|
| `allow_freeform` | Whether free-text input is accepted in addition to fixed options |
|
|
| `default` | Default answer used on timeout |
|
|
| `timeout_seconds` | How long to wait before using the default or timing out |
|
|
| `stage` | The node ID that generated this question |
|
|
| `metadata` | Arbitrary key-value metadata for integrations |
|
|
|
|
## Answer values
|
|
|
|
Answers are one of six variants:
|
|
|
|
| Value | Meaning |
|
|
|---|---|
|
|
| `Yes` | Affirmative response to a yes/no or confirmation question |
|
|
| `No` | Negative response |
|
|
| `Selected(key)` | A specific option was chosen (carries the option key) |
|
|
| `Text(string)` | Free-text input |
|
|
| `Skipped` | The user dismissed the question without answering |
|
|
| `Timeout` | The question's timeout elapsed without a response |
|
|
|
|
An answer can also carry a `selected_option` (the full `{key, label}` pair) and a `text` field for freeform input.
|
|
|
|
## How human gates build questions
|
|
|
|
When the engine reaches a human gate node (`shape=hexagon`), the [human handler](/workflows/human-in-the-loop) builds a question from the node's outgoing edges:
|
|
|
|
1. Each edge becomes an option, with the accelerator key parsed from the label (e.g. `[A] Approve` → key `A`, label `[A] Approve`)
|
|
2. Edges with `freeform=true` are excluded from the option list and enable free-text fallback
|
|
3. The question text comes from the node's `label` attribute
|
|
|
|
The handler then passes the question to the interviewer, waits for an answer, and maps it back to an edge for [transition](/workflows/transitions#human-gate-transitions).
|
|
|
|
## Channels
|
|
|
|
The `Interviewer` trait has a simple interface — `ask(question) → answer` — and Arc provides implementations for each delivery channel:
|
|
|
|
### Console
|
|
|
|
The default for CLI runs. On a TTY, the console interviewer uses interactive widgets (arrow-key selection, checkbox multi-select, confirm prompts) via `dialoguer`. When stdin is piped (non-TTY), it falls back to a line-based reader with numbered options.
|
|
|
|
```bash
|
|
arc run workflow.dot
|
|
# At a human gate:
|
|
# ? Approve Plan
|
|
# [1] A - [A] Approve
|
|
# [2] R - [R] Revise
|
|
# Select:
|
|
```
|
|
|
|
### Web
|
|
|
|
The default for API server runs. The web interviewer holds questions in a queue until answers are submitted externally — typically by the web UI or a REST API call. Each question gets a unique ID (e.g. `q-1`), and the `ask()` call blocks on a oneshot channel until `submit_answer(id, answer)` is called.
|
|
|
|
This decoupling means the workflow engine and the user interface can run in different processes. The web UI polls for pending questions and posts answers back to the API.
|
|
|
|
### Slack
|
|
|
|
Arc's Slack integration uses the web interviewer under the hood. When a human gate fires, the pending question is rendered as a Slack message with interactive buttons. When a user clicks a button, the Slack event handler calls `submit_answer()` on the web interviewer, unblocking the workflow.
|
|
|
|
### Auto-approve
|
|
|
|
For fully automated runs or CI pipelines, the auto-approve interviewer answers every question without human input:
|
|
|
|
- `YesNo` / `Confirmation` → `Yes`
|
|
- `MultipleChoice` / `MultiSelect` → first option
|
|
- `Freeform` → `"auto-approved"`
|
|
|
|
Enable it with the `--auto-approve` flag:
|
|
|
|
```bash
|
|
arc run workflow.dot --auto-approve
|
|
```
|
|
|
|
## Timeouts
|
|
|
|
Questions can have a `timeout_seconds` field. When set, Arc wraps the interviewer call with a timeout:
|
|
|
|
- If the user answers before the deadline, their answer is used normally
|
|
- If the timeout elapses and a `default` answer is set on the question, the default is used
|
|
- If the timeout elapses with no default, the answer is `Timeout`
|
|
|
|
The human handler then checks the node's `human.default_choice` attribute. If set, execution continues to the default target. Otherwise, the stage retries.
|
|
|
|
```dot
|
|
approve [shape=hexagon, label="Approve?", human.default_choice="deploy"]
|
|
```
|