Module 5 · Research case studies
Architecture & algorithms deep-dive — layers, dispatcher, schema, pipeline
This is the engineering deep-dive of the case study. It reconstructs, layer by layer, how the paper's architecture turns raw source into a verified, prioritized finding set: the dispatcher that owns no domain logic, the parallel execution model and its Amdahl/Gustafson bound, the deduplication and normalization steps, the shared World context, and the agent chain — ending in a full worked example where no single tool found the bug but the pipeline did.
Learning objective
Work through the full architecture end to end: the four layers, the Pure Dispatcher contract, the distributed parallel-execution algorithm and its speedup bound, the unified Finding schema and shared context, and the six-agent refinement pipeline — with pseudocode and a complete worked correlation walkthrough.
- Track
- Research
- Module
- 5 · Research case studies
- Prerequisites
- The Semantic Refinement Pattern (lesson 1) · The refinement pipeline & unified schema (lesson 2)
The whole machine in one view
Lessons 1 and 2 introduced the pattern and the pipeline. This lesson is the mechanic's view: it follows one target all the way through, naming the data structures and the algorithm at each layer. The paper's architecture is a straight pipeline of four layers over a distributed runtime, and the discipline that makes it work is that each layer has exactly one job and a clean interface to the next.
the pipeline at a glance (re-expressed)Source / bytecode | [L1] Pure Dispatcher -> launches + collects, zero domain logic | [L2] Parallel Tool Exec -> all detectors run concurrently, isolated | [L3] Schema Normalization -> per-tool adapters -> one Finding type | + dedupe + build shared World context [L4] Sequential LLM Refine -> triage -> correlate -> chain -> | exploit-skeleton -> remediation -> summary Verified, prioritized finding set + report
Keep the invariant from lesson 1 in view the whole way down: layers 1–3 produce grounded candidate findings; layer 4 may only filter, correlate and enrich them. Nothing after layer 2 is allowed to originate a finding.
Layer 1 — the Pure Dispatcher contract
The paper defines an orchestration component as a Pure Dispatcher when it (1) contains no domain-specific logic, (2) makes no decisions based on analysis content, (3) treats all tools uniformly through one interface, and (4) could be reused for a different analysis domain unchanged. The pay-off is three kinds of substitutability: swap a tool without touching orchestration, reuse the dispatcher across domains, and test orchestration independently of analysis.
The uniformity comes from a minimal tool interface every detector implements. The dispatcher only ever calls these methods; it never branches on what a finding means.
minimal tool interface (re-expressed, not verbatim)interface AnalysisTool: name() -> stable unique id timeout() -> max seconds allowed analyze(target) -> ToolOutput # raw, tool-specific normalize(ToolOutput) -> Finding[] # adapter to unified schema
Because the dispatcher decides nothing about content, the correctness argument of lesson 1 has a clean home: all domain judgement lives in tools (detection) and in the agent chain (refinement), never in coordination code. That is what lets the paper claim the dispatcher "could orchestrate any set of tools".
Layer 2 — distributed parallel execution
Layer 2 dispatches each tool as an independent task on a distributed task runtime (the paper uses Apache Ray, built on an actor model). The runtime fits because tool run-times are wildly heterogeneous — from about a second to several minutes — which suits task-based scheduling better than batch frameworks that assume uniform work. Each task is isolated: a tool that crashes or times out contributes an empty result rather than failing the run.
parallel execution + fault isolation (re-expressed)def run_one(tool, target): try: raw = tool.analyze(target) # bounded by tool.timeout() return tool.normalize(raw) # -> Finding[] except ToolError as e: log_warning(tool.name(), e) return [] # isolated failure, not a crash def analyze_parallel(tools, target): futures = [async(run_one, t, target) for t in tools] # launch all results = await_all(futures) # join return dedupe(flatten(results)) # one list
The deduplication key the paper uses is the pair (finding type, location): two tools reporting the same issue at the same place collapse to one candidate, which both reduces noise and preserves provenance for each contributing tool.
The speedup bound — Amdahl and Gustafson
The paper frames the achievable speedup formally. For n independent tools with run-times t_1..t_n, the parallel wall-clock time is dominated by the slowest tool plus orchestration overhead, so speedup approaches the sum of the serial times divided by (max single time + overhead).
speedup model (re-expressed)serial_time = sum(t_i for i in 1..n) parallel_time = max(t_i for i in 1..n) + overhead speedup = serial_time / parallel_time # tools with similar times -> speedup approaches n # heterogeneous times -> speedup bounded by the slowest tool
Amdahl's law bounds this (the non-parallel orchestration portion caps the gain); Gustafson's reframing explains why near-linear scaling is still achievable as you add more tools or larger workloads — the parallel portion grows with the problem. PROVEN (as arithmetic of the model): these are properties of the speedup equation, not of the paper's corpus. MEASURED separately: the paper reports a roughly 7x wall-clock speedup in its own runs, which is a result about its toolset, not a guarantee.
Do not conflate the two. The Amdahl/Gustafson relations are math that always holds; the "~7x" is an observation on one machine and one toolset.
Layer 3 — normalization, the Finding record and the World
Normalization is where heterogeneity dies. Each tool has an adapter that maps its native output into one Finding record carrying provenance (which tool), a standardized type and severity, a confidence, a precise location, a human-readable description, structured evidence, and an optional standardized weakness id.
unified Finding + shared World (re-expressed)Finding { id, tool, type, severity, confidence, location{file, contract, function, line_start, line_end}, description, evidence[]{kind, content}, cwe_id? } World { target, contracts{}, findings[], dependency_graph, entry_points[], state_variables[], external_calls[], findings_for(contract) -> Finding[] # cross-reference helpers findings_by_type(type) -> Finding[] }
The World object is the second half of layer 3 and the thing that makes correlation possible: instead of each agent re-deriving the dependency graph or re-scanning for related findings, the pipeline builds one shared context once and lets every agent query it. An adapter example: a pattern-matcher's JSON of detectors maps each detector to a Finding, translating its severity labels into the unified scale and attaching the matched code snippet as evidence — pure translation, no new judgement.
Layer 4 — the six-agent chain, precisely
Each agent inherits a common base (a model client at low temperature, a context-builder that renders the relevant slice of the World into a prompt) and implements one process step over Finding[] + World. They run strictly in sequence, each consuming its predecessor's enriched output.
triage agent, the precision driver (re-expressed)class TriageAgent: # prompt asks, per finding: is the vulnerable code reachable? # are inputs validated upstream? does business logic make it exploitable? # output per finding: { keep: bool, confidence: float, reason: str } def process(findings, world): kept = [] for f in findings: ctx = build_context(f, world) # contract, function, related, deps r = llm.decide(TRIAGE_PROMPT, f, ctx) if r.keep: f.confidence = r.confidence f.reason = r.reason kept.append(f) # only REMOVES or annotates return kept
The ordering is deliberate: triage prunes false positives first (so later, more expensive agents work on a clean set); correlation groups by root cause; the attack-chain agent assembles multi-step scenarios; exploit-skeleton generation drafts proof-of-concept scaffolds for high-severity items (explicitly needing manual refinement); remediation proposes fixes; summary writes the report. Across the whole chain every agent only removes, groups, links or annotates — honoring the Detection Separation Property by construction.
Full worked correlation walkthrough
Here is the end-to-end trace of the paper's flagship case, where no single tool reported the vulnerability but the pipeline did. The target is an accounting flaw: a pool can be drained by manipulating how balances are read. Use this as the canonical illustration of "the sum of partial observations".
- 1L2 tool outputs (partial): a pattern-matcher flags an external-call pattern at medium confidence; an access-control analyzer flags a missing check on a deposit path; a symbolic executor times out on the deep path and reports nothing; a property tool finds nothing because the needed invariant (pool balance equals token balance of pool) was never specified.
- 2L3 normalizes these into Finding records at a shared location and builds the World (dependency graph + entry points + state variables for the pool contract).
- 3L4 / triage: reasons that the flagged call path reads a live total-assets value derived from on-chain balances, so the external-call finding is genuinely reachable — promotes it from medium to high and keeps it; discards unrelated low-value noise.
- 4L4 / correlation: links the promoted call finding with the access-control flag, inferring that unprotected deposits let an attacker move balances that the accounting then mis-reads — a single root cause spanning two findings.
- 5L4 / attack-chain: assembles the sequence — (a) transfer tokens directly to the pool, (b) the pool's accounting diverges from true balances, (c) a dependent operation reverts or mis-prices for everyone — producing one compound finding no individual tool emitted.
- 6Verification (lesson 3): the generate-and-execute stage produces a runnable test that performs the transfer and demonstrates the divergent behavior, marking the compound finding VERIFIED.
The point is architectural, not luck: correlation was only possible because (a) all tools fed one schema with provenance and (b) a shared World let the agent cross-reference findings and dependencies. Remove either and the chain cannot form.
Application to agent systems
If you are building an AI-augmented analysis pipeline, this lesson is a blueprint: put a content-agnostic dispatcher at the front so tools are swappable and orchestration is independently testable; run independent producers in parallel with per-task isolation and dedupe by (type, location); normalize everything to one provenance-carrying record before any model runs; build one shared context object instead of re-deriving state per agent; and compose single-responsibility agents in a fixed order with cheap pruning first and expensive reasoning last. The architecture, not any single clever prompt, is what makes the result reproducible and the guarantees checkable.
Exercise
Reorder the six agents so the most expensive one runs first instead of last, keeping all else equal. Explain concretely what degrades — in cost, and in the quality of correlation and attack-chain reasoning — and why the paper's ordering (triage first) is the right default.
Expected direction: Running an expensive agent (e.g., attack-chain or exploit-skeleton) first means it processes the full un-triaged candidate set, including many false positives — so compute cost rises and it wastes effort building chains around findings triage would have discarded. Correlation/attack-chain quality also drops because they reason over noisy input, producing spurious chains anchored on false positives. Triage-first prunes cheaply so every downstream agent works on a clean, higher-precision set; that is why the paper orders cheap filtering before expensive reasoning.
Check your understanding
- What four conditions make an orchestrator a "Pure Dispatcher"?
- No domain-specific logic; no decisions based on analysis content; all tools handled through one uniform interface; reusable for an entirely different analysis domain.
- What key is used to deduplicate findings in layer 2, and why keep provenance?
- The (finding type, location) pair; provenance is kept so each contributing tool is still traceable, which the no-hallucination argument depends on.
- Distinguish the Amdahl/Gustafson speedup model from the "~7x" result.
- The speedup model is arithmetic of the equation (always true); the ~7x is a measured observation on the paper's specific toolset and machine, not a guarantee.
- Why is the shared World object necessary for the correlation walkthrough to work?
- Correlation and attack-chain agents need cross-referenceable findings plus a dependency graph and entry points in one place; without the shared context they could not link partial observations into one compound finding.
- In the walkthrough, which step first makes the vulnerability "real" rather than a set of fragments?
- The attack-chain agent assembling the transfer -> accounting-divergence -> dependent-failure sequence into one compound finding (later confirmed by the execution test).
References
- [1]Research paper under study — four-layer architecture, Pure Dispatcher, unified schema and agent pipeline (cited generically)
- [2]Apache Ray — distributed actor/task runtime for heterogeneous parallel execution
- [3]Amdahl's law and Gustafson's reframing — bounds and scaling of parallel speedup
- [4]Standardized weakness classification (CWE) — used in the Finding record
References point to public, well-known technical sources for further study.
Related articles
