Fetches a repository's README from raw.githubusercontent.com, trying the main and master branches in parallel and selecting whichever returns HTTP 200. Runs five AI quality probes concurrently, averages two scores deterministically, routes through a quality lane, and appends a test-coverage warning when the AI detects no mention of tests.
What this example teaches
SelectStringOp, no try/catchAddFloatOp + DivFloatOp — once the AI returns two floats, all maths is deterministicSelectStringOp selects between an empty string and a warning message; StringConcatOp appends it — no conditional code in the driverStringTruncateOp) that caps input to 8 KB to stay within AI context limitsParallel fetches feed a preprocessing op; five probes fan out; deterministic averaging feeds quality lanes; an orthogonal bool gate injects a warning suffix.
Both fetch_main and fetch_master run without any condition — they always execute in parallel. IfIntEqOp converts the HTTP status to a boolean, and SelectStringOp acts as a pure data multiplexer: no imperative branching required.
// Both fetches run in parallel — no Condition on either vertex.
Vertex("fetch_main").Op("HTTPGetOp").
Input("URL", "main_url").
Output("Body", "main_body").
Output("StatusCode", "main_status").
Vertex("fetch_master").Op("HTTPGetOp").
Input("URL", "master_url").
Output("Body", "master_body").
// Was the main-branch fetch a 200?
Vertex("check_main").Op("IfIntEqOp").
Input("A", "main_status").
Input("B", "int_200").
Output("Match", "main_ok").
// Pick the body whose branch actually exists.
Vertex("pick_readme").Op("SelectStringOp").
Input("Cond", "main_ok").
Input("IfTrue", "main_body").
Input("IfFalse", "master_body").
Output("Result", "readme_raw").
# Both fetches run in parallel — no Condition on either vertex.
b.vertex("fetch_main").op("HTTPGetOp") \
.input("URL", "main_url") \
.output("Body", "main_body") \
.output("StatusCode", "main_status")
b.vertex("fetch_master").op("HTTPGetOp") \
.input("URL", "master_url") \
.output("Body", "master_body")
# Was the main-branch fetch a 200?
b.vertex("check_main").op("IfIntEqOp") \
.input("A", "main_status") \
.input("B", "int_200") \
.output("Match", "main_ok")
# Pick the body whose branch actually exists.
b.vertex("pick_readme").op("SelectStringOp") \
.input("Cond", "main_ok") \
.input("IfTrue", "main_body") \
.input("IfFalse", "master_body") \
.output("Result", "readme_raw")
// Both fetches run in parallel — no Condition on either vertex.
wf.op.hTTPGetOp("fetch_main")
.input("URL", "main_url")
.output("Body", "main_body")
.output("StatusCode", "main_status");
wf.op.hTTPGetOp("fetch_master")
.input("URL", "master_url")
.output("Body", "master_body");
// Was the main-branch fetch a 200?
wf.op.ifIntEqOp("check_main")
.input("A", "main_status")
.input("B", "int_200")
.output("Match", "main_ok");
// Pick the body whose branch actually exists.
wf.op.selectStringOp("pick_readme")
.input("Cond", "main_ok")
.input("IfTrue", "main_body")
.input("IfFalse", "master_body")
.output("Result", "readme_raw");
After truncation, all five probes read from the same readme wire. With no mutual dependencies, the dagor engine schedules all five concurrently — five AI questions answered in roughly the time of one.
Vertex("purpose_op").Op("AIComputeStringToStringOp").
Params(map[string]string{"operation": "summarize the purpose in one sentence"}).
Input("Input", "readme").
Output("Result", "purpose_str").
Vertex("doc_score_op").Op("AIScoreOp").
Params(map[string]string{"criterion": "documentation completeness"}).
Input("Input", "readme").
Output("Result", "doc_score").
Vertex("has_tests_op").Op("AIBoolOp").
Params(map[string]string{"predicate": "does this README mention tests or CI?"}).
Input("Input", "readme").
Output("Result", "has_tests").
b.vertex("purpose_op").op("AIComputeStringToStringOp") \
.params({"operation": "summarize the purpose in one sentence"}) \
.input("Input", "readme") \
.output("Result", "purpose_str")
b.vertex("doc_score_op").op("AIScoreOp") \
.params({"criterion": "documentation completeness"}) \
.input("Input", "readme") \
.output("Result", "doc_score")
b.vertex("has_tests_op").op("AIBoolOp") \
.params({"predicate": "does this README mention tests or CI?"}) \
.input("Input", "readme") \
.output("Result", "has_tests")
wf.op.aIComputeStringToStringOp("purpose_op", {"operation": "summarize the purpose in one sentence"})
.input("Input", "readme")
.output("Result", "purpose_str");
wf.op.aIScoreOp("doc_score_op", {"criterion": "documentation completeness"})
.input("Input", "readme")
.output("Result", "doc_score");
wf.op.aIBoolOp("has_tests_op", {"predicate": "does this README mention tests or CI?"})
.input("Input", "readme")
.output("Result", "has_tests");
The average is computed with two op vertices — AddFloatOp sums the two score wires, and DivFloatOp divides by a constant wire. Both are typed library ops; no math lives in driver code.
Vertex("sum_scores").Op("AddFloatOp").
Input("A", "doc_score").
Input("B", "clarity_score").
Output("Result", "sum_scores_val").
Vertex("const_2").Op("ConstFloat64Op").
Params(map[string]string{"Value": "2.0"}).
Output("Result", "two_f").
Vertex("avg_score_op").Op("DivFloatOp").
Input("A", "sum_scores_val").
Input("B", "two_f").
Output("Result", "avg_score").
b.vertex("sum_scores").op("AddFloatOp") \
.input("A", "doc_score") \
.input("B", "clarity_score") \
.output("Result", "sum_scores_val")
b.vertex("const_2").op("ConstFloat64Op") \
.params({"Value": "2.0"}) \
.output("Result", "two_f")
b.vertex("avg_score_op").op("DivFloatOp") \
.input("A", "sum_scores_val") \
.input("B", "two_f") \
.output("Result", "avg_score")
wf.op.addFloatOp("sum_scores")
.input("A", "doc_score")
.input("B", "clarity_score")
.output("Result", "sum_scores_val");
wf.op.constFloat64Op("const_2", {"Value": "2.0"})
.output("Result", "two_f");
wf.op.divFloatOp("avg_score_op")
.input("A", "sum_scores_val")
.input("B", "two_f")
.output("Result", "avg_score");
SelectStringOp acts as an in-graph ternary: when has_tests is true it routes an empty string; when false it routes the warning text. StringConcatOp appends whichever was selected. No if statements in the driver.
// Registered: operator.RegisterOpFactory("warning_const", builtin.ContextValFactory[string](warningKey{}))
// Injected: ctx = context.WithValue(ctx, warningKey{}, "\n\nWARNING: tests not mentioned")
Vertex("warning_const").Op("warning_const").
Output("Result", "warning_text").
Vertex("test_warning").Op("SelectStringOp").
Input("Cond", "has_tests").
Input("IfTrue", "empty_text"). // tests mentioned: no warning
Input("IfFalse", "warning_text"). // no tests: warning appended
Output("Result", "test_warning_str").
Vertex("final_narrative").Op("StringConcatOp").
Input("A", "narrative").
Input("B", "test_warning_str").
Output("Result", "final_narrative").
# Registered: operator.register_op_factory("warning_const", builtin.ContextValFactory(warningKey{}))
# Injected: ctx = context.with_value(ctx, warningKey{}, "\n\nWARNING: tests not mentioned")
b.vertex("warning_const").op("warning_const") \
.output("Result", "warning_text")
b.vertex("test_warning").op("SelectStringOp") \
.input("Cond", "has_tests") \
.input("IfTrue", "empty_text") \
.input("IfFalse", "warning_text") \
.output("Result", "test_warning_str")
b.vertex("final_narrative").op("StringConcatOp") \
.input("A", "narrative") \
.input("B", "test_warning_str") \
.output("Result", "final_narrative")
// Registered: operator.registerOpFactory("warning_const", builtin.ContextValFactory(warningKey{}))
// Injected: ctx = context.withValue(ctx, warningKey{}, "\n\nWARNING: tests not mentioned")
wf.op.customOp("warning_const", "warning_const")
.output("Result", "warning_text");
wf.op.selectStringOp("test_warning")
.input("Cond", "has_tests")
.input("IfTrue", "empty_text") // tests mentioned: no warning
.input("IfFalse", "warning_text") // no tests: warning appended
.output("Result", "test_warning_str");
wf.op.stringConcatOp("final_narrative")
.input("A", "narrative")
.input("B", "test_warning_str")
.output("Result", "final_narrative");
export CLAUDE_API_KEY=<your key>
# Live slug — main branch
go run ./examples/readme-quality/ --slug akennis/dagor
# Live slug — master branch fallback
go run ./examples/readme-quality/ --slug tj/n
# Offline fixture
go run ./examples/readme-quality/ --fixture examples/readme-quality/testdata/dagor.md
export CLAUDE_API_KEY=<your key>
# Live slug — main branch
python examples/readme_quality/main.py --slug akennis/dagor
# Live slug — master branch fallback
python examples/readme_quality/main.py --slug tj/n
# Offline fixture
python examples/readme_quality/main.py --fixture examples/readme_quality/testdata/dagor.md
export CLAUDE_API_KEY=<your key>
# Live slug — main branch
npx tsx examples/readme_quality/index.ts --slug akennis/dagor
# Live slug — master branch fallback
npx tsx examples/readme_quality/index.ts --slug tj/n
# Offline fixture
npx tsx examples/readme_quality/index.ts --fixture examples/readme_quality/testdata/dagor.md
The same binary is dual-mode. Pass --mcp and instead of running once and exiting it speaks the Model Context Protocol over stdin/stdout, exposing the entire workflow as a single MCP tool, assess_readme. The Sparsi SDK derives the tool's input schema from the workflow's UserInput struct and validates every tools/call request against it, so an agent runs the whole fetch → five-probe → score → verdict pipeline with one tool call.
# Speak MCP over stdin/stdout instead of running once
go run ./examples/readme-quality --mcp
# Speak MCP over stdin/stdout instead of running once
python examples/readme_quality/main.py --mcp
# Speak MCP over stdin/stdout instead of running once
npx tsx examples/readme_quality/index.ts --mcp
Register it with any MCP client by pointing the client at that command:
{
"mcpServers": {
"readme-quality": {
"command": "go",
"args": ["run", "./examples/readme-quality", "--mcp"]
}
}
}
{
"mcpServers": {
"readme-quality": {
"command": "python",
"args": ["examples/readme_quality/main.py", "--mcp"]
}
}
}
{
"mcpServers": {
"readme-quality": {
"command": "npx",
"args": ["tsx", "examples/readme_quality/index.ts", "--mcp"]
}
}
}
Measured on testdata/dagor.md. Input costs are high because the full README (~2,100 tokens) is re-passed as context to each of the six probes independently.
| Op | Calls | Input tokens | Output tokens | Total tokens |
|---|---|---|---|---|
| AIBoolOp | 2 | 4,275 | 8 | 4,283 |
| AIScoreOp | 2 | 4,303 | 14 | 4,317 |
| AIComputeOp | 2 | 4,300 | 311 | 4,611 |
| Total | 6 | 12,878 | 333 | 13,211 |