Agent workflow specialized in developing, testing, debugging, and maintaining Scout workflows
WorkflowCoder extends ScoutCoder (which extends ComputerUse) to provide specialized tools for Scout workflow development. It introduces a development-oriented interface for executing workflow tasks, inspecting job state, validating inputs, and managing job caches -- all with structured output suitable for agent reasoning.
The key principle is that agents should almost never need to construct Ruby
one-liners or bash commands to test workflows. Instead, they use the
run_task tool, which loads the workflow, validates the task and its inputs,
executes it, and returns a structured JSON response with execution status,
output value, generated files, warnings, exception summaries, timing, job
directory, and log messages.
The workflow inherits all capabilities from ScoutCoder and ComputerUse:
- ComputerUse:
read,write,delete,patch,list_directory,file_stats,pwd,search,bash,ruby,python,r,playwright,html2md,html_query,docx2md,current_time,searxng, and more. - ScoutCoder:
help_list_repos,help_list_repo_documents,help_get_repo_document,help_overview,help_workflow,summarize_file,explain_code,explore_directory_structure,plan,implement.
When developing or debugging a Scout workflow, follow this iterative cycle:
- Discover: Use
list_tasksto see what tasks exist in a workflow and their input specifications. - Understand: Use
task_inputsto get detailed input specs including recursive inputs propagated from dependencies. - Test: Use
run_taskto execute a task with specific inputs. The structured output tells you the status, output, warnings, files, timing, and any exceptions -- all without writing Ruby code. - Inspect: Use
job_infoto check the status of a previously executed (or failed) job without re-running it. - Clean: Use
clean_jobto clear cached results before re-running after code changes.
Example workflow for testing a task:
# First, discover what's available
run_task(workflow="ComputerUse", task="current_time") # or just use list_tasks
# Get input specs for a specific task
task_inputs(workflow="ComputerUse", task="current_time")
# Run the task
run_task(workflow="ComputerUse", task="current_time", inputs={})
# Check job info without re-running
job_info(workflow="ComputerUse", task="current_time", inputs={})
Workflows will reload the workflow.rb file every time if specified in the
environment variable SCOUT_UPDATE_WORKFLOW or UPDATE_WORKFLOW is set to 'true'
or if the configuration variable update is 'true' for the token 'workflow' or
the name of the workflow. When this happens only the main files will be
updated, not any file required, if you are working on a task repeatedly keep it
defined in the workflow.rb file so that it gets updated; if you tuck it into a
required file it will be kept from reloading.
A Scout workflow is a Ruby module that extends Workflow. Tasks are declared
using the task DSL method. Here is a minimal example:
module MyWorkflow
extend Workflow
desc "Greet someone"
input :name, :string, "Name to greet", "World"
task :greet => :string do |name|
"Hello, #{name}!"
end
end
Key patterns:
- Input declaration:
input :name, :type, "description", default, optionswhere options can includerequired: trueorjobname: true. - Task types:
:string,:integer,:float,:boolean,:array,:json,:tsv,:file,:stream. - Dependencies: Use
depor inline dependencies in the task hash. - File tasks:
task :my_task => :file do ... endproduces a file on disk. - Extension:
task :my_task => :stringspecifies the return type.
For submitting and later controlling jobs (blocking, path-only, background
fork, or a no-run preview), see the "Job submission and control" section
under # Tasks.
- String vs symbol keys: Scout tasks expect symbol keys for inputs.
run_taskhandles this conversion automatically. - Cached results: If you change code but don't clean,
step.runreturns the cached result. Useclean: trueinrun_taskor useclean_job. - Missing required inputs: Scout raises
ParameterExceptionwhen required inputs are missing.run_taskcatches this and returns it as structured JSON witherror_phase: "input_validation". - Streaming tasks: Some tasks return streams. Use
stream: trueinrun_taskif the task supports streaming output. - Dependency resolution: Tasks with dependencies may need inputs that are
forwarded from upstream tasks. Use
task_inputswith the recursive_inputs field to see all accepted inputs.
- Always use
run_taskinstead ofrubyorbashto test workflow tasks. - Use
list_tasksbefore guessing task names. - Use
task_inputsto understand what inputs a task accepts. - Use
clean: truewhen re-running after code changes. - Read the
warningsarray inrun_taskoutput -- it catches typos in input names and missing required inputs before execution. - Check
error_phaseto understand where a failure occurred:load_workflow,task_validation,input_validation,job_creation,job_reference,execution, orjob_state(the last one, only fromjob_result, marks a state report about an unfinished job, not an exception). - Exceptions of the class ScoutException, such as ParameterException are considered controlled and understood, and are preferred for foreseeable errors. Otherwise they are considered uncontrolled and will be considered potentially recoverable by retrying the job.
Documentation files for Scout workflows are written in markdown and contain three broad parts:
- A one-liner description of the workflow as a whole.
- One or more paragraphs of description on the workflow and its
functionalities, and may include advice, installation
instructions, examples, etc. Header indicators like
#or##are allowed in this section. - A section describing the different tasks.
The third section, where tasks are described, follows a particular format. It begins with a new line like this:
# Tasks
Note the # character indicating it is a level 1 header.
This is followed by the different task descriptions. Each task description starts with the name of the task, a one-liner description of what it does, followed by one or more paragraphs of explanation on the task, including (if pertinent) relevant implementation details. Like this example:
## example_task
Perform an example task
If this were a real task here you would find the details.
There could also be a second paragraph.
In the task descriptions, avoid using header indicators like # or ## to
avoid confusing the parser. In the section containing the broad description of
the workflow, header indicators are allowed.
The job_path/short_path that preview_job, run_task and job_info
report are deterministic functions of the run, which is what makes running by
reference reliable:
- A job using only default inputs is labeled plain
Default; any non-default provided input (or any dependency) turns the label intoDefault_<md5>, where the hash covers the non-default inputs and the resolved dependencies. - File-valued inputs are digested by content, not by path: change the content of an input file and the label hash changes, moving the job to a new path even though the path string you passed is identical.
- The
.infofile of a job is written only when the job starts running, sopreview_jobreportinginfo_exists: falsesimply means the job was never run. short_pathis the three-segmentWorkflow/task/nameidentifier built from the job label, and it is whatjob_inforeports; Scout has nosort_pathconcept,short_pathis the accepted form of that identifier and the form accepted byrun_task --job.
There is one submission point -- run_task -- with four ways to use it, and a
family of control tasks that address an already-submitted job by reference
(full job path, Workflow/task/name short_path, or task/name plus the
workflow option: exactly the reference forms that running by reference
accepts).
Submission modes:
| Mode | Where | Blocks | Loads result | Returns | scout-camp equivalent |
|---|---|---|---|---|---|
wait_result |
run_task (default, no mode needed) |
yes | yes | full diagnostics + output |
initiate_step sync case, step.run (lib/scout/sinatra/base.rb:66-67) then serve_step loading the payload (base.rb:90-95) |
wait_path |
run_task --mode wait_path |
yes | no | diagnostics with output: null, output_type: "path_receipt" |
initiate_step sync run (base.rb:66-67) with the final step.join but no step.load (base.rb:76) |
fork |
run_task --mode fork |
no | no | diagnostics with submitted: true, pid, pid_alive |
initiate_step async case: step.fork unless step.started? (base.rb:68-69), Open.wait_for(step.path, timeout: 0.5) (base.rb:74) |
| preview | preview_job |
no | no | the would-be path; writes no .info, runs nothing |
none -- camp has no dry-run endpoint; this is a WorkflowCoder addition |
-
wait_resultis byte-for-byte the historical behavior and remains the default: omitmodeand nothing changes. -
wait_pathruns the job to completion in the caller's process but withstep.run(:no_load), so the result is never deserialized -- useful for tasks whose payload is huge or binary. On error it returns the structured error as usual and still includes the path. -
forkmirrors scout-camp's asynchronousinitiate_step(lib/scout/sinatra/base.rb:57-79in the scout-camp checkout): the job runs in a detached child via the engine's ownStep#fork, and the task returns in well under the task duration. The child survives the task returning:Step#fork(scout-gearscout/workflow/step.rb:291-309) detaches the child and the engine registers no at_exit hook for it. Poll withjob_status, fetch withjob_result. Note that a forked job that is killed by TERM records an:errorstate in its own info; see thejob_stopconvention below.Execution-environment caveat:
forkrelies on the detached child outliving the task process. Two facts bound this (seetmp/probe-verdicts.md): (1)scout workflow taskadds no sandbox of its own around the task process, and parent-exit survival of the detached child is probe-verified even when the task process itself runs inside a bwrap--die-with-parentPID-namespace sandbox; (2) the detached child shares its parent's PID namespace, so if that whole enclosing sandbox is torn down while the job is still running (teardown-on-exit), the kernel kills the background job with it and aforkreceipt would then refer to a job that dies shortly after. Full sandbox-teardown behavior is UNVERIFIED (no bypass was attempted); only parent-exit survival is probe-verified. Under a harness that tears down its sandbox on exit, usewait_pathorwait_resultinstead. -
preview_jobis the fourth mode: it derives the path without running anything at all.
mode composes with the job reference input (re-run a referenced job in any
mode) and with workflow/task/inputs as usual. An invalid mode value
returns the usual structured error with error_phase: "input_validation".
Ruby, all four modes (executed verbatim; outputs below):
require "scout"
Workflow.require_workflow "WFTestAI" # fixture workflow
preview = WorkflowCoder.job(:preview_job, nil, workflow: "WFTestAI",
task: "slow_task", inputs: {"seconds" => 3}).run
blocked = WorkflowCoder.job(:run_task, nil, workflow: "WFTestAI",
task: "slow_task", inputs: {"seconds" => 3},
mode: "wait_path").run
bg = WorkflowCoder.job(:run_task, nil, workflow: "WFTestAI",
task: "slow_task", inputs: {"seconds" => 3},
mode: "fork").run
CLI, fork and wait_path (executed verbatim):
scout workflow task WorkflowCoder run_task --workflow WFTestAI --task slow_task \
--inputs '{"seconds":3}' --mode fork
scout workflow task WorkflowCoder run_task --workflow WFTestAI --task slow_task \
--inputs '{"seconds":2}' --mode wait_path
Observed responses (from the runs above, abbreviated):
# fork -- returned in 0.7 s while the task sleeps 3 s
{"mode":"fork","status":"start","job_path":".../WFTestAI/slow_task/Default_0ace1017a4617e070870efea9721bf48",
"output":null,"submitted":true,"pid":21,"pid_alive":true,
"messages":["Forked: job runs in a detached child process; poll with job_status, fetch with job_result."]}
# wait_path -- blocked ~2 s, same path the preview predicted, no payload
{"mode":"wait_path","status":"done","execution_time":2.014,"output":null,
"output_type":"path_receipt","job_path":".../WFTestAI/slow_task/Default_2c00ffa16b5f007cb27754ca8b1a8f86"}
The control tasks never re-execute the job they address: reference resolution
goes through Step.load / Workflow#load_job, which produce a bare path-Step
with no task block, so nothing can run; status, result and files come straight
from the .info sidecar and the job file on disk. All of them are pure reads
except job_stop. Their wrapper caches are self-cleaned before each run, so
the same reference can be polled repeatedly and always returns the current
state of the underlying job.
List all tasks available in a workflow
Given a workflow name, this task loads the workflow using
Workflow.require_workflow, iterates over all declared tasks, and returns a
JSON array. Each entry contains the task name, description, result type,
declared inputs (with name, type, description, default, and required flag),
and declared dependencies.
This is the primary discovery tool. Use it before calling run_task to
confirm that the task name is correct and to see what inputs are expected.
Example output structure:
[
{
"name": "current_time",
"description": "Return current time as string",
"type": "string",
"inputs": [],
"dependencies": []
}
]
Get detailed input specifications for a single workflow task
Given a workflow name and a task name, this task returns a JSON object with
both the direct inputs (declared on the task itself) and the recursive inputs
(inputs propagated from upstream dependencies via dep declarations).
The recursive_inputs field is particularly useful for tasks that have dependencies, since Scout automatically forwards qualifying inputs to upstream tasks. Without checking recursive_inputs, you might miss inputs that the task actually accepts.
Example output structure:
{
"workflow": "ComputerUse",
"task": "read",
"description": "Read a file ...",
"result_type": "string",
"direct_inputs": [
{ "name": "path", "type": "string", "description": "...", "required": true }
],
"recursive_inputs": [...]
}
Execute a workflow task and return structured diagnostics
This is the primary development tool. It loads the target workflow, validates that the task exists, checks inputs for common mistakes (typos, missing required inputs), optionally cleans cached results, executes the task, and returns a comprehensive JSON object with all execution details.
Inputs:
workflow(string, required unlessjobis given): Name of the workflow to load.task(string, required unlessjobis given): Name of the task to run.inputs(text, default {}): Hash of input name to value pairs, given as a JSON string. Keys may be strings or symbols; they are converted to symbols automatically.clean(boolean, default false): Clean this job's cached result before running.recursive_clean(boolean, default false): Clean all dependency caches before running.stream(boolean, default false): Passtruetostep.runfor streaming tasks.mode(select, defaultwait_result): how to submit the job. One ofwait_result(block, run, return the result content -- the historical behavior),wait_path(block, run withrun(:no_load), return only the path, never the result payload), orfork(submit in a detached child process, return immediately with a path receipt). See the "Job submission and control" section above for the full picture and examples.job(string, optional): Reference to an existing job; when given, the recorded inputs are recovered from that job's.infofile and replayed, soworkflow,taskandinputsdo not need to be re-supplied. See the "Running by reference" paragraphs below.
The returned JSON contains:
status: Execution status (done, error, aborted, waiting, etc.)output: The return value of the task (serialized, truncated if very long)output_type: Type hint for the output (string, array, hash, nil, etc.)execution_time: Wall-clock seconds for the execution phasejob_path: Filesystem path to the job resultreferenced_job: Recovery echo, present only whenjobwas given (see below)files: Array of file paths generated in the job's files_dirwarnings: Non-fatal issues (unknown inputs, missing required inputs)messages: Log messages from step.infoexception_class: Ruby exception class name if an error occurredexception_message: Human-readable exception messagebacktrace_summary: First 10-15 lines of the backtrace for debuggingdependencies: List of dependency job names/pathserror_phase: Where the error occurred (load_workflow, task_validation, input_validation, job_creation, job_reference, execution, or job_state for the result-of-unfinished report ofjob_result)
The task never raises exceptions to the caller. All errors are captured and returned as structured JSON. This makes it safe for agents to call without worrying about unhandled exceptions.
Example usage:
run_task(
workflow="ComputerUse",
task="current_time",
inputs={}
)
Example response (abbreviated):
{
"status": "done",
"output": "2026-08-06 12:34:56 +0000",
"output_type": "string",
"execution_time": 0.001,
"files": [],
"warnings": []
}
Running by reference
Instead of re-supplying workflow, task and inputs, pass the job input
with a reference to a job that was already run at least once. run_task
resolves the reference, recovers the inputs recorded in the referenced job's
.info (info[:provided_inputs]), merges any inputs JSON over them, and
replays them through the normal pipeline. When the inputs round-trip
faithfully the run lands on exactly the same job path; if that job is already
done the cached result is returned without recomputing.
Three reference forms are accepted, tried in this order:
- Full job path: the
job_pathvalue returned byrun_task,job_infoorpreview_job. Absolute paths are used as-is; an existing relative path is accepted too. A trailing.infois tolerated. This is the most explicit form and the one to copy from a previous response. short_path: the three-segmentWorkflow/task/nameidentifier thatjob_inforeports in itsshort_pathfield (and thatpreview_jobalso returns). Note on terminology: Scout has nosort_path;short_pathis the same idea and is the accepted form here.task/name(two segments) together with theworkflowoption: resolved throughWorkflow#load_job. Useful when the workflow is already fixed and only the task and label need to be typed.
The examples below use a hypothetical MyWorkflow/greet job and are
illustrative: they cannot be executed verbatim because that workflow does not
exist in this checkout. The same three reference forms, executed against the
WFTestAI fixture, are verified in tmp/cli_doc_verify.sh (transcript in
tmp/readme_examples_check.out).
Example, run by full job path:
scout workflow task WorkflowCoder run_task \
--job /home/user/.scout/var/jobs/MyWorkflow/greet/Default_1a2b3c4d
Example, run by short_path:
scout workflow task WorkflowCoder run_task \
--job MyWorkflow/greet/Default_1a2b3c4d
Example, run by task/name plus workflow:
scout workflow task WorkflowCoder run_task \
--job greet/Default_1a2b3c4d --workflow MyWorkflow
Example, override one input on top of the recovered ones (overlay semantics:
entries in inputs win, untouched recovered entries are kept):
scout workflow task WorkflowCoder run_task \
--job MyWorkflow/greet/Default_1a2b3c4d \
--inputs '{"name": "World"}'
Programmatic equivalent:
WorkflowCoder.job(:run_task, nil,
job: 'MyWorkflow/greet/Default_1a2b3c4d'
).run
Every run by reference echoes what happened in the referenced_job block:
"referenced_job": {
"reference": "MyWorkflow/greet/Default_1a2b3c4d",
"kind": "short_path", // path | short_path | task_name
"resolved_path": "/home/user/.scout/var/jobs/MyWorkflow/greet/Default_1a2b3c4d",
"short_path": "MyWorkflow/greet/Default_1a2b3c4d",
"workflow": "MyWorkflow",
"task": "greet",
"recovered_inputs": { "name": "World" },
"merged_inputs": false
}
Inputs recorded in .info do not always round-trip perfectly: file and step
inputs serialize as paths, so rebuilding the job can land on a different job
path. That divergence is reported as a warnings entry, never as a failure,
and the remedy is the save_inputs / load_inputs bundle of the referenced
job (scout workflow task ... --save_inputs dir when producing it,
--load_inputs dir to reproduce it exactly).
A reference that cannot be resolved -- a typo, a job that was never run (no
.info exists), or task/name without the workflow option -- returns the
usual structured error JSON with error_phase: "job_reference" and a
ParameterException whose message lists every interpretation that was tried.
Reference resolution requires the job to have been run at least once, because
that is where the recovered inputs come from.
Report the status of a job by reference
Reads the .info sidecar and the payload file and returns a normalized status
plus the raw engine status, the job pid and its liveness, the started/ended
timestamps, log messages and, when the job errored, the recorded exception.
The normalization is the scout-camp serve_step status model
(lib/scout/sinatra/base.rb:81-100) extended with pid liveness: terminal
states (done/error/aborted) pass through verbatim; any state whose pid
is alive is running (exactly the engine's own running? predicate,
scout-gear info.rb:194-196); a payload file with no terminal status counts
as done. raw_status always carries the sidecar value (start is what a
forked child writes while it runs).
Inputs:
job(string, required): full job path,Workflow/task/name, ortask/nameplus theworkflowoption.workflow(string): only used to disambiguatetask/namereferences.
Never triggers execution. Executed verbatim:
scout workflow task WorkflowCoder job_status \
--job /home/mvazque2/.scout/var/jobs/WFTestAI/slow_task/Default_0ace1017a4617e070870efea9721bf48
Observed (a running fork):
{"status":"running","raw_status":"start","pid":21,"pid_alive":true,
"job_reference":{"kind":"path",
"resolved_path":"/home/mvazque2/.scout/var/jobs/WFTestAI/slow_task/Default_0ace1017a4617e070870efea9721bf48",
"short_path":"WFTestAI/slow_task/Default_0ace1017a4617e070870efea9721bf48"}}
Ruby equivalent:
WorkflowCoder.job(:job_status, nil,
job: "WFTestAI/slow_task/Default_0ace1017a4617e070870efea9721bf48").run
Stop a running job by reference
Terminates the job through the engine's own abort path: Step#abort (TERM +
waitpid through Misc.abort_child), then a brief liveness check of
info[:pid], escalating to Process.kill("KILL", pid) plus
Misc.wait_child only if the process survived the TERM, and finally a
step.merge_info(:status => :aborted, :end => Time.now) reconciliation.
CONVENTION: our stop always ends in :aborted. The engine itself only writes
:aborted from the streaming abort callback (scout-gear step.rb:272); a
TERM-killed forked child instead records :error with an Aborted exception
through the normal error path (step.rb:248-261), which is a recoverable
error and would make a deliberately stopped job look like a failed one. We
therefore normalize to :aborted so aborted? is truthfully true afterwards.
The merge uses merge_info, never reset_info/update_info: those replace
the whole info hash (info.rb:34-42, 144-148) and would drop the recorded
inputs and dependencies. scout-camp has no abort endpoint at all; this task is
engine-Step#abort based.
A job already done/error/aborted is reported as-is (action: "none")
instead of being killed.
The action field distinguishes what happened: aborted when a live job
was stopped, and none for the two no-op cases -- an already terminal job
(done/error/aborted, reported with its status) and a job that was
never started (a .info sidecar with a non-terminal status but no pid,
left untouched). The two no-ops differ only in their messages entry.
Aborting is not permanent: rerunning the same task with the same inputs
after a stop starts a fresh run and reaches done normally (verified in
tmp/readme_examples_check.out: stop -> action: aborted, immediate rerun
-> status: done). The reconciliation only edits the .info of the stopped
job; it does not poison the task or the inputs.
Inputs: same job / workflow pair as job_status.
Executed verbatim (fork a 90 s job, then stop it; 8 s is too short to be
reliable, since each CLI invocation pays several seconds of startup and the
job may already be done -- the stop then correctly a no-op -- by the time
job_stop runs):
scout workflow task WorkflowCoder run_task --workflow WFTestAI --task slow_task \
--inputs '{"seconds":90}' --mode fork
scout workflow task WorkflowCoder job_stop \
--job <job_path from the fork receipt>
Observed: the first job_status after the fork reported status: "running"
with pid_alive: true; job_stop returned status: "aborted",
escalated: false, pid_alive: false. A job_status immediately after the
stop may briefly still report the stale start/alive reading: the forked
child writes its own terminal state asynchronously while dying, and the two
writers race for the .info file. Within about half a second the state
settles and stays status: "aborted", raw_status: "aborted",
pid_alive: false (probe: tmp/readme_abort_settle.out).
Children caveat: TERM reaches the job pid only. Grandchildren started from
the task block with step.cmd / CMD.cmd are recorded in
info[:children_pids] and are NOT killed by job_stop; the framework has
no process-group kill. When such children are still alive after the stop,
the response carries children_pids_alive with their pids plus a warning,
so they can be killed separately if they must not outlive the job. (Probed
with a task that spawns a sleep through step.cmd; the response reported
the still-alive child -- tmp/verify_gap_check.out, gap 5.)
Ruby equivalent:
WorkflowCoder.job(:job_stop, nil,
job: "/home/mvazque2/.scout/var/jobs/WFTestAI/slow_task/Default_ffa5c6f46131596b3eabca031870b351").run
Load the result payload of a finished job by reference
Reads the job's stored payload from its job file (no task block involved, so
nothing can re-execute). A done job returns its content in output (truncated
to 5000 bytes with a warning when larger); an error job returns its recorded
exception; an aborted job reports that; a job still running or waiting
reports output_type: "in_progress" and error_phase: "job_state"
with the current status instead of blocking (a state report, not a failure).
Inputs: same job / workflow pair as job_status (job accepts the full
job path, Workflow/task/name, or task/name plus the workflow option).
Executed verbatim:
scout workflow task WorkflowCoder job_result \
--job /home/mvazque2/.scout/var/jobs/WFTestAI/slow_task/Default_0ace1017a4617e070870efea9721bf48
Observed:
{"status":"done","output":"slow_task done after 3.0s (requested 3s)",
"job_reference":{"kind":"path", ...}}
Ruby equivalent:
WorkflowCoder.job(:job_result, nil,
job: "WFTestAI/slow_task/Default_0ace1017a4617e070870efea9721bf48").run
List (or read) the files produced by a job
Lists every file in the job's files_dir with path, size and mtime. With the
optional file input, reads ONE file instead: the payload is returned bounded
by max_bytes (default 5000) and binary files are reported as binary rather
than dumped. Requesting a file that is not in this job's files_dir returns
error_phase: "input_validation".
Inputs: job / workflow as above, plus file (string, optional) and
max_bytes (integer, default 5000).
Executed verbatim (a task that wrote two files into its files_dir):
scout workflow task WorkflowCoder job_files \
--job /home/mvazque2/.scout/var/jobs/WFTestAI/files_task/Default
Observed:
{"status":"done","files":[
{"path":".../WFTestAI/files_task/Default.files/binary.bin",
"name":"binary.bin","size":64,"mtime":"2026-09-04 09:28:40 +0200"},
{"path":".../WFTestAI/files_task/Default.files/report.txt",
"name":"report.txt","size":2400,"mtime":"2026-09-04 09:28:40 +0200"}]}
Reading one file, bounded (Ruby equivalent):
WorkflowCoder.job(:job_files, nil,
job: "WFTestAI/files_task/Default",
file: "report.txt", max_bytes: 40).run
Observed:
{"status":"done","file":"report.txt",
"content":"line1\nline2\nline1\nline2\nline1\nline2\nline1...(truncated)",
"warnings":["File 'report.txt' (2400 bytes) truncated to 40 bytes."]}
A binary file is reported, not dumped ("binary": true with its size, the
content key stays null); a name not present in this job's files_dir returns
error_phase: "input_validation" with a ParameterException that lists the
available files. Full transcript: tmp/probe_files.out.
Clean cached results for a workflow task
Given a workflow name, task name, and optional inputs, this task creates the
job (without running it) and cleans its cached result. With recursive: true,
it also cleans all dependency results.
Use this before re-running a task after modifying its code, or to free disk space from large intermediate results.
Example usage:
clean_job(
workflow="ComputerUse",
task="current_time",
inputs={}
)
Retrieve structured info about a job without running it
Given a workflow name, task name, and optional inputs, this task creates the
job (without running it) and reads its step.info metadata. This is useful
for checking the status of a previously executed job, examining messages,
checking timing, or inspecting exception details -- all without triggering a
new execution.
The returned JSON contains:
status: Current job status from step.infomessages: Log messages recorded during previous executiondependencies: Dependency job names/pathsexception: Exception details if the job previously erroredtime_started: When the job was started (if ever)time_done: When the job completed (if ever)job_path: Filesystem path to the job resultshort_path: CompactWorkflow/task/nameidentifier of the job; this is the value to pass as thejobinput ofrun_taskwhen running by referencefiles: Files in the job's files_dir (if any)
Example usage:
job_info(
workflow="ComputerUse",
task="current_time",
inputs={}
)
Preview the job path a task run would get, without running it
This is the dry-run companion of run_task: it goes through the exact same
phases (load workflow, task validation, input validation, job creation) with
the same warnings and the same error_phase taxonomy, but stops after the
job is created. The job is NOT executed: no run/exec/persist is issued,
no .info file is written, and no cache entry is touched, so previewing has
no effect on the job's state or on a later run.
Inputs:
workflow(string, required): Name of the workflow to load.task(string, required): Name of the task to preview.inputs(text, default {}): Hash of input name to value pairs, given as a JSON string.
The returned JSON contains:
job_path: Full filesystem path the job result would occupyshort_path: CompactWorkflow/task/nameidentifier of the jobnameandclean_name: The job label and its label without the hash suffix (see the job identity notes in the introduction)files_dir: Directory where the job's produced files would livenon_default_inputs: The inputs that made the job label non-defaultdependencies: short_paths of the jobs this run would depend oninfo_existsandresult_exists: Whether this exact job was already run beforestatus: Read from the existing.infowhen present,not_runotherwiseexecuted: Always false; a marker that the preview never ran anythingwarnings,messages,exception_class,exception_message,backtrace_summary,error_phase: same diagnostics contract asrun_task
Use it to check where a run will land before committing to it (for instance
before a long computation), to see whether an equivalent job already exists
and in what state, or to validate inputs without any side effect: input
mistakes surface here with the same warnings and error_phase they would
produce in run_task.
Example usage:
preview_job(
workflow="ComputerUse",
task="read",
inputs={"path": "workflow.rb"}
)
Same call on the command line:
scout workflow task WorkflowCoder preview_job \
--workflow ComputerUse --task read \
--inputs '{"path": "workflow.rb"}'
Example response (abbreviated):
{
"status": "not_run",
"executed": false,
"job_path": "/home/user/.scout/var/jobs/ComputerUse/read/Default_5f4e3d2c",
"short_path": "ComputerUse/read/Default_5f4e3d2c",
"name": "Default_5f4e3d2c",
"clean_name": "Default",
"non_default_inputs": ["path"],
"info_exists": false,
"result_exists": false
}
Note that preview_job addresses a job by its (would-be) inputs. To inspect
or re-run a job you already have a reference to, use job_info /
run_task --job instead; for the four-way submission picture (wait_result /
wait_path / fork / preview) see "Job submission and control" above.