Add A Workbench Tool
SkillDev toolsUse when adding a new workbench tool to npa end to end — implementation, CLI, SDK, toolRef catalog, container, tests, docs, and skill — in the order that keeps every CI gate green.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Add A Workbench Tool skill
What this skill tells your AI
The instructions your AI receives, as published by nebius/nebius-physical-ai in skills/workflows/add-workbench-tool/SKILL.md and read by ahel’s review.
Adding a tool is not one edit. A minimal tool touches ~18 files across six phases, and ~12 separate gates fail if you skip one. Work the phases in order: each phase's gate can only pass once the previous phase exists.
CONTRIBUTING.md is the prose rationale for this layer and
docs/architecture/contributor-context.md the architecture. This skill is the
ordered execution path.
Decide The Archetype First
The archetype decides which phases apply. Getting this wrong means building a container nothing routes to, or a GPU stage that lands on the default pod.
| Archetype | Runs in | Phases | Examples |
|---|---|---|---|
| In-process / CPU | default npa workflow pod | A, B, D, E | insights, dataset, scenario_gen |
| Containerized service | own image, deployed to workbench namespace | A–E | detection_training, lancedb |
| Containerized GPU stage | own image, SkyPilot task pod | A–E + image routing | cosmos3, nurec |
In-process tools need no Dockerfile and no image-routing entry. Do not add a
container "for symmetry" — a container that no toolRef routes to is dead
weight the packaging and security gates still police.
The One Architectural Rule
One implementation, three thin clients. Put behavior in
npa/src/npa/workbench/<tool_snake>/; the FastAPI service, the CLI, and the SDK
all call into it. Never implement training, inference, ingest, or status logic
twice.
The cleanest reference to copy is detection-training:
npa/src/npa/workbench/detection_training/service.pynpa/src/npa/workbench/detection_training/schemas.pynpa/src/npa/cli/workbench/detection_training.pynpa/src/npa/sdk/workbench/detection_training.py
LeRobot (npa/src/npa/cli/workbench/lerobot.py) is the richest tool but keeps
much of its logic as CLI orchestration. Read it for CLI surface breadth, not as
the layering model to copy.
Naming
Pick the name once; it derives everything else. <tool> is kebab-case,
<tool_snake> is snake_case.
| Surface | Form | Example |
|---|---|---|
| CLI group | npa workbench <tool> | npa workbench scenario-gen |
| Python package | npa.workbench.<tool_snake> | npa.workbench.scenario_gen |
| SDK module | npa.sdk.workbench.<tool_snake> | npa.sdk.workbench.scenario_gen |
| toolRef key | workbench.<tool_snake>.<verb> | workbench.scenario_gen.generate |
| Image name | npa-<tool> | npa-cosmos3 |
| Skill | skills/tools/<tool>/SKILL.md | skills/tools/scenario-gen/SKILL.md |
The toolRef key is snake_case but the argv inside it is the kebab-case CLI path. Mixing these up is the most common catalog error.
Phase A — Implementation And Clients
- Create
npa/src/npa/workbench/<tool_snake>/with__init__.py,schemas.py(Pydantic request/response models), and the domain modules. Addservice.pyexposingcreate_app()when the tool owns a service. - Create
npa/src/npa/cli/workbench/<tool_snake>.pywith a Typerapp. Use a package directory instead when the tool has many verbs (npa/src/npa/cli/workbench/lancedb/cli.pyis the modular reference). - Register the CLI group in
npa/src/npa/cli/workbench/__init__.py:
from npa.cli.workbench.<tool_snake> import app as <tool_snake>_app
app.add_typer(<tool_snake>_app, name="<tool>")
- Create
npa/src/npa/sdk/workbench/<tool_snake>.pyand export it fromnpa/src/npa/sdk/workbench/__init__.py(import plus__all__entry). - Add
<tool_snake>to the lazy submodule list innpa/src/npa/workbench/__init__.py.
Register both the CLI and the Python-callable surface. SONIC is a known deviation that registers only the CLI; do not copy it.
For option naming, output format, error handling, and the config/credentials
accessors, follow skills/atomic/npa-cli-conventions/SKILL.md.
Phase B — Workflow Surface
- Add one
ToolEntryper invocable verb tonpa/src/npa/orchestration/npa_workflow/catalog.py. The argv must name real CLI flags — seeskills/atomic/toolref-argv-contract/SKILL.mdbefore writing it, because this is where tools most often ship a stage that renders cleanly and then dies in the pod. - Add a row per toolRef to
docs/workbench/npa-workflow-tool-catalog.md. - Create at least one reference spec under
workflows/testing/. Every catalog entry must be reachable from a shipped spec unless it is deliberately listed inPUBLIC_REUSABLE_TOOLREFSincatalog.py. - GPU or containerized stages only: map the toolRef prefix to its image in
npa/src/npa/orchestration/npa_workflow/skypilot_render.py. Without this the stage silently runs on the default image.
Phase C — Container (containerized archetypes only)
- Create
npa/docker/workbench/<tool>/Dockerfileplus abuild.shfollowing the--registry/--pushshape. - Add the image to
npa/docker/workbench/packaging-contract.yamland classify redistribution withskills/atomic/solution-licensing/SKILL.md. Verify the claim against the built image, not the Dockerfile. - Register the image name and pinned tag in
npa/src/npa/deploy/images.pyandnpa/pyproject.toml. Keep the tag family (cuda12, orcuda13-b300for Blackwell) — do not invent a third;npa/docker/workbench/tags.yamlandnpa/docker/workbench/check_tag_consistency.pyenforce it. - Add a golden eval to
npa/src/npa/smoke/golden_evals.yamland its capability tonpa/src/npa/smoke/capabilities.pyso the image is provably functional.
Phase D — Tests
npa/tests/cli/test_<tool_snake>_cli.py—CliRunneragainstnpa.cli.main:app, covering registration, help, every management verb, and every capability verb.npa/tests/workbench/test_<tool_snake>.py— the shared implementation and each service endpoint, with at least one failure path per endpoint.npa/tests/workflows/test_<tool_snake>_workflow.py— spec validation and argv construction.- Add the tool to
npa/tests/guardrails/test_three_tier_contract.py, either as a fullCapabilityContractor in the explicit seam set. A new CLI group with neither fails immediately.
Mock infrastructure at the call site and keep GPU-heavy imports out of module
scope. Live coverage is not optional for workflow-facing changes — see
skills/atomic/testing-conventions/SKILL.md.
Phase E — Docs And Skill
- Write
docs/cli/<tool>.mdand add it todocs/cli/README.md, then regenerate the CLI reference withbash scripts/build_docs.sh. - Write
skills/tools/<tool>/SKILL.mdand register it inskills/index.yamlwith at least one smoke. UpdateAGENTS.mdandCLAUDE.mdonly if the root index lists the skill. - For a containerized tool, load
skills/atomic/audit-container-docs/SKILL.mdand reconciledocs/workbench/container-image-catalog.mdafter the image pin and redistribution/publication classification are final. Public-plan images must have their exact resolved pin in the table; restricted, internal, or unpublished images must not be represented as publicly available.
Phase F — Optional Integrations
Agent UI routing, the daily GPU coverage rotation
(npa/tests/orchestration/npa_workflow/test_daily_coverage.py requires a new
workflow image to appear in a workflow of four or more steps), and public
registry publication each have their own gates. Skip them deliberately, not by
omission.
What Catches Each Omission
Run these before pushing rather than discovering them in CI. The order is
cheapest-first; skills/atomic/pre-pr-validation/SKILL.md has the full ladder.
| Skipped step | Gate that fails |
|---|---|
| CLI group not in seam or contracts | npa/tests/guardrails/test_three_tier_contract.py |
| Catalog argv names a flag the CLI lacks | npa/tests/guardrails/test_tool_catalog_argv.py |
python -m toolRef argv drifts | npa/tests/guardrails/test_module_toolref_argv.py |
| toolRef missing from the catalog doc | npa/tests/orchestration/npa_workflow/test_catalog_doc_sync.py |
| Catalog entry no spec reaches | npa/tests/guardrails/test_shown_workflow_catalog.py |
Spec outputs: disagree with write paths | npa/tests/guardrails/test_spec_declared_outputs.py |
| Dockerfile not in the packaging contract | npa/tests/docker/test_packaging_contract.py |
| Image missing k8s prerequisites | npa/tests/guardrails/test_workbench_image_k8s_prereqs.py |
| Skill file absent from the index | npa/tests/guardrails/test_skills_index.py |
| Docs reference a spec that does not exist | npa/tests/guardrails/test_no_dangling_workflow_references.py |
| CLI options changed, docs not regenerated | Lint / docs-drift |
| New workflow image not in a 4+ step workflow | npa/tests/orchestration/npa_workflow/test_daily_coverage.py |
To decode a failure message into its fix, use
skills/atomic/guardrail-failures/SKILL.md.
Footguns
- A stage that renders and then dies.
validate-specandplan-specdo not check argv against the CLI signature. Only the argv guardrail does. - A format word passed to a path option.
--output jsonon an option that takes a path makes the stage succeed and write the artifact nowhere. - Nesting infrastructure choices. Image and accelerator belong in
resources.<profile>, not in the stage argv. - Inventing a top-level CLI group. New commands belong under
npa workbench; the top-level utilities innpa/src/npa/cli/main.pyare legacy and marked as such. - Copying a legacy layout.
nurec,genesis, andgrootpredatecli/workbench/. They work; they are not the pattern for new tools.
Signals
- GitHub stars
- 28
- Forks
- 15
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
add-workbench-tool- Source
- github.com/nebius/nebius-physical-ai