CatsCrdl

CatsCrdl

Daniel's thoughts on infosec

Flowwork

An AI Coding Hardness

Daniel Wyleczuk-Stern

7-Minute Read

Flowwork - An AI Coding Hardness

Background

I’m a fairly big believer in the power of GenAI for coding. I know there’s active debate in this area. If you’re not a fan of LLMs for coding, this blog may not be for you. Personally, I believe that humans will not write code for much longer (with some niche exceptions). For the current capabilities of LLMs, I do not think they’re good enough to “vibe-code” production level code. Harnesses that enforce strict coding standards with multiple, layered approaches to enforce them is essential. After almost a year of iteration, I’ve got “flowwork” in a good enough state where I feel comfortable writing about it. (Why “flowwork”? It was originally called workflow and I called v2 flowwork because I’m not creative.)

Inspiration

While I was working on this before I read about Gas Town, the blog post inspired me to really level up the capabilities of my harness. However, I don’t have unlimited budget, so I really needed to optimize for tight quality loops. I also didn’t want the agents to pick up work (yet), but instead, I drive ordering.

The Flow

A key aspect of my development philosophy is that it should code like humans code. This means think before code. Thus, I wanted to ensure that my agents always planned before coding. I could enforce this with prompts, but fortunately there’s tools for this. After some exploration, I landed on LangChain, which essentially allows you to abstract LLM applications into constituent steps. So, essentially, rather than one massive prompt that says “do A, then B, then C”, I have multiple prompts for each step of the development process, and LangChain lets me instrument ordering easily. Without further ado, here’s the flow

Flowwork LangGraph Architecture

Here’s how a task moves through the pipeline, stage by stage, from design to a merged PR.

  ┌─────────────────────────────────────────────────────────────────────┐
  │                           START                                      │
  └────────────────────────────┬────────────────────────────────────────┘
                               │
                               ▼
          ┌────────────────────────────────────────┐
          │           PIPELINE STAGES              │
          │    (Configured via state.json DAG)     │
          └────────────────────────────────────────┘
                               │
            ┌──────────────────┼──────────────────┐
            ▼                  ▼                  ▼
       ┌─────────┐        ┌──────────┐      ┌───────────┐
       │ design  │───────▶│design_   │─────▶│   plan    │
       │         │        │  review  │      │           │
       └─────────┘        └──────────┘      └───────────┘
                                                 │
                                                 ▼
                                           ┌──────────────┐
                                           │ plan_review  │
                                           └──────┬───────┘
                                                  │
                                                  ▼
                                          ┌──────────────────┐
                                          │ execution_scope  │ ◄─── CONTROL NODE
                                          │ (fullstack/split)│
                                          └────────┬─────────┘
                                                   │
                  ┌───────────────────────────────┼────────────────────────┐
                  ▼                               ▼                        ▼
          ┌──────────────┐            ┌──────────────────┐      ┌──────────────┐
          │ test_design  │            │ test_design      │      │ test_design  │
          │ (fullstack)  │            │ (frontend-only)  │      │ (backend-only)
          └──────┬───────┘            └────────┬─────────┘      └──────┬───────┘
                 │                             │                       │
                 ▼                             ▼                       ▼
       ┌─────────────────────────────────────────────────────────────────┐
       │        impl_frontend ─────▶ impl_backend ─────▶ test_impl       │
       │          (Kimi)                (Claude)         (Claude)        │
       └─────────────────────────────────────────────────────────────────┘
                               │
                      ┌────────┴────────┐
                      ▼                 ▼
            ┌──────────────────┐   ┌──────────┐
            │ security_review  │──▶│ bazel_   │
            │  (can inline-fix)│   │  suite   │
            └────────┬─────────┘   └──────────┘
                     │                  │
                     ▼                  ▼
            ┌──────────────┐    ┌────────────────┐
            │  preflight   │───▶│ preflight_     │  ◄─── CONTROL NODE
            │              │    │ remediation    │
            └──────┬───────┘    └────────────────┘
                   │                   │
                   └───────┬───────────┘
                           ▼
                ┌────────────────────┐
                │       e2e          │
                │   (browser tests)  │
                └────────┬───────────┘
                         ▼
                ┌────────────────────┐
                │     quality        │
                │ (lint/type/format) │
                └────────┬───────────┘
                         ▼
                ┌────────────────────┐
                │    validation      │
                └────────┬───────────┘
                         ▼
                ┌────────────────────┐
                │    bug_finder      │  ◄─── Optional, CI failures only
                │     (optional)     │
                └────────┬───────────┘
                         ▼
                ┌────────────────────┐
                │   pr_creation      │
                │ (push + create PR) │
                └────────┬───────────┘
                         ▼
                ┌────────────────────┐
                │    pr_monitor      │
                │  (watch CI gates)  │
                └────────┬───────────┘
                         ▼
                ┌────────────────────┐
                │  await_review      │  ◄─── CONTROL NODE: human gate
                │ (human approval)   │
                └────────┬───────────┘
                        ▼
                ┌────────────────────┐
                │  retrospective     │
                └────────┬───────────┘
                        ▼
                ┌────────────────────┐
                │    finalize        │  ◄─── CONTROL NODE: cleanup
                └────────┬───────────┘
                        ▼
  ┌─────────────────────────────────────────────────────────────────────┐
  │                            END                                       │
  └─────────────────────────────────────────────────────────────────────┘

Error Recovery Paths

  ┌─────────────────────────────────────────────────────────────────┐
  │ DESIGN/PLAN/IMPL/TEST/SECURITY/QUALITY  (any stage fails)      │
  └─────────────────────┬───────────────────────────────────────────┘
                        │ error
                        ▼
             ┌─────────────────────┐
             │   local_fix         │  ◄─── Reset impl chain + retry
             │ (per-stage budget)  │      (bounded by attempt count)
             └──────┬──────────────┘
                    │
          ┌─────────┴─────────┐
          ▼                   ▼
     retry impl          exhausted:
     budget >0           gate/escalate

  ┌─────────────────────────────────────────────────────────────────┐
  │ BAZEL_SUITE / PREFLIGHT  (infra error: rate limit, timeout)    │
  └─────────────────────┬───────────────────────────────────────────┘
                        │ infra_error
                        ▼
             ┌──────────────────────┐
             │  infra_retry         │  ◄─── Bounded retry for transients
             │                      │
             └──────┬───────────────┘
                    │
          ┌─────────┴──────────┐
          ▼                    ▼
     retry stage          fail: local_fix

  ┌──────────────────────────────────────────────────────────────────┐
  │ PR_MONITOR  (CI fails after PR created)                         │
  └──────────────────────┬────────────────────────────────────────────┘
                         │ ci_failure
                         ▼
            ┌──────────────────────┐
            │    ci_fix            │  ◄─── Full pipeline rerun
            │ (reset from design)  │
            └──────┬───────────────┘
                   │
                   └──────▶ design (restart)

  ┌──────────────────────────────────────────────────────────────────┐
  │ QUALITY  (reviewer verdict stale after impl commits)            │
  └──────────────────────┬────────────────────────────────────────────┘
                         │ stale_verdict
                         ▼
            ┌──────────────────────┐
            │   reanalysis         │  ◄─── Re-run staleness check
            │                      │
            └──────┬───────────────┘
                   │
                   └──────▶ test_impl (reset downstream)

Control Flow Nodes

  ┌──────────────────────────────────────────────────────────────────┐
  │                    CONTROL FLOW NODES                            │
  │             (Deterministic Python logic, not LLM)                │
  └──────────────────────────────────────────────────────────────────┘

  • execution_scope
    └─ Route based on frontend/backend/fullstack lane selection

  • test_phase_scope
    └─ Determine which test stages to run

  • frontend_phase_advance / backend_phase_advance / test_phase_advance
    └─ Serialize multi-lane execution

  • local_fix
    └─ Error recovery: reset impl chain, retry with budget

  • preflight_remediation
    └─ Fix deterministic preflight violations or gate

  • infra_retry
    └─ Handle transient errors (rate limits, timeouts)

  • ci_fix
    └─ Full pipeline rerun for post-PR CI failures

  • reanalysis
    └─ Detect stale verdicts, reset reviewers if code changed

  • plan_replan
    └─ Re-plan if dependencies shift

  • await_review
    └─ Human gate: pause for approval before merge

  • finalize
    └─ Terminal: record telemetry, cleanup

Design Principles

Flow Like a Developer

Design, plan, implement, review, test, review, PR. That’s the flow in a nutshell. That’s how I code major features, and that’s how I wanted the agent to perform.

Specificity Matters

When I overloaded a specific node, it would fail more often or produce worse code. Therefore, I ended up breaking some nodes into smaller nodes and it resulted in better performance.

Right Model for the Right Node

I generally reserve the higher powered models (e.g. Opus) for more critical nodes in Design and Plan whereas lesser models (like OpenAI Codex Terra) handle implementation.

Don’t Implement All at Once

Sometimes a plan would produce 5 steps. If I sent all of that to implement at once, it would result in missing features a lot. So now, the plan produces a checklist with 5 steps and implement is called 5 times to implement one aspect at a time.

Static Controls Are Good

Strict type checks, code coverage, end to end testing, etc are essential for ensuring code by agents is produced at a high enough quality.

Review Everything

Every single step has a separate prompt (and sometimes a different model) review that stages output. LangChain easily supports loops until a review stage passes. If a review node is failed a number of times, it will halt and require human review.

Multiple Model Support

Because models are invoked via the CLI, it supports Claude, Codex, Kimi, and DeepSeek with easy extension for others.

State is Handled in Json

Rather than rely on controller memory, state is handled via a well structured json file. Corruptions are easily surfaced as errors, allowing me to go in and debug.

Log all the things

The above json file logs almost everything allowing for easy analysis after implementation.

UI

I’ve originally run this via the CLI, but I thought it’d be cool to have a console. Thus, “flowwork console”. (I’m not good at naming.)

I use git worktrees for multi-tasking. I select an issue number and the coordinator auto assigns it to a slot and starts the chain.

UI One

I have the ability to track progress and ask the LLM about issues or progress as it goes.

UI Two

Human Responsibilities

With so much of the development driven by LLMs, where does my responsibility lie? Primarily in a few areas: order of operations, code review, and well scoped requirements. With time and tokens becoming a limiting factor, know what features to prioritize has become more important. While I don’t review every line of code, I do review major PRs for alignment with repo standards. And crafting well-scoped tickets (that aren’t overscoped) with clear requirements and acceptance criteria is essential to having a working PR at the end.

Conclusion

If you’re doing heavy LLM development, consider investing in developing an agentic harness. I’m sure there’s also some available online, but I found that developing my own really helped me get comfortable with my AI development process and my repo.

Say Something

Comments

Recent Posts

Categories

About

A random collection of thoughts on cybersecurity.