DEV Community

Wissem Boughamoura
Wissem Boughamoura

Posted on

Inside the Architecture of an Autonomous Multi-Model Coding Agent Engine

Most AI coding workflows suffer from two core failure modes:

  1. The Ghost Exit: The runner finishes with exit code 0, but made zero code edits and wrote zero reports.
  2. The Context Amnesia: On multi-file tasks, models forget prior architectural decisions, rewrite already completed work, or validate their own hallucinations with "looks good to me."

To solve this, I designed wb-flow β€” an open-source, zero-dependency orchestrator that turns chaotic agent chats into a deterministic, wave-based engineering pipeline.

Here is the complete architectural map of how it works under the hood:

flowchart TD

subgraph group_setup["Setup Surface"]
  node_install_cli["Install CLI<br/>[install.js]"]
  node_initializer["Project Initializer<br/>[init.js]"]
  node_wrapper_generator["Agent Wrappers<br/>[wrappers.js]"]
  node_command_templates["Command Templates"]
end

subgraph group_planning["Planning Commands"]
  node_plan_command["Plan Procedure"]
  node_wave_parser["Plan Parser<br/>[wave_parser.js]"]
  node_next_generator["Next Generator<br/>[next.js]"]
end

subgraph group_execution["Wave Execution"]
  node_work_command["Work Procedure"]
  node_wave_runner["Wave Runner<br/>[wave.js]"]
  node_wave_generator["Wave Generator<br/>[wave_generator.js]"]
end

subgraph group_validation["Validation State"]
  node_valid_command["Validation Procedure"]
  node_plan_linter["Plan Linter<br/>[lint.js]"]
  node_run_watcher["Run Watcher<br/>[watch.js]"]
  node_report_archiver["Report Archiver<br/>[archive.js]"]
end

subgraph group_routing["Model Routing"]
  node_wave_router["Model Router<br/>[wave_router.js]"]
  node_model_manager["Model Manager<br/>[model.js]"]
  node_model_catalog["Model Catalog<br/>[models.json]"]
end

node_developer(("Developer"))
node_coding_agent(("Coding Agent"))
node_model_providers(("Model Providers"))

node_developer -->|"requests workflow"| node_coding_agent
node_coding_agent -->|"runs bootstrap"| node_install_cli
node_install_cli -->|"starts setup"| node_initializer
node_initializer -->|"writes wrappers"| node_wrapper_generator
node_initializer -->|"copies templates"| node_command_templates
node_wrapper_generator -->|"renders procedures"| node_command_templates
node_coding_agent -->|"runs planning"| node_plan_command
node_coding_agent -->|"runs work"| node_work_command
node_coding_agent -->|"runs validation"| node_valid_command
node_work_command -->|"starts wave"| node_wave_runner
node_valid_command -->|"starts validation"| node_wave_runner
node_wave_runner -->|"parses plan"| node_wave_parser
node_wave_runner -->|"generates dispatch"| node_wave_generator
node_wave_generator -->|"reads task cells"| node_wave_parser
node_wave_generator -->|"routes roles"| node_wave_router
node_wave_router -->|"reads catalog"| node_model_catalog
node_model_manager -->|"updates catalog"| node_model_catalog
node_model_manager -.->|"probes models"| node_model_providers
node_wave_generator -.->|"dispatches work"| node_model_providers
node_coding_agent -->|"runs lint"| node_plan_linter
node_plan_linter -->|"checks structure"| node_wave_parser
node_coding_agent -->|"watches runs"| node_run_watcher
node_coding_agent -->|"archives reports"| node_report_archiver
node_coding_agent -->|"derives next"| node_next_generator
node_next_generator -->|"reads plan"| node_wave_parser

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
class node_install_cli,node_initializer,node_wrapper_generator,node_command_templates toneBlue
class node_plan_command,node_wave_parser,node_next_generator toneAmber
class node_work_command,node_wave_runner,node_wave_generator toneMint
class node_valid_command,node_plan_linter,node_run_watcher,node_report_archiver toneRose
class node_wave_router,node_model_manager,node_model_catalog,node_developer,node_coding_agent,node_model_providers toneIndigo

The 5 Architectural Pillars

1. Setup Surface (init.js, wrappers.js)

Instead of locking developers into a single closed ecosystem, wb-flow generates native command wrappers for Claude Code, Codex, OpenCode, Gemini CLI, and Cursor. Any assistant can invoke the exact same slash commands.

2. Planning Commands & DAG (wave_parser.js, next.js)

The plan is stored in Markdown tables directly on disk. The parser extracts a Directed Acyclic Graph (DAG) and partitions tasks into parallel execution Waves (Wave A, Wave B, Wave C).

3. Wave Runner (wave.js, wave_generator.js)

Runs independent tasks in parallel using isolated workers. Before any task runs, a workspace hash snapshot is taken.

4. Validation & Gatekeeper (lint.js, watch.js, archive.js)

  • Gate 1: Checks the runner process exit code.
  • Gate 2 (Content-Hash Gate): Compares workspace file hashes. If the agent exited cleanly with code 0 but touched zero files, it is marked as a NO-OP and rejected.
  • Independent Model Validation: An entirely different LLM is dispatched to run unit tests and score the worker's evidence before any task can be marked βœ… Valid.

5. Dynamic Model Routing (wave_router.js, models.json)

You can assign specific models to specific roles:

  • Lightweight models for mechanical tasks.
  • Heavy reasoning models (e.g. Claude Opus, GPT-5) for planning.
  • Independent models for adversarial validation.

Try it out

The entire engine has zero external npm dependencies and runs on plain Node.js.

If you find this approach interesting or want to test multi-model wave execution in your repo, star the repo on GitHub and let me know your thoughts in the comments!

Top comments (0)