Graphs and reports for agents¶
A noodlelab graph is a pipeline of typed nodes saved as <name>.graph.json.
People open it in the browser editor. Agents write it as JSON, through the
MCP tools or directly, and run it headless. It carries its own report:
Reporting nodes that render a PDF.
The loop¶
With the MCP server (noodlelab mcp, set up by noodlelab init-agent):
guide, thenlist_examples. Read a similar example withget_example, or copy it withcopy_exampleand adapt it.list_nodeswith a query ("uncertainty","beam","csv"), thendescribe_nodefor each node you use: its inputs, units, options and defaults.save_graphwith the whole graph, or build it up step by step withedit_graph. Both returnissues: fix every error (type or unit mismatches, unknown nodes, missing inputs) before running.run_graphreturns each node’s result in short, the checks and requirement verdicts with their margins, errors, the files written (the report’s PDF) and the provenance record.verifywith the graph’s path. It passes when every check passed and every requirement was verified.
When the user has the editor open and started the agent from its Agent panel, every save shows up on their canvas straight away.
Without MCP, write the JSON file and use the command line:
noodlelab verify g.graph.json --json (it checks, runs and audits),
noodlelab run g.graph.json -v for every node’s value, and noodlelab nodes to list
the node types.
The format¶
A graph document holds a graph and optional metadata. The editor adds a layout when it saves; omit it, and the editor arranges the nodes itself.
{
"format": "noodlelab.graph", "version": 1, "name": "drop",
"metadata": {"title": "Drop test", "tracked": [{"node": "4", "output": "result", "label": "h/g"}]},
"graph": {
"nodes": [
{"id": "1", "type": "requirements.requirements",
"inputs": {"text": {"value": "DRP-001 h_over_g <= 0.5 s^2 [Analysis] # Short enough"}}},
{"id": "2", "type": "units.quantity", "title": "Height", "inputs": {"value": {"value": "2.0 m"}}},
{"id": "3", "type": "uncertainty.measurement", "title": "g",
"inputs": {"value": {"value": "9.81 ± 0.02 m/s^2"}, "name": {"value": "g"}}},
{"id": "4", "type": "units.quantity_math", "title": "h / g",
"inputs": {"operation": {"value": "divide"},
"a": {"link": {"node": "2", "output": "result"}},
"b": {"link": {"node": "3", "output": "result"}}}},
{"id": "5", "type": "requirements.verify_requirement",
"inputs": {"requirements": {"link": {"node": "1", "output": "result"}},
"value": {"link": {"node": "4", "output": "result"}},
"id": {"value": "DRP-001"}}}
],
"report": [
{"id": "1", "type": "report.new_report", "inputs": {"title": {"value": "Drop test"}}},
{"id": "2", "type": "report.add_value",
"inputs": {"report": {"link": {"node": "1", "output": "result"}},
"label": {"value": "h / g"},
"value": {"link": {"node": "4", "output": "result", "tab": "processing"}}}},
{"id": "3", "type": "report.add_compliance_matrix",
"inputs": {"report": {"link": {"node": "2", "output": "result"}},
"verifications": {"link": {"node": "5", "output": "log", "tab": "processing"}}}},
{"id": "4", "type": "report.add_run_details",
"inputs": {"report": {"link": {"node": "3", "output": "result"}}}},
{"id": "5", "type": "report.render_report",
"inputs": {"report": {"link": {"node": "4", "output": "result"}},
"filename": {"value": "drop-test.pdf"}}}
]
}
}
Every input is
{"value": ...}or{"link": {"node", "output"}}. The MCP tools also accept bare values ("b": 2).Values with units and uncertainty are typed as text:
"2.0 m","9.81 ± 0.02 m/s^2".A report node links to a processing output with
"tab": "processing". Links never go from processing into the report.metadata.trackedlists outputs the editor’s Tracked tab follows from run to run.Subgraphs (
"type": "subgraph") and repeat, sweep and optimize zones are described in the docstring ofnoodlelab.core.graph. The examples use all of them.The JSON schema of the graph document is in graph.schema.json.
Patterns¶
Formulas with units:
symbolic.expression(for example"sqrt(2*h/g)"),symbolic.set_valuefor each symbol, thensymbolic.evaluate.symbolic.to_mathfeedsreport.add_equation, so the report shows the formula it computed. Example 23 does this throughout.Constants: a
units.constantnode (name:pi,tau,c,g0,k_B,G… and optionalunit), or a line in asymbolic.valuesnode (g = g0, or justc). Never retype a constant’s digits. A graph’s own constants go in"constants"next to"nodes"(edit_graphop"constant"), and constants for the whole workspace go inconstants.toml. Examples 3, 6, 9, 17 and 23 use them.Measurements:
uncertainty.measurementforx ± u,uncertainty.add_uncertaintyfor Type B (from a datasheet or resolution), anduncertainty.mean_of_repeatsfor Type A.uncertainty.uncertainty_budgetgoes into the report, anduncertainty.monte_carlochecks the GUM result.Requirements: one
requirements.requirementsnode. Userequirements.requirement_valueto feed a limit into the design,requirements.verify_requirementfor each check (chain theirlogoutputs), andreport.add_compliance_matrixfrom the lastlog.Checks that are not requirements:
checks.expect_value,checks.expect_in_range,checks.expect_table.Trade studies: a Sweep zone over a design, then
requirements.check_candidatesto keep the rows that meet every requirement.The report:
report.new_report, then add headings, text, values, figures, equations, requirements and the compliance matrix. Finish withreport.add_run_details(provenance: who ran it, when, versions and input hashes) andreport.render_report.