Skip to content

Latest commit

 

History

History
87 lines (57 loc) · 8.34 KB

File metadata and controls

87 lines (57 loc) · 8.34 KB

Product Spec

What Is Executant

Executant is a CLI workflow runner for developers who use Claude Code. You define a workflow as a YAML file — a sequence of steps that are either AI prompts or bash commands — and executant runs them in order with a live TUI showing progress, output, and elapsed time.

Who It's For

Primary user: A developer who runs Claude Code regularly and wants to automate multi-step AI-assisted tasks. They know their tools, are comfortable with YAML and the terminal, and want to spend less time supervising repetitive workflows.

Representative use cases:

  • Convert a legacy codebase to TypeScript, validate the result, run tests
  • Generate a feature implementation from a spec file, then run lint + tests
  • Audit every file in a directory and emit a report

Design Principles

1. YAML as the interface. The workflow definition is a plain YAML file. No code required. Steps describe what to do, not how to orchestrate it.

2. Zero configuration. Sensible defaults for everything: self-healing is off by default (opt-in per step), judge retry limit is 5, tools default to the standard safe set. Override only when needed.

3. Transparent execution. The TUI shows every step's status, live output, and elapsed time. Nothing happens silently. CI mode (--ci) emits NDJSON for scripting.

4. Composable quality controls. Self-healing and LLM-as-judge are opt-in per step. They compose: a step can be self-healing and judge-evaluated.

5. Non-destructive on failure. If a workflow fails, the YAML file stays in tasks/todo/ so it can be retried unchanged. Completed runs move to tasks/done/ with a timestamp prefix.

Feature Set

Workflow Execution

  • Sequential step execution with fail-fast semantics
  • continue_on_error: true for non-critical steps
  • --step <name|index> to run a single step
  • --from-step <n> to resume from a step
  • --to-step <n> to stop after a step (combine with --from-step for a range)

Step Types

  • prompt — runs Claude with full tool access (or a restricted allowed_tools list)
  • script — runs bash directly (no AI cost)
  • log — emits a plain progress marker
  • forEach — repeats one or more child steps for each item in a list or shell command output; use a steps: array on the forEach step to run multiple child steps per iteration; repeat: N is shorthand for a forEach with a generated numeric list ["1"..."N"]{{item}} gives the 1-based iteration number in all child steps
  • workflow — runs another workflow (local file or URL) as a self-contained nested sub-run; its steps nest under this one step, and a vars: map passes overrides to the child. Every referenced workflow is fetched and validated before execution starts, so a bad reference anywhere in the chain fails fast. Not supported inside forEach/repeat.

Quality Controls

  • llm_as_judge — evaluates step output and retries on FAIL (up to 5x)
  • self_healing — auto-repairs failed script steps via Claude (up to 5x)
  • Failure retrospective — when a step ends the run, a post-mortem explains the root cause, cites the evidence, and evaluates the task file itself (var composition, iteration counts, missing verification, tool restrictions, timeouts). Judge and self-healing history is included, so a step killed by llm_as_judge is analysed against every verdict the judge gave rather than the bare attempt count. When the workflow is at fault, the TUI offers to apply the fix via refine. Disable with --no-retrospective or EXECUTANT_RETROSPECTIVE=0

Context Injection

  • vars — shared key/value pairs substituted as {{var_name}}
  • context — injects file contents into a prompt at runtime
  • output — captures a script step's stdout to a file

TUI Controls

  • i — interjection — opens a text input at the bottom of the screen. The typed message is queued and prepended as [User correction from a previous step] to the next Claude step's prompt. If a Claude step is currently running, the message waits for the next Claude step (the Claude CLI processes each invocation as a complete unit; mid-execution injection is not possible). If a script step is running, the message is similarly deferred. Press Esc to cancel without sending.
  • Output pane scroll and resize — the step list is always shown in full; the live output pane absorbs all the resizing. /k/PageUp and /j/PageDown scroll back through a step's output and return to following its live tail; [/] (or dragging the pane's bottom border with the mouse, where the terminal supports it) resizes the pane, and that size then stays fixed for the rest of the run instead of springing back to auto-sizing on the next step.
  • Context gauge — one line above the footer: the repo and branch the run is in, then how full the running session's context window is (executant main ━━━━━━━━━━ 81% 162.2k/200k), amber past 70% and red past 90%. It counts input + cache creation + cache read — the tokens that actually occupy a window — and is sized to that session's model (200k, or 1M for a [1m] model). Each prompt step is one claude -p session with its own window: the gauge fills as that session's conversation grows and resets when the next step opens a new one, never carrying context across steps or summing them. It reads each turn's own usage as it streams, not the CLI's end-of-step totals, which add every turn together and so describe throughput rather than occupancy. These are executant's numbers, not those of the Claude Code session that launched it: each prompt step is a separate claude -p child with its own window. Nothing is configured and nothing is polled — the gauge is derived from the event stream and moves the moment a step reports usage. EXECUTANT_STATUSLINE=0 hides it
  • q / Ctrl+C — abort the workflow immediately
  • Retrospective actions — after a failure, u updates the task file with the suggested changes, d dismisses, and o toggles between the analysis and the failing step's raw output; ↑↓ + Enter select. The update action is offered only when a local task file exists and changing it would have helped

Tooling

  • executant plan — generates a workflow YAML from a natural language description
  • executant refine — applies natural language instructions to an existing workflow YAML
  • executant update — upgrades to the latest version
  • --ci — headless mode, NDJSON event stream to stdout

Observability

  • OpenTelemetry export — set OTEL_EXPORTER_OTLP_ENDPOINT and every run exports one trace (a root span, a span per step, a span per forEach iteration — annotated with tool, self-healing, and judge activity plus API cost) and step-level metrics to an OTLP/HTTP collector
  • Trace context propagation — every subprocess a step spawns inherits TRACEPARENT, so instrumented tools inside your scripts join the run's trace
  • Run report — every successful run ends with wall-clock duration, total API cost, total tokens, and how much fell into Anthropic's >200k-token extended-context pricing tier (free — pure aggregation, always shown). A one-sentence efficiency idea for the task file is available on top of that, grounded in what actually happened during the run (judge retries, self-healing fixes) rather than just the YAML — but it's opt-in, not automatic: press a in the TUI once the report is on screen, or set EXECUTANT_REPORT_SUGGESTION=1 to have it generated every time. Either way it's a single Haiku call, never load-bearing (a timeout or bad response just omits the line), so an unattended or CI run is never disturbed by an API call it didn't ask for

This extends design principle 3 (transparent execution) beyond the terminal while honoring principle 2 (zero configuration): a single env var is the only switch, and when it is unset the OTel SDK is never even loaded. The exported data lives in your collector, not in executant — runs remain independent, with no persistent state between them.

Non-Goals

  • Parallel execution across top-level steps (steps are intentionally sequential; concurrency: N on forEach/repeat parallelizes iterations within one step, not the step sequence itself)
  • Multi-agent coordination (each step is a single Claude session)
  • Persistent state between runs (each run is independent)
  • A graphical UI (the TUI is terminal-only)