# Dot factory – a spec to describe complex (agentic) workflows

> Source: <https://redbeardlab.github.io/dotFactory/>
> Published: 2026-10-18 05:11:32+00:00

A spec, not a runtime

# Describe the workflow.

Not the implementation.

        dotFactory is a small language for automations with and without AI agents.
        You write the *dataflow* — typed values moving through triggers,
        steps and sinks — and leave the object models, async runtimes and
        dependency-injection frameworks behind.
      

No runtime to install. No platform to sign up for. The interface is the deliverable.

The problem

## You are thinking in dataflow. You are writing everything else.

A trigger feeds a step, the step calls an agent, the output feeds another step, maybe a retry, then a sink. That picture is simple. But the moment you open an editor, the language makes you manage machinery that has nothing to do with it.

### What you actually mean

- The trigger fires on a GitHub issue.
- The step drafts a reply.
- The reviewer approves or sends it back.
- The sink pings the team on Slack.

### What the language asks about

- `class` hierarchies & metaclasses
- interface satisfaction & goroutine lifecycle
- ownership, borrowing, lifetimes
- the event loop, the DI wiring, the framework

        When the language fits the domain, the code becomes a direct
        translation of intent. Everything else follows from that.
      

The whole vocabulary

## Eight concepts. No spare parts.

Every idea earns its place by being needed to describe how work flows through a graph. This is the entire language.

### Type

Structural records. Fields and shapes — no classes, inheritance, or constructors.

```
type Issue = { number: int }
```

### Trigger

Admits one seed value and holds it constant for the whole run. The host supplies the kind.

```
trigger issue_opened:
  kind: github
  config: { path: "/api/github/webhook", target: { one: { repo: "acme/widgets" } } }
```

### Step

Where work happens. Invokes a unit and binds its typed outputs.

```
step draft:
  runner: agent
```

### Runner

How a step runs: a shell command, a container, or an agent behind a typed interface.

```
runner agent:
  kind: opencode
```

### Binding

Named edges that move values between ports. The bindings *are* the program.

```
in: { issue: in.issue }
```

### Loop

A `prev` (loop-carried) binding re-fires a step with feedback — a retry, not control flow.

```
prev feedback: review.result
```

### Sink

An effect. Inside a loop it fires each pass; downstream it fires once.

```
sink result:
  kind: file
  config: { path: "out/result.json" }
```

### External port

A bridge to the outside, implemented by the host. Secrets are opaque — redacted everywhere.

``` php
external port credential
  (key: text) -> secret
```

Anatomy of a factory

## One screen of code, read top to bottom.

        The smallest factory that still shows the three things a factory is for:
        a credential, an agent runner, and a review loop with a bounded retry.
        **Pick a part** to see what it does.
      

The execution model

## A run is a round of firings until nothing can fire.

        Press play. The trigger admits a GitHub issue, the loop runs ```
draft →
        review
```
, the reviewer sends the draft back once, then approves.
        The `progress` sink fires **inside** the loop —
        once per pass — while `notify` waits downstream and fires
        **once**.
      

**0**

Why a spec

## Write the factory once. Host it anywhere.

        The factory describes *what should happen*. The host supplies
        *how*: which trigger kinds exist, which runner backends run, where
        sinks write, how ports resolve. Those are adapter concerns — so they can
        compete without locking in your workflows.
      

trigger issue_opened: kind: github

step draft: runner: agent

sink notify: kind: slack

- **Local** — docker containers & the OpenCode server
- **Cloudflare** — workers as steps, R2 as sink
- **AWS / GCP** — Lambda/Batch steps, SQS triggers
- **Your own** — register the kinds your domain needs

The spec defines the concepts; implementations follow. Tooling — validators, diagrammers, editors — can be built against the interface, independently of any one host.
