How the Two-Phase Loop in ADHD Enforces Convergence Only After Divergence
The ADHD two-phase loop mechanically isolates idea generation from evaluation by spawning N parallel, stateless LLM branches with hard-wall prompts that forbid any scoring or ranking, then sequentially runs a single "critic" pass that performs the only convergence step after all divergent outputs are collected.
The UditAkhourii/adhd repository implements an Adaptive Divergent-then-Convergent Heuristic that solves creative reasoning problems by strictly separating exploration from judgment. Understanding how the two-phase loop in ADHD enforces convergence only after divergence requires examining the mechanical isolation built into the TypeScript orchestration layer. The architecture guarantees that no evaluation leakage occurs during the generation phase by enforcing separation at the API-call level.
Phase 1: Strict Divergence Through Isolated Branch Generation
Parallel Stateless Sessions
In src/diverge.ts, the system samples N cognitive frames from the frame library and launches parallel generation calls via Promise.all. Each branch invokes callLLM() with a unique system prompt containing only the problem description, the selected frame's "vantage" prompt, and a hard-wall instruction that explicitly forbids evaluation, ranking, or hedging.
According to the implementation in src/diverge.ts, each branch executes as a fresh Claude Agent SDK session. This means there is no shared KV-cache, no shared message history, and no cross-branch context. Consequently, branches[i] never sees branches[j] during divergence—by construction. The raw JSON output from each branch contains only generative ideas without any comparative analysis.
The Hard-Wall Guarantee
The hard-wall prompt acts as a mechanical barrier rather than a gentle suggestion. By explicitly instructing the model that evaluation is forbidden and requiring a pure JSON array output, the system ensures that the divergent phase remains pure generation. Because each branch runs in isolation, there is no possibility of early convergence through implicit comparison or ranking.
Phase 2: Controlled Convergence Through the Critic Pipeline
The Three-Pass Evaluation Structure
Once the divergent phase completes, src/engine.ts triggers the convergent phase through a single critic LLM call that executes three sequential passes defined in separate modules:
- Score (
src/score.ts): Rates every leaf on novelty, viability, and fit (0-10 each), tagging traps with concrete reasons. - Cluster (
src/cluster.ts): Groups leaves by underlying design angle rather than surface keywords. - Deepen (
src/deepen.ts): Expands the top-K non-trap leaves with sketches, risk analysis, and child ideas.
Inverted System Prompts for Evaluation
The critic uses an inverted system prompt that forces evaluation mode—a stark contrast to the generation prompts. This design in src/engine.ts represents the only point in the pipeline where pruning and selection occur, ensuring that convergence never interferes with the earlier exploration. The critic consumes all previous outputs from the branches array, performing structured analysis only after the full solution space has been explored.
Mechanical Enforcement in the Orchestration Layer
API-Level Separation
The enforcement happens at the function call level, not merely through prompt engineering. src/engine.ts distinctly separates the Promise.all block (divergent) from the sequential critic calls (convergent). The generator-critic split uses distinct functions with distinct system prompts, eliminating inadvertent leakage of evaluation information into the generation stage.
Implementation Example
The orchestration code explicitly sequences the phases:
// Divergent phase – fire N parallel branches, each with a hard-wall prompt
const branches = await Promise.all(
frames.map(frame =>
withSemaphore(concurrency, () => callLLM({
systemPrompt: `${frame.vantage}\n\nFORBIDDEN: evaluation, ranking, hedging. JSON array out.`,
userPrompt: `${problem}\n\n${context ?? ""}`,
}))
)
);
// No branch ever sees another branch's output – isolation is guaranteed
// Convergent phase – single critic call runs three passes
const scored = await scoreLeaves(branches);
const clustered = await clusterByAngle(scored);
const deepened = await deepenTopK(clustered, K);
This code demonstrates the mechanical separation: the first block spawns isolated generators, while the second block performs the only convergent evaluation.
Summary
- Mechanical isolation at the API-call level prevents any evaluation during the divergent phase.
- Stateless parallel branches in
src/diverge.tsrun with hard-wall prompts that forbid ranking or hedging. - Single critic pipeline in Phase 2 (
src/score.ts,src/cluster.ts,src/deepen.ts) performs the only convergence after all divergent outputs are collected. - Three-pass evaluation (Score, Cluster, Deepen) ensures structured convergence only after full exploration.
- Temporal barrier in
src/engine.tsguarantees convergent functions execute only afterPromise.allresolves.
Frequently Asked Questions
How does ADHD prevent premature convergence during idea generation?
ADHD uses hard-wall system prompts in src/diverge.ts that explicitly forbid evaluation keywords and hedging language. Combined with stateless LLM sessions where each branch operates in isolation without shared context or KV-cache, the architecture physically prevents the model from comparing or ranking ideas during the generation phase.
What ensures that the critic phase only runs after divergence completes?
The orchestration logic in src/engine.ts uses Promise.all to wait for all parallel divergent branches to return before invoking the sequential critic functions (scoreLeaves, clusterByAngle, deepenTopK). This creates a strict temporal barrier where convergence functions cannot execute until the branches array is fully populated.
Why are the Score, Cluster, and Deepen operations separated into different files?
Separating these concerns into src/score.ts, src/cluster.ts, and src/deepen.ts allows each pass to use specialized system prompts and logic. This modular approach prevents the critic from conflating evaluation (scoring), synthesis (clustering), and expansion (deepening), ensuring that each convergence step remains methodologically distinct and traceable.
Can the number of divergent branches (N) be configured?
Yes. The N parameter representing the number of cognitive frames is configurable. The system samples this configurable number of frames from the frame library and maps them to parallel LLM calls via the frames.map() operation in src/engine.ts, allowing users to scale the breadth of divergence according to problem complexity.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →