Harness Node Splitter
Overview
The horizontal workflow graph says what the agent should do; the harness
executes it. Before the harness can run anything, the workflow must be split
into nodes with focused responsibilities. The chapter gives one rule that cuts
through the design space:
Nodes differ by tool surface, not by prompt.
The sharpest example is security tooling. Kyle Polley's RedAI splits vulnerability
discovery across two roles whose distinction is entirely tool-level. The
scanner node holds a filesystem and threat-models source code (optimized for
recall). The validator node holds a browser driver, an iOS simulator, a
network stack, and a scripting runtime, and drives each candidate into a live
environment. Swap the prompts and nothing changes — the validator can still
drive the browser because it holds the tool. The role lives in the tool surface.
The chapter's Tip operationalizes this:
Before you add a node, list the tools it will call. If the list overlaps more
than 80% with an existing node, you have a prompt variation of that node —
merge them and vary the prompt. If the tool lists differ substantially, split.
This skill applies that rule to candidate operations and emits each node's
constrained context scope — the tool surface it may invoke, the memory
slices it reads/writes, and the input/output contract the schema validator holds
it to (three of the harness's six surfaces). A tool-less reasoning node (like
Example 2-3's classify vs analyze) is distinguished by prompt and DAG
position, not tools, so tool-less nodes default to split.
When to Use
- Turning a horizontal-workflow sketch into executable nodes
- Auditing an existing workflow for the common failure mode: one
retrieval-and-reasoning node that both queries the graph and does causal
analysis, with neither pass at specialist quality
- Deciding whether a proposed new node is a real role or a prompt variation of
an existing one (the Tip as a pre-add gate)
Phrases that should invoke this skill: "split this workflow into nodes", "should
this be one node or two", "nodes differ by tool surface", "merge or split these
nodes", "the 80% tool-overlap rule", "constrain the node's context".
When NOT to Use
- Scheduling nodes into parallel phases. Once nodes exist, ordering them
into topological phases with parallelism is
investigation-dag-planner (Ch5).
- Deciding vertical vs horizontal. That first-hop routing is
dual-graph-router; this skill runs after a request has routed to
horizontal/both.
- Selecting which tools a query needs. Filtering a large tool registry for a
query is
rag-mcp-tool-selection (Ch6). This skill assumes each operation's
tool set is already declared.
- Writing the node prompts. This decides the node BOUNDARIES; the prompt
content per node is a separate step.
Process
| Step |
Input |
Action |
Output |
Verification |
| 1 |
operation rows (id, node_type, tools, reads, writes, contracts) |
lib.operations_from_dicts(rows) |
list of Operation |
each op has a tool list (possibly empty) and a node_type |
| 2 |
two tool sets |
lib.tool_overlap(a, b) |
float in [0,1] |
identical sets → 1.0; disjoint → 0.0; empty+empty → 0.0 |
| 3 |
operations + threshold |
lib.split_nodes(ops, threshold=0.8) |
SplitResult (nodes, per-pair decisions) |
same-surface same-type ops merge; distinct-surface ops split |
| 4 |
a Node |
lib.node_scope(node) |
dict: tool_surface / memory_reads / memory_writes / input_contract / output_schema / prompt_variants |
scope names only that node's tools and memory, not the whole registry |
| 5 |
a candidate op + existing nodes |
lib.audit_operation(op, nodes) |
dict with verdict merge/split + reason |
the Tip as a pre-add gate |
Rationalizations
| Agent rationalization |
Documented rebuttal |
| "One retrieval-and-reasoning node is simpler — it queries the graph AND does the analysis." |
That is the exact failure mode the chapter names: "neither pass the quality a specialist node would." The query node holds a graph-read surface; the analysis node holds none. Different surfaces → split. |
| "These two nodes have different prompts, so they are different nodes." |
Different prompts on the SAME tool surface are one node with prompt variation at the input (the Tip). The role is the tool surface, not the wording. Merge and vary the prompt. |
| "Two reasoning nodes both call no tools — merge them, same (empty) surface." |
Tool-less nodes have no tool surface to key on; their role lives in prompt + DAG position. Example 2-3 keeps classify and analyze separate for this reason. This skill scores empty+empty as 0.0 → split by default. |
| "Split everything — more nodes is more modular." |
Over-splitting fragments a single role across nodes that share a tool surface, adding coordination overhead for no leverage. If the tool lists overlap ≥ 80%, it is one role. |
| "Jaccard overlap is too crude for the real tool surface." |
Production derives the true surface from the typed tool registry (harness surface 3) and may weight by cost/risk. The merge/split CONTRACT (≥ threshold → merge) is the stable seam; swap the scorer, keep the contract. |
Red Flags
- A node with a graph-read tool AND a causal-analysis task. This is the
retrieval-and-reasoning conflation. Split into a retrieval node (holds the
graph-read surface) and a reasoning node (holds none).
- Zero merges on a workflow with obvious prompt variants. Either every
operation genuinely has a distinct surface, or the tool lists were declared too
finely — check that "get latency" and "get CPU" both declare the same metrics
tool.
- Everything merged into one node. The tool lists were declared too coarsely
(every op claims "aws"); declare the specific tool each op actually calls.
- A merged node whose prompt variants have incompatible input contracts.
Merging is safe only when the variants operate on the same input surface; a
contract mismatch means these were different roles after all.
- CLI
--help exits non-zero. SKILL.md / CLI mismatch; the multi-harness
invariant is broken.
Non-Negotiable Verification
Run the benchmark battery.
python cli.py benchmark
Confirm 9 sample operations collapse to 8 nodes, get_metrics+get_cpu
merge (identical tool surface), the RedAI scanner/validator pair stays split
(overlap 0.0), and classify/analyze stay separate reasoning nodes.
Inspect the split visually.
python cli.py split
Confirm the [MERGED] node fuses the two metric-query operations and lists
both prompt variants, and that the two disjoint execution nodes remain apart.
Run the Tip as a pre-add gate.
python cli.py overlap --a cloudwatch_get_metric_data --b cloudwatch_get_metric_data
python cli.py overlap --a filesystem --b browser_driver network_stack
The first must verdict merge (1.0), the second split (0.0).
JSON scope round-trips.
python cli.py split --json | python -c "import json,sys; json.load(sys.stdin)"
No exception means the CLI is harness-portable and each node scope serializes.
Security Posture
- Prompt injection. Operation tasks and tool names are author-controlled
workflow metadata. This skill only tokenizes tool names and compares sets; it
never executes a task or invokes a tool. If operation definitions are ingested
from untrusted sources, sanitize
tools and task before splitting — a
malicious tool name could bias a merge, but cannot execute.
- Least privilege by construction. The whole point of the per-node scope is
that a node sees only its own tool surface and its own memory slice, not the
full registry or the full vertical graph. Honoring
node_scope at execution
time is how the harness enforces least privilege (composes with
capability-authorization-gate, Ch3, and subgraph access control, Ch8).
- Privilege escalation. No shell invocation, no eval, no dynamic import, no
file writes outside the read-only bundled
sample-workflow.json. Merging two
nodes UNIONS their tool surfaces — review a merged node's combined surface so a
merge does not silently grant a node more capability than either input had.
Source Attribution
Distilled from Agentic GraphRAG (O'Reilly, by Anthony Alcaraz and Sam Julien),
Chapter 2 — Agentic Graph Architecture Foundations:
- "The Horizontal Workflow Graph" — reasoning/execution/decision/validation node
responsibilities (Example 2-3).
- "Defining the harness" — the six harness surfaces; the per-node scope this
skill emits maps to surfaces 3 (tool registry), 4 (memory interface), and 5
(schema validator).
- "Splitting a workflow into nodes" — "nodes differ by tool surface, not by
prompt"; the RedAI scanner-vs-validator worked example; the 80%-tool-overlap
Tip.
This Generator-pattern skill runs after dual-graph-router routes a request to
the horizontal graph and before investigation-dag-planner schedules the
resulting nodes into phases.
1---2name: harness-node-splitter3description: Split a workflow description into constrained harness nodes using the chapter's rule "nodes differ by tool surface, not by prompt." Given candidate operations each with a declared tool set, merge the ones whose tool surfaces overlap >= 80% (prompt variations of one role) and split the ones with distinct tool surfaces (different roles), then emit the per-node constrained context scope the harness enforces (tool surface + memory reads/writes + input/output contract). Implements the RedAI scanner-vs-validator distinction and the 80%-overlap Tip from Agentic Graph RAG Ch2. Use when turning a horizontal-workflow sketch into executable nodes. NOT for scheduling nodes into parallel phases (that is investigation-dag-planner, Ch5), NOT for deciding vertical-vs-horizontal (that is dual-graph-router), NOT for selecting which tools a query needs (that is rag-mcp-tool-selection, Ch6).4---56# Harness Node Splitter78## Overview910The horizontal workflow graph says what the agent should do; the harness11executes it. Before the harness can run anything, the workflow must be split12into nodes with focused responsibilities. The chapter gives one rule that cuts13through the design space:1415> Nodes differ by tool surface, not by prompt.1617The sharpest example is security tooling. Kyle Polley's RedAI splits vulnerability18discovery across two roles whose distinction is entirely tool-level. The19**scanner** node holds a filesystem and threat-models source code (optimized for20recall). The **validator** node holds a browser driver, an iOS simulator, a21network stack, and a scripting runtime, and drives each candidate into a live22environment. Swap the prompts and nothing changes — the validator can still23drive the browser because it holds the tool. The role lives in the tool surface.2425The chapter's Tip operationalizes this:2627> Before you add a node, list the tools it will call. If the list overlaps more28> than 80% with an existing node, you have a prompt variation of that node —29> merge them and vary the prompt. If the tool lists differ substantially, split.3031This skill applies that rule to candidate operations and emits each node's32**constrained context scope** — the tool surface it may invoke, the memory33slices it reads/writes, and the input/output contract the schema validator holds34it to (three of the harness's six surfaces). A tool-less reasoning node (like35Example 2-3's `classify` vs `analyze`) is distinguished by prompt and DAG36position, not tools, so tool-less nodes default to split.3738## When to Use3940- Turning a horizontal-workflow sketch into executable nodes41- Auditing an existing workflow for the common failure mode: one42 retrieval-and-reasoning node that both queries the graph and does causal43 analysis, with neither pass at specialist quality44- Deciding whether a proposed new node is a real role or a prompt variation of45 an existing one (the Tip as a pre-add gate)4647Phrases that should invoke this skill: "split this workflow into nodes", "should48this be one node or two", "nodes differ by tool surface", "merge or split these49nodes", "the 80% tool-overlap rule", "constrain the node's context".5051## When NOT to Use5253- **Scheduling nodes into parallel phases.** Once nodes exist, ordering them54 into topological phases with parallelism is `investigation-dag-planner` (Ch5).55- **Deciding vertical vs horizontal.** That first-hop routing is56 `dual-graph-router`; this skill runs after a request has routed to57 horizontal/both.58- **Selecting which tools a query needs.** Filtering a large tool registry for a59 query is `rag-mcp-tool-selection` (Ch6). This skill assumes each operation's60 tool set is already declared.61- **Writing the node prompts.** This decides the node BOUNDARIES; the prompt62 content per node is a separate step.6364## Process6566| Step | Input | Action | Output | Verification |67|------|-------|--------|--------|--------------|68| 1 | operation rows (id, node_type, tools, reads, writes, contracts) | `lib.operations_from_dicts(rows)` | list of `Operation` | each op has a tool list (possibly empty) and a node_type |69| 2 | two tool sets | `lib.tool_overlap(a, b)` | float in [0,1] | identical sets → 1.0; disjoint → 0.0; empty+empty → 0.0 |70| 3 | operations + threshold | `lib.split_nodes(ops, threshold=0.8)` | `SplitResult` (nodes, per-pair decisions) | same-surface same-type ops merge; distinct-surface ops split |71| 4 | a `Node` | `lib.node_scope(node)` | dict: tool_surface / memory_reads / memory_writes / input_contract / output_schema / prompt_variants | scope names only that node's tools and memory, not the whole registry |72| 5 | a candidate op + existing nodes | `lib.audit_operation(op, nodes)` | dict with verdict merge/split + reason | the Tip as a pre-add gate |7374## Rationalizations7576| Agent rationalization | Documented rebuttal |77|------------------------|--------------------|78| "One retrieval-and-reasoning node is simpler — it queries the graph AND does the analysis." | That is the exact failure mode the chapter names: "neither pass the quality a specialist node would." The query node holds a graph-read surface; the analysis node holds none. Different surfaces → split. |79| "These two nodes have different prompts, so they are different nodes." | Different prompts on the SAME tool surface are one node with prompt variation at the input (the Tip). The role is the tool surface, not the wording. Merge and vary the prompt. |80| "Two reasoning nodes both call no tools — merge them, same (empty) surface." | Tool-less nodes have no tool surface to key on; their role lives in prompt + DAG position. Example 2-3 keeps `classify` and `analyze` separate for this reason. This skill scores empty+empty as 0.0 → split by default. |81| "Split everything — more nodes is more modular." | Over-splitting fragments a single role across nodes that share a tool surface, adding coordination overhead for no leverage. If the tool lists overlap ≥ 80%, it is one role. |82| "Jaccard overlap is too crude for the real tool surface." | Production derives the true surface from the typed tool registry (harness surface 3) and may weight by cost/risk. The merge/split CONTRACT (≥ threshold → merge) is the stable seam; swap the scorer, keep the contract. |8384## Red Flags8586- **A node with a graph-read tool AND a causal-analysis task.** This is the87 retrieval-and-reasoning conflation. Split into a retrieval node (holds the88 graph-read surface) and a reasoning node (holds none).89- **Zero merges on a workflow with obvious prompt variants.** Either every90 operation genuinely has a distinct surface, or the tool lists were declared too91 finely — check that "get latency" and "get CPU" both declare the same metrics92 tool.93- **Everything merged into one node.** The tool lists were declared too coarsely94 (every op claims "aws"); declare the specific tool each op actually calls.95- **A merged node whose prompt variants have incompatible input contracts.**96 Merging is safe only when the variants operate on the same input surface; a97 contract mismatch means these were different roles after all.98- **CLI `--help` exits non-zero.** SKILL.md / CLI mismatch; the multi-harness99 invariant is broken.100101## Non-Negotiable Verification1021031. **Run the benchmark battery.**104 ```105 python cli.py benchmark106 ```107 Confirm 9 sample operations collapse to 8 nodes, `get_metrics`+`get_cpu`108 merge (identical tool surface), the RedAI scanner/validator pair stays split109 (overlap 0.0), and `classify`/`analyze` stay separate reasoning nodes.1101112. **Inspect the split visually.**112 ```113 python cli.py split114 ```115 Confirm the `[MERGED]` node fuses the two metric-query operations and lists116 both prompt variants, and that the two disjoint execution nodes remain apart.1171183. **Run the Tip as a pre-add gate.**119 ```120 python cli.py overlap --a cloudwatch_get_metric_data --b cloudwatch_get_metric_data121 python cli.py overlap --a filesystem --b browser_driver network_stack122 ```123 The first must verdict merge (1.0), the second split (0.0).1241254. **JSON scope round-trips.**126 ```127 python cli.py split --json | python -c "import json,sys; json.load(sys.stdin)"128 ```129 No exception means the CLI is harness-portable and each node scope serializes.130131## Security Posture132133- **Prompt injection.** Operation tasks and tool names are author-controlled134 workflow metadata. This skill only tokenizes tool names and compares sets; it135 never executes a task or invokes a tool. If operation definitions are ingested136 from untrusted sources, sanitize `tools` and `task` before splitting — a137 malicious tool name could bias a merge, but cannot execute.138- **Least privilege by construction.** The whole point of the per-node scope is139 that a node sees only its own tool surface and its own memory slice, not the140 full registry or the full vertical graph. Honoring `node_scope` at execution141 time is how the harness enforces least privilege (composes with142 `capability-authorization-gate`, Ch3, and subgraph access control, Ch8).143- **Privilege escalation.** No shell invocation, no eval, no dynamic import, no144 file writes outside the read-only bundled `sample-workflow.json`. Merging two145 nodes UNIONS their tool surfaces — review a merged node's combined surface so a146 merge does not silently grant a node more capability than either input had.147148## Source Attribution149150Distilled from *Agentic GraphRAG* (O'Reilly, by Anthony Alcaraz and Sam Julien),151Chapter 2 — Agentic Graph Architecture Foundations:152153- "The Horizontal Workflow Graph" — reasoning/execution/decision/validation node154 responsibilities (Example 2-3).155- "Defining the harness" — the six harness surfaces; the per-node scope this156 skill emits maps to surfaces 3 (tool registry), 4 (memory interface), and 5157 (schema validator).158- "Splitting a workflow into nodes" — "nodes differ by tool surface, not by159 prompt"; the RedAI scanner-vs-validator worked example; the 80%-tool-overlap160 Tip.161162This Generator-pattern skill runs after `dual-graph-router` routes a request to163the horizontal graph and before `investigation-dag-planner` schedules the164resulting nodes into phases.