Use when adding or changing an npa.workflow toolRef argv template, or when changing a CLI option that a toolRef already passes — the contract that stops a stage from rendering cleanly and then crashing in the pod.
Scanned 9/8/2026
Install to Claude Code
npx -y skills add nebius/nebius-physical-ai --skill toolref-argv-contract --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Toolref Argv Contract?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/nebius-toolref-argv-contract)More formats (shields.io, HTML) on the badges page.
---
name: toolref-argv-contract
description: Use when adding or changing an npa.workflow toolRef argv template, or when changing a CLI option that a toolRef already passes — the contract that stops a stage from rendering cleanly and then crashing in the pod.
---
# The toolRef Argv Contract
A `toolRef` is the only way an `npa.workflow` spec invokes a workbench tool. The
engine expands it from `TOOL_CATALOG` in
`npa/src/npa/orchestration/npa_workflow/catalog.py` into an argv list and runs
it inside the task pod.
Nothing in `validate-spec` or `plan-spec` compares that argv against the CLI
command it will be handed. A template can validate, plan, and render perfectly
and still die with `No such option` on real infrastructure after a GPU has been
provisioned. `npa/tests/guardrails/test_tool_catalog_argv.py` exists to close
that gap. Treat it as the real gate, not the spec validators.
Read this before writing an entry, and again whenever you rename or retype a CLI
option that any toolRef already passes.
## Shape
```python
"workbench.<tool_snake>.<verb>": ToolEntry(
name="workbench.<tool_snake>.<verb>",
description="...",
argv_template=[
"npa", "workbench", "<tool>", "<verb>",
"--input-path", "{{config.input_uri}}",
"--output-path", "{{config.output_uri}}",
],
),
```
The catalog key is snake_case; the argv inside it is the kebab-case CLI path.
`workbench.scenario_gen.generate` invokes `npa workbench scenario-gen generate`.
## Rules
**Every flag must exist on the target command.** Verify against the live
signature, not memory and not the docs. `npa workbench <tool> <verb> --help` is
the source of truth.
**Include required flags.** A template missing `--run-id` or `--workflow-run`
fails at runtime, not at render.
**Never pass a format word to a path option.** `--output json` on an option that
takes a path is the worst failure mode in this class, because the stage
*succeeds* and the declared artifact silently never appears. When a command has
both, `--output` takes the path and `--output-format` takes the word.
**Do not pass infrastructure.** Image, accelerator, and GPU type belong in the
spec's `resources.<profile>`, not in the stage argv. Passing them again nests
infrastructure selection inside the pod.
**Booleans cannot be templated.** A v0.0.1 argv template is a fixed list with no
conditional rendering, so `--flag {{config.x}}` cannot express a paired boolean
such as `--headless/--no-headless`. Either hardcode the flag or record the
parameter as an unreachable `spec_gap` in the three-tier contract.
**Prefer `npa ...` over a wrapper.** Templates that start with `bash` or
`python` are harder to check. The `bash -c` forms have their embedded `npa`
commands extracted and audited, and `python -m` forms are parsed against their
module's argparse by `npa/tests/guardrails/test_module_toolref_argv.py`, but
inline `python -c` source is genuinely unchecked. The exemption lists in the
argv guardrail are pinned to shrink and never grow; a new non-CLI template fails
the build.
**Multi-node is fail-closed.** `ToolEntry.multi_node_mode` defaults to
`forbidden` because the same command running on every node would publish racing
outputs. A sharded tool must declare its rank-aware activation and its durable
join through `shard_activation_config` and `shard_output_config`.
## Registering The Entry
An argv template alone is not enough. Three other surfaces must agree:
- **Reachability** — every catalog entry must be reachable from a shipped spec
under `workflows/`, or be listed in
`PUBLIC_REUSABLE_TOOLREFS` in `catalog.py` as a deliberate public primitive.
Enforced by `npa/tests/guardrails/test_shown_workflow_catalog.py`.
- **Docs** — one row per toolRef in
`docs/workbench/npa-workflow-tool-catalog.md`. Enforced by
`npa/tests/orchestration/npa_workflow/test_catalog_doc_sync.py`.
- **Image routing** — a containerized or GPU stage needs its toolRef prefix
mapped to an image in
`npa/src/npa/orchestration/npa_workflow/skypilot_render.py`. Without it the
stage runs on the default image, which usually fails late and confusingly.
## Verify Locally
```bash
npa/.venv/bin/python -m pytest \
npa/tests/guardrails/test_tool_catalog_argv.py \
npa/tests/guardrails/test_module_toolref_argv.py \
npa/tests/guardrails/test_shown_workflow_catalog.py -q
```
Then validate the spec that uses the entry:
```bash
npa workbench workflow validate-spec workflows/testing/<spec>.yaml
```
Remember what each proves. The guardrail proves the argv can run; `validate-spec`
proves the spec is well formed. Neither proves the stage does the right thing —
that needs live coverage, per `skills/atomic/testing-conventions/SKILL.md`.
## Failures This Contract Has Already Caught
Each of these shipped or nearly shipped, and each is now pinned by a test:
- `workbench.rl.policy_train` passed `--learning-rate`, `--batch-size`, and
`--input-path` to `npa workbench isaac-lab train`, which has none of them. The
real options are `--steps`, `--num-envs`, and `--data-path`.
- `workbench.sonic.eval` passed `json` to `--output`, a path option. The stage
reported success and the declared `eval.json` never appeared.
- `workbench.lancedb.create_failure_views` passed `--table` to
`lancedb create-mv`, whose option is `--source-table`. It hid inside a
`bash -c` wrapper, which the audit did not read at the time.
- `sim2real_envgen.raw_shard` omitted the required `--run-id` from a `python -m`
invocation.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!