How to Debug Failed Branches Using frameId and ideaId Tracking in ADHD

ADHD emits frameId and ideaId identifiers through every phase of its divergence-and-deepening engine, allowing you to trace exact failure points in the event stream without guessing which frame or idea caused the pipeline to collapse.

When the ADHD code-generation pipeline drops a branch or returns empty results, guessing which prompt failed wastes engineering time. The repository solves this by propagating stable frameId and ideaId values through every divergence, scoring, and deepening step. This guide shows you how to debug failed branches using frameId and ideaId tracking so you can pinpoint malformed LLM outputs and zero-idea frames by reading the event stream and the source files in src/engine.ts and src/render.ts.

How Identifiers Are Assigned in the Source Code

Frame Definitions in src/frames.ts

Each frame carries a stable id field that becomes the frameId for every branch spawned from it. In src/frames.ts (lines 5‑23), the frame objects are declared with unique identifiers the engine references during the diverge phase.

Branch and Idea Objects in src/engine.ts and src/types.ts

During the diverge phase, src/engine.ts (lines 352‑364) returns each branch as an object containing { frameId, ideas }. Every idea inside that branch receives a generated id and inherits the parent frameId. The type definitions in src/types.ts (lines 3‑22) enforce these fields, ensuring every Idea and Branch payload remains traceable.

Event Emission and CLI Visibility

The engine fires lifecycle events—frame:start, frame:done, deepen:start, and deepen:done—from src/engine.ts (lines 356‑361) that embed these IDs. On the consumption side, src/render.ts (lines 35‑44) formats branch output for the CLI so IDs remain visible when a branch fails, while src/cli.ts (lines 124‑126) writes concise status lines to process.stderr for live debugging.

Common Failure Points and How IDs Expose Them

Diverge Phase: Zero-Ideas or Malformed JSON

If the LLM call for a frame returns no ideas or unparseable JSON, the frame:done event reports count = 0. The embedded frameId tells you exactly which frame’s prompt caused the problem, so you can inspect the raw LLM response for that specific frame.

Scoring Phase: Missing or Unparseable Idea Fields

When an idea cannot be parsed or is missing required fields, the deepen:start event still surfaces the ideaId. You can locate the offending idea object in the raw LLM response and compare it against the expected shape defined in src/types.ts.

Deepen Phase: Empty Sub-Ideas

A follow-up LLM call that fails to produce sub-ideas results in a deepen:done event containing the same ideaId but a zero-length childIdeas array. This signal means the seed idea identified by that ideaId could not be expanded, and you should inspect its idea.text and the prompt sent to the model.

Rendering Phase: Omitted Branches

If the final report omits a branch, src/render.ts reveals which frameId (and optionally ideaId) were dropped. Comparing the rendered output against the earlier event log lets you verify whether the omission happened during generation or formatting.

Step-by-Step Debugging Workflow

Enable Verbose Event Logging

The default CLI already prints events, but you should run with the --verbose flag to ensure every frame:start and deepen:done line is emitted:

adhd "my problem" --frames 5 --ideas 6 --verbose

Capture the Event Stream

Pipe stderr and stdout to a file so you can grep the sequence later:

adhd "my problem" … 2>&1 | tee debug.log

Search the Log for Failure Signatures

Look for lines that indicate zero output or deepening failures:


frame:done   0 ideas (hardware-eyes)
deepen:start   a1b2c3 :: some promising idea
deepen:done    a1b2c3   (no child ideas)

  • frameId = hardware-eyes tells you which frame produced zero ideas.
  • ideaId = a1b2c3 tells you which seed idea produced no children.

Re-Run the Problematic Piece in Isolation

You can call the low-level functions directly from a Node REPL or a small script using the stored IDs. Import divergeBranch, deepenIdea, and the FRAMES array to target the exact failure:

import { divergeBranch, deepenIdea } from "./src/engine";
import { FRAMES } from "./src/frames";

// Re-run the failed frame:
const frame = FRAMES.find(f => f.id === "hardware-eyes")!;
const branch = await divergeBranch(problem, context, frame, 6, model);
console.log(branch);   // inspect raw LLM output

// Re-run the failing idea:
const idea = branch.ideas.find(i => i.id === "a1b2c3")!;
const deep = await deepenIdea(problem, idea, [], model);
console.log(deep);

Inspect Raw LLM Prompts and Responses

The frame.prompt defined in src/frames.ts and the generated idea.text give direct clues about why the model returned nothing or malformed JSON. Compare the prompt template against the actual LLM response captured in your log or REPL output.

Adjust Parameters and Iterate

After identifying the faulty frameId or ideaId, tweak the prompt, increase ideasPerFrame, or adjust the temperature. Re-run the engine and verify that the event log now shows non-zero counts for all frameIds and successful deepen:done entries.

Automating Failure Detection

A post-run script can flag any frame that produced zero ideas or any idea that failed to deepen:

import * as fs from "fs";

const log = fs.readFileSync("debug.log", "utf-8").split("\n");
log.forEach(line => {
  if (line.includes("frame:done") && /0 ideas/.test(line)) {
    const id = line.match(/\(([^)]+)\)/)![1];
    console.warn(`⚠️ Frame "${id}" produced no ideas`);
  }
  if (line.includes("deepen:done") && /no child ideas/.test(line)) {
    const id = line.match(/ideaId: (\w+)/)![1];
    console.warn(`⚠️ Idea "${id}" could not be deepened`);
  }
});

Running this script after each execution highlights exactly which frameId or ideaId needs attention before you dive into manual log inspection.

Summary

  • frameId originates from the stable id field in src/frames.ts and tags every branch during the diverge phase in src/engine.ts.
  • ideaId is generated per idea and inherited from the parent frameId, with shapes enforced in src/types.ts.
  • The engine emits frame:start, frame:done, deepen:start, and deepen:done events that carry these IDs, consumed by src/cli.ts and rendered by src/render.ts.
  • A frame:done event showing 0 ideas maps directly to a failed frameId.
  • A deepen:done event showing no child ideas maps directly to a failed ideaId.
  • You can replay isolated failures by importing divergeBranch and deepenIdea from src/engine.ts and targeting the exact IDs.

Frequently Asked Questions

Where are frameId and ideaId defined in the ADHD source code?

The frameId values come from the stable id fields declared in src/frames.ts (lines 5‑23). The ideaId values are generated during branch creation in src/engine.ts (lines 352‑364) and enforced by the type definitions in src/types.ts (lines 3‑22).

How do I know if a specific frame failed to produce ideas?

Listen for the frame:done event in the output stream. If the event reports 0 ideas and includes a frameId such as hardware-eyes, that frame’s LLM call returned no parseable ideas. You can then inspect the prompt for that exact frameId in src/frames.ts.

Can I re-run a single failed branch without executing the entire engine?

Yes. Import divergeBranch and deepenIdea from src/engine.ts, look up the specific frame from FRAMES in src/frames.ts, and call the functions with the problematic frameId or ideaId. This lets you debug the raw LLM response in isolation without rerunning the full pipeline.

What does a zero-length childIdeas array indicate during the deepen phase?

In the deepen:done event, a zero-length childIdeas array tied to a specific ideaId means the follow-up LLM call could not generate sub-ideas for that seed idea. You should inspect the idea.text and the deepening prompt to determine why the model returned an empty result.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →