This is a multi-part series about Invoker.
https://github.com/Neko-Catpital-Labs/Invoker/
Invoker is a persisted workflow engine for software work. It runs a DAG of tasks in isolated environments, records execution history, and composes the result through git branches, merge gates, and review.
Invoker applies familiar workflow, build-system, and CI ideas to software work whose outputs are code changes that need to be composed and reviewed.
That is why it looks adjacent to build systems, workflow orchestrators, durable execution engines, and CI systems without being identical to any one of them. The core ideas are familiar. The main difference is that Invoker applies them to work whose outputs are code branches and review states rather than only logs or status bits.
What The System Is Optimizing For
The architecture follows from a straightforward constraint: software work is not just a list of steps. It is a sequence of state transitions that can race, fail, be retried, produce code, conflict with other code, and require human approval before it should count as integrated.
Many workflow tools are good at showing what is happening right now. Invoker is designed to also preserve what happened, which result is currently authoritative, and what should happen next when a workflow is resumed, retried, edited, or reviewed.
To that end, the architecture focuses on being as simple and pragmatic as possible treating correctness and repeatability as first class citizens and speed of execution as a distant 3rd. We treat state changes as events going through a series of pipes and transformations, adopting a functional programming approach. We also eliminate as much coordination and exceptions to the invariants as possible. This also means eliminating caching to retain a single source of truth.
As a result, we only have 5 mutexes across the 90k line codebase at the boundaries between layers with none within the package itself.
Why Persistence And A Single Writer Matter
One of Invoker’s main architectural rules is that workflow state is not owned by whichever UI or CLI process happens to be open. It is persisted and it has an owner.
This is a direct response to the persistence model rather than a stylistic preference. Invoker’s persistence layer uses sql.js, which means multiple writable processes can overwrite each other’s state if they all treat themselves as authoritative.
So Invoker uses a single-writer owner model: one process owns writable access, and other surfaces either delegate mutations to that owner or open the data read-only.
From there, several other design choices follow:
- Why mutations go through a narrow serialized command path
- Why the database is treated as the authority instead of an in-memory graph
- Why GUI, headless, and Slack are surfaces on top of the same engine rather than separate implementations.
The familiar shape: surfaces send commands into one workflow engine, which persists state, schedules runnable work, dispatches executors, and then composes code through git and review.
How Invoker Works
In Invoker, code change management is part of the execution model itself. A task does not merely succeed or fail. It can produce a branch, create an attempt record, become stale when upstream inputs change, pause for input, or stop at a merge gate that requires review.
In other words, Invoker does not treat git, review, and approval as side effects that happen after execution. It models them as workflow states that affect what the current workflow result means.
It is for this reason that we effectively ban self-mutations without creating a new state.
The architecture can be read from left to right: borrowed ideas shape the engine, the engine runs the workflow, and the outputs are code artifacts and review states, not just booleans.
The Happy Path
Imagine a plan with two tasks: one refreshes dependencies and one runs tests after that work completes.
name: ci-hardening
baseBranch: main
tasks:
- id: deps
description: Refresh lockfile and install dependencies
command: pnpm install --frozen-lockfile
- id: tests
description: Run unit tests
command: pnpm test
dependencies: [deps]
One workflow tour in time order, from plan to persisted state, execution, branch outputs, and merge/review.
- The plan is parsed into a workflow definition. Defaults are applied, task ids are validated, and dependency edges become part of the stored workflow model.
- The workflow is persisted before it is acted on. Invoker stores durable task and workflow state so the workflow can be resumed, queried, or mutated later without depending on transient UI memory.
- The scheduler decides what is ready. The graph determines which tasks are blocked, runnable, completed, or stale, and the scheduler drains runnable work under a concurrency cap.
- The task runner builds an explicit work request. Rather than handing execution an ambiguous blob of context, Invoker assembles the selected executor, upstream branch information, and task metadata into a clear request/response protocol.
- The executor runs the task in isolation. That might be a worktree, a Docker container, or an SSH workspace. Isolation is important because task execution is supposed to produce outputs deliberately, not mutate a shared repo invisibly.
- The task produces more than a success bit. A finished attempt can produce branch metadata, commit information, logs, and other execution records that downstream tasks consume.
- Results converge at a merge gate. Merge and review policy are not a side conversation. The workflow can stop at an explicit gate where branch composition, conflict handling, and human approval decide whether the work should advance.
- Surfaces stay aligned because they call the same engine. Desktop, headless, and Slack do not each invent their own workflow semantics. They invoke the same actions on top of the same persisted state.
Selected Attempts And Staleness
Like other systems that separate execution history from current state, Invoker allows a task to have multiple attempts, but only one selected attempt is currently authoritative for downstream composition.
That distinction is what gives the system a build-system-like notion of invalidation. If an upstream task is rerun and a new attempt becomes selected, downstream work may no longer be based on the right inputs. Invoker can then mark those downstream tasks as stale instead of pretending the old result is still valid. Yes, this is the same idea as ABI and cache invalidations.
As a natural consequence, any retry or recreate invalidates pending attempts and tasks. Recreates are especially unique in that they effectively create a new task that is divorced from the previous task that it recreates, giving you a clean “start over” state.
This is one of the places where a workflow engine differs from an ordinary task tracker. In a normal task board, “done” is often final. In Invoker, “done” may stop being authoritative when the selected upstream lineage changes.
Build system handle this today by forcing the user to customize and write the invalidation rules themselves. We adopt a more pragmatic and simple approach to do a complete invalidation. After all, with AI, there are no guarantees that it will always follow instructions.
Changes to upstream dependencies may invalidate whatever is downstream; there is no guarantee.
Experimentation
Invoker also supports a familiar fan-out and fan-in pattern for cases where one workflow path is not enough. A task can spawn experiment variants, let those variants run as separate branches or attempts, and then converge later through reconciliation or an explicit merge or review step.
This is not a separate subsystem bolted onto the side. It follows from the same execution model as the rest of the system: immutable attempts, explicit branch outputs, persisted workflow state, and shared commands for selecting which result should count as authoritative.
That makes experimentation a normal workflow operation rather than an informal process outside the tool. Invoker can fan work out, preserve the resulting lineage, and later let a human or workflow action select the result that should continue downstream.
This is a particularly interesting feature since during a workflow, even the user may not know what exactly is the right answer. Instead of doing the work outside in worktrees and losing the work, Invoker allows you to make the experiment legible from inside the graph.
That also means selecting a different combination of accepted experiments will invalidate the graph downstream and the final result.
Why Merge Gates Matter
A merge gate is the point where branch composition, conflicts, and approval become explicit workflow state.
It is not just “the last task.” It is a workflow-owned convergence point where code branches are composed under explicit policy. That policy can include conflict handling, PR or review steps, and approval before the workflow is allowed to count as integrated.
Design Principles
- Make state explicit. Persist workflow meaning so the system can be resumed, inspected, and audited.
- Keep mutation paths narrow. Serialize workflow-affecting actions instead of letting every caller write state independently.
- Keep graph logic pure when possible. Separate DAG reasoning from execution details so readiness and invalidation stay legible.
- Use hard package boundaries. Enforce architectural layers rather than relying on convention alone.
- Prefer official control paths over shortcuts. Use headless or owner-mediated actions instead of poking the database directly.
- Make verification executable. Prefer concrete commands and reproducible checks over vague claims that something was “tested.”
What Invoker Primarily Borrows From Other Systems
The comparisons are useful mainly as orientation.
- From build systems like Bazel: explicit inputs, invalidation, and structured worker-style execution.
- From orchestrators like Airflow: DAG scheduling, visible task states, and operator-facing progress views.
- From durable execution engines like Temporal: persisted workflow state and multiple clients talking to one engine.
- From CI systems: isolated execution, branch-oriented outputs, approvals, and convergence before integration.
The key point is not that Invoker imitates those tools literally. It reuses their familiar patterns in a workflow whose outputs are code changes that need to be composed, reviewed, and kept consistent over time.
A bigger list is in the appendix.
Bottom Line
Invoker is a persisted workflow engine that runs a DAG of work in isolated environments and composes the result through branches, merge gates, and review policy.
That is why it reads as part build system, part workflow orchestrator, part CI runner, and part control plane for software execution. Once you decide that workflow state should be explicit and durable, the rest of the architecture is mostly the expected consequence.
In the next blog, I will outline my experiences with vibe coding Invoker and why I am not as bullish on AI as much as many other companies.
Appendix: Explicit Mapping Charts
For legibility, the charts can be found here.
