An agent that designs and runs a workflow toward a goal, one step at a time. It is built on behalf, and the workflow tool that runs the steps is a provider: Snakemake is the first, and others plug in behind the same interface. It replaces the snakemake provider on the add-snakemake branch of resource-secretary (PR #5), which was driven through mcp-server and fractale.
Every run, whatever the provider, uses the same work directory:
WORK_DIR/
├── input/ input data, symlinked (read only for the agent)
├── steps/ one directory per executed step, NN_stepname/
└── logs/ one log per step
The agent reads the environment, lists the inputs, plans, and executes steps with the provider's tools. A step that fails is removed automatically. The run ends when the agent calls finish. The provider adds whatever its tool needs next to these directories; Snakemake keeps a Snakefile that is rebuilt after every step.
pip install -e ".[gemini,workflow]" # or claude / awsThe workflow extra installs Snakemake. Running a Snakemake workflow also needs git and conda or mamba on PATH. Miniforge provides both.
# once per provider: fetch and index what it needs. For snakemake, the wrappers
# at a release tag (cached in ~/.cache/workflow-secretary)
workflow-secretary prepare --provider snakemake --wrappers-version v9.19.0
# tutorial data for plans/variant-calling.yaml
curl -sSL -o tutorial.tar.gz https://github.com/snakemake/snakemake-tutorial-data/archive/v5.4.5.tar.gz
tar --wildcards -xf tutorial.tar.gz --strip 1 "*/data"
workflow-secretary run \
--plan plans/variant-calling.yaml \
--input data \
--workdir work/run-01 \
--provider snakemake \
--backend gemini --model gemini-2.5-pro \
--conda-prefix ~/conda-envsThe exit code is 0 when the agent finishes with status: complete, and 2 otherwise. Flags shared by every provider:
--provider: the workflow tool (defaultsnakemake, orWORKFLOW_SECRETARY_PROVIDER).--goal: give the goal text instead of a plan.--max-calls: the tool call budget (default 100).--confirm: approve each action tool call interactively.--backendand--model: the behalf runner (gemini,claude,aws) orscript.
Each provider adds its own group to workflow-secretary run --help. Snakemake's:
--wrappersand--wrappers-version: the catalog checkout (default: the user cache at the pinned version).--cores: cores given to snakemake.--conda-prefix: share conda environments across runs, so repeated runs don't rebuild them.--conda-frontend: only passed to snakemake when given. Snakemake 9 ignores it with a warning, which would otherwise appear in every result the agent reads.
--backend script --script FILE replays a fixed list of tool calls instead of calling a model. The tests use it, and it is useful for checking a workflow by hand. tests/scripts/variant-calling.yaml replays the whole variant-calling plan for samples A, B and C with a sample sheet:
workflow-secretary run \
--plan plans/variant-calling.yaml \
--input data \
--workdir work/scripted-01 \
--backend script --script tests/scripts/variant-calling.yaml \
--conda-prefix ~/conda-envsThe exit code follows the script's finish call, so check attempts in the record to see whether every step actually ran.
Every provider gets these from the task:
| Tool | Kind | Purpose |
|---|---|---|
get_environment |
read | layout, path conventions, provider details, history |
list_input_dir |
read | staged inputs |
list_work_dir |
read | step outputs and history |
finish |
read | final report: complete or incomplete, with summary, issues, and reason |
The Snakemake provider adds:
| Tool | Kind | Purpose |
|---|---|---|
search_wrappers |
read | keyword search of the pinned snakemake-wrappers catalog |
get_wrapper_details |
read | README, packages, example rule |
view_snakefile |
read | current Snakefile |
create_sample_sheet |
action | input/samples.csv, plus SAMPLES and samples_df in the Snakefile |
execute_wrapper |
action | append and run a wrapper rule |
execute_rule |
action | append and run a custom shell rule, optionally with conda_packages |
delete_rule |
action | remove a successful rule by name |
Tool arguments follow behalf's flat schema, so every argument is required. An empty value means "not set": {} for params, [] for packages.
A provider is a subclass of WorkflowProvider in workflowsecretary/providers/base.py, registered by name in workflowsecretary/providers/__init__.py. The base class owns everything the agent's view of a run has in common, so a provider only renders and runs its own steps:
- the layout:
setup()stages inputs,input_root,steps_dir,logs_dir,next_step_dir(name),remove_step_dir(dir) - path handling:
resolve_inputs,resolve_outputs,check_names, andcheck_paths, which refuses reads outsideWORK_DIRand writes outsidesteps/ - execution:
run(cmd, **attempt)runs a command in the work directory, appends toattempts, and returns the response every execute tool gives the agent (stdout and stderr tails, a listing ofsteps/) - the common tools above
A provider implements:
| Member | Purpose |
|---|---|
name |
registry key, and the tool named in the prompt |
history |
ordered steps in the workflow: [{step, name, step_dir}, ...] |
tools() |
its Tools: name, description, flat schema, a function of the arguments, and kind (read or action) |
instructions() |
its section of the system prompt: how its tools fit the shared strategy, naming rules, what the agent must know |
environment() |
extra fields for get_environment, e.g. a catalog version |
versions() |
versions of everything that can change a run, for the record |
artifacts() |
files to copy into the run record, e.g. {"Snakefile": path} |
add_arguments(parser) and from_args(args, workdir, input_dir) |
its run options and construction |
add_prepare_arguments(parser) and prepare(args) |
its prepare options and one-time setup |
The system prompt is shared: persona, layout, path conventions, permissions, the one-step-at-a-time strategy, and the constraints are the same for every provider, and the provider's instructions() fill one section. tests/test_providers.py has a complete provider in about sixty lines, one that runs each step as a shell command, and runs the task, the scripted runner and the recorder over it. That is the template for a new one.
Each run writes runs/<run-id>/ containing:
run.json, with these fields:goal,planandproviderversions: workflow-secretary, behalf, python, runner and model, plus the provider's (for snakemake: snakemake, the wrappers tag and commit, and the conda frontend)statusandfinish: the agent's reportsteps: the steps in the final workflowattempts: every execution, with return code and timeoutputs: every file understeps/, with size and sha256calls: every tool call, with arguments and result
- the provider's artifacts, for snakemake the
Snakefilethe agent built.
status is the agent's own report. attempts and outputs come from the provider, so a reported success can be checked against what actually ran.
| Plan | Original runs | Data |
|---|---|---|
variant-calling |
20 | Snakemake tutorial v5.4.5 |
qc-and-trim |
20 | not published |
amplicon-batch |
21 | not published |
amplicon-demultiplex-shell |
3 | not published |
Goal texts are copied from the fractale-experiments plan and run logs.
- The workflow tool is a provider. The task, recorder, CLI and scripted runner know nothing about Snakemake; the Snakemake provider is one implementation of the interface above.
- Pinned wrappers. Rules use
wrapper: 'bio/...'with--wrapper-prefix file://<checkout>/, so the code that runs is the same checkout the agent reads docs from. Runs also work without network access to GitHub. The provider indexed a clone ofmasterbut executedmaster/...fetched at run time. - Failed rules are removed automatically, and
delete_rulerequires a name. The provider's prompt told the agent to callrollback_step, which was never exposed as a tool. Its equivalent,delete_rulewith no name, would have removed the previous successful step. execute_wrapperandexecute_rulehave descriptions. In the provider their docstrings were f-strings, which Python does not treat as docstrings. mcp-server therefore sent both tools with no description.- The run ends with a
finishtool instead of a JSONstopmessage. In 4 of the original runs the agent calledstopas a tool. execute_ruleis shell only.runandscriptwere never used in the 44 logged runs.- Logs and cores are set by the engine. Logs are automatic and cores come from
--cores, so neither is an agent argument. A rule whose outputs use{sample}gets alogs/rule.{sample}.log, which snakemake requires;rule allexpands only targets that use{sample}, so a joint step after per-sample steps works. - Values are quoted with
repr. A shell command containing quotes or\tstays valid in the Snakefile. - Sandbox checks cover inputs as well as outputs. Input and output names must be valid identifiers.
- Configuration comes from arguments instead of module-level environment variables.
- behalf 0.1.0 called ADK's
create_sessionsynchronously, which is a coroutine in current ADK (2.11), so--backend geminifailed. Fixed in behalf 0.1.1, which this package requires. - Tool schemas cannot mark arguments optional, which is why every argument is required here.
- The Claude runner fixes
max_turnsat 40. The--max-callsbudget is enforced in the tools, so it applies to every backend. - Runners do not report token usage, so records carry tool calls and timing but not tokens.
python -m pytest tests/test_unit.py tests/test_providers.py # no snakemake or model needed
# needs snakemake, conda/mamba, git and the tutorial data
export WORKFLOW_SECRETARY_TEST_DATA=data
export WORKFLOW_SECRETARY_CONDA_PREFIX=~/conda-envs
python tests/test_integration.pyThe unit tests use a fake snakemake and a two-wrapper catalog. One of them checks that Google ADK builds a complete schema for every tool, and is skipped when google-adk is not installed. The provider tests exercise the interface with the shell provider described above.
The integration tests run the variant-calling chain for sample A (bwa mem, samtools sort and index, bcftools mpileup and call) with real wrappers, then the whole plan for A, B and C through the CLI with tests/scripts/variant-calling.yaml. They also cover a real failure followed by a retry, a shell rule, and the record written by a scripted run. The first run builds the conda environments, which takes a few minutes; later runs with the same --conda-prefix take under a minute.
HPCIC DevTools is distributed under the terms of the MIT license. All new contributions must be made under this license.
See LICENSE, COPYRIGHT, and NOTICE for details.
SPDX-License-Identifier: (MIT)
LLNL-CODE- 842614