Skip to content

Validate a workflow

science-adk validate is static, instant and free. Run it after every edit — it is the cheapest way to find a mistake that would otherwise surface halfway through a long run.

science-adk validate                  # the current experiment
science-adk validate 001-my-hypothesis
science-adk validate --json
ok: no issues found

Reading the report

error  edge fetch.planets -> analyze.planet: 'analyze' has no input port 'planet'
       fix: Available inputs: planets
warn   node plot: is not connected to anything
       fix: Connect it, or remove it from the workflow.

Each issue has three parts: where (workflow, edge …, node …, or file:class), what is wrong, and a fix written as an instruction. Errors block the run; warnings do not.

Common errors and their fixes

Port name mismatch between class and DAG

error  analyze.py:FitKeplerLaw: input ports ['planet'] do not match
       workflow.json ['planets']
       fix: The class and the DAG must agree. Update whichever is wrong.

The class's Ports(...) and the node's ports block must declare the same names. Change whichever one is wrong — the check does not care which is authoritative, only that they agree.

Type mismatch on an edge

error  edge load.dataset -> train.dataset: type mismatch: json -> dataframe
       fix: Change one port's type, or insert a node that converts.

Only identical types, any on either side, and int → float are accepted. If the conversion is real, make it a node; if the declaration was sloppy, fix the declaration.

ports without an annotation

error  analyze.py:FitKeplerLaw: declares `ports` without a type annotation
       fix: Write `ports: Ports = Ports(...)`, not `ports = Ports(...)`.

Agents are pydantic models. An unannotated assignment is not a field, so the ports would silently be empty at run time.

An unannotated class attribute

error  analyze.py:FitKeplerLaw: class attribute `assumptions` has no type
       annotation
       fix: For a constant write `assumptions: ClassVar[...] = ...`; for
       configuration, read it from `params` instead.
from typing import ClassVar

assumptions: ClassVar[list[str]] = ["..."]

A method named run

error  analyze.py:FitKeplerLaw: defines `run`, which belongs to the ADK runner
       protocol
       fix: Rename it to `execute`: `async def execute(self) -> dict:`.

execute is not async

error  analyze.py:FitKeplerLaw: `execute` must be async
       fix: Change `def execute` to `async def execute`.

Two agents in one file

error  analyze.py: defines 2 agent classes (FitKeplerLaw, Helper)
       fix: Split them into separate files: one agent per file.

One node, one file, one class. A helper that is not an agent is fine; a second Agent subclass is not.

A required input with no edge

error  node evaluate: required input 'settings' is not connected
       fix: Add an edge into evaluate.settings, or mark the port optional.

A cycle

error  workflow: cycle detected: analyze -> evaluate -> analyze
       fix: Remove an edge to break the cycle.

Two edges into one input

error  edge b.out -> c.in: input c.in is already fed by a.out
       fix: An input port accepts exactly one edge.

If a node genuinely needs to combine two upstream values, give it two input ports.

Integrity findings

These come from reading the agent's AST, and they exist because each one is a way a broken run turns into a believable number.

error  analyze.py:42: exception is caught and discarded
       fix: Let it propagate. A swallowed error becomes a fabricated result.
warn   analyze.py:51: bare `except:` catches everything, including bugs
warn   fetch.py:17: calls eval()
warn   fetch.py:8: suppresses warnings

The discarded exception is an error — it is the pattern that most reliably converts a failure into a fake success. The rest are warnings, and each is worth a sentence in the audit rationale if you keep it.

Warnings worth arguing with

Warning When it is acceptable
has no evaluation node Almost never — without one, no run can be scored.
is not connected to anything While a node is being built. Not at review time.
suppresses warnings When you have read the warning and know precisely why it is benign. Say so.

In the loop

science-adk validate && science-adk run

The runner validates again before creating a run directory and refuses to start on an invalid workflow, so nothing partially executes:

error: Workflow is not valid, so nothing was run:
error  workflow: cycle detected: analyze -> evaluate -> analyze

See also