DorkOS
GuidesThe /flow workflow

The dials

Every /flow config knob in one place, what it does, its default, and when to turn it.

The dials

/flow ships with sane defaults. Out of the box, it already runs the loop the way it's meant to: safe, sequential, and asking before anything risky. You don't need to touch anything on this page to use /flow.

Come here when you want to change one specific thing, like turning on the autonomous loop or getting pinged when a question is waiting on you. Every dial below is a real, working setting: nothing here is a wish-list item.

Quick win: turn on the autonomous loop

By default, /flow only runs when you type a command. To let it work on its own schedule (a "tick" that claims one ready item and carries it forward), flip one flag.

Open .dork/plugins/flow/skills/flow-drain/SKILL.md and find the frontmatter block at the top:

---
# before
enabled: false
cron: '0 * * * *'
---

Change enabled to true:

---
# after
enabled: true
cron: '0 * * * *'
---

Autonomous mode also needs a running DorkOS server. That's what actually fires the schedule. Manual /flow commands don't need the server running at all. If you want to turn it back off in a hurry, /flow:pause flips this same flag for you.

Everything past this point is reference material: the full list of dials, grouped by what they control. Skim the headings and stop wherever you've found what you needed.

Reference

Where the dials live

Almost every setting lives in one file. The autonomous schedule is the one exception:

FileOwnsValidated by
.dork/plugins/flow/config/config.jsonThe engine's rulebook: autonomy, eligibility, involvement, gates, loops, ingestion.config.schema.json (linked at the top of the file)
.dork/plugins/flow/skills/flow-drain/SKILL.md (frontmatter)The Pulse schedule: when the autonomous tick fires, and whether it fires at all.The Pulse task loader

That schema file isn't just documentation: it's enforced. If you type a value that isn't on the allowed list, or set a number below the floor /flow requires, the file fails to load instead of quietly misbehaving. Most code editors also read the schema reference at the top of config.json and give you autocomplete and inline errors as you type, the same way editors validate a package.json.

A per-repo WORKFLOW.md override is part of the plan for config.json, but nothing reads it yet. Adding one today has no effect. That piece is planned for a later, server-backed build.

A few dials below describe a schedule (priority, intervalMs) that a future always-on process will run unattended. Today, the Pulse cron (see the Cadence section) walks the loops in that same priority order once per tick, but nothing fires them independently in the background yet. Each such dial is flagged where it appears.

Cadence: the Pulse schedule

Autonomous mode runs on DorkOS Pulse, a file-based scheduler. Its timing lives in the frontmatter of .dork/plugins/flow/skills/flow-drain/SKILL.md, not in config.json:

DialWhat it doesDefaultWhen to change
enabledWhether the autonomous tick fires at all.falseSet true to turn on the loop (it's off until you opt in).
cronWhen each tick fires. One tick claims and carries one item forward.0 * * * * (top of each hour)Tighten for a busier queue; loosen to ease up on the tracker.
timezoneThe timezone the schedule is evaluated in.America/Los_AngelesMatch your working hours.
max-runtimeHard ceiling on how long a single tick may run.2hRaise for large items; lower to fail fast.
permissionsHow much the unattended session is allowed to do without asking.acceptEditsTighten for a more cautious unattended posture.

The schedule ships enabled: false. Autonomous mode also needs a running DorkOS server (it hosts the scheduler); manual /flow doesn't. /flow:pause flips this same enabled flag to halt the whole schedule from one place.

Autonomy

How aggressively the engine takes work on its own, under autonomy in config.json:

DialWhat it doesDefaultNotes
defaultWhether /flow runs all the way to a gate, or one stage at a time."auto""auto" drains the ready queue; "manual" runs one stage and stops.
concurrencyWork on one item at a time, or several at once."sequential"Today it's always sequential. "parallel" is reserved for later.
wipCap.globalMax items in flight across all projects at once.2The ceiling on total concurrent work.
wipCap.perProjectMax items in flight for a single project.1Keeps one project from hogging the loop.
seatWhich autonomous runner hosts the loop."pulse""pulse" is the only option today; more are planned.

Setting concurrency: "parallel" is accepted, but it does nothing yet: today's engine always processes one item at a time. Leave it "sequential" for now.

Eligibility

Dispatch (the step that hands an item to the engine) only ever picks up work that's genuinely ready. Readiness is marked by the agent/ready label, applied at these stages:

TRIAGE applies agent/ready (plus a stage/* label) when it accepts a piece of work, whether it's simple enough to go straight to EXECUTE or complex enough to need IDEATE first.

DECOMPOSE applies agent/ready to each task it breaks work down into.

Dispatch filters down to ready items, then ranks them. If the queue is empty but there's work waiting to be shaped, it says so ("0 ready, N shapeable: run a triage pass?") instead of going quiet.

agent/ready is a label on an item in your tracker, not a setting in config.json: the shaping stages produce it, you don't set it directly. What you can tune is how the ready items get ordered, under dispatch:

DialWhat it doesDefault
dispatch.rankThe order of tiebreakers used to rank ready items.["unblockers", "priority", "projectStatus", "type", "size", "age"]
dispatch.sizeOrderAt the size tiebreaker, prefer smaller items or larger ones."small-first"

Dispatch also skips anything not actually dispatchable: items with open blockers, items in a finished or canceled project, items already at the WIP cap, and items your ownership settings don't allow it to touch. agent/ready is the dial you feed; everything else is a safety check.

Involvement

When the engine asks you first versus just proceeds, under involvement:

DialWhat it doesDefault
commsHow the engine decides its tone and channel when it talks to you."infer-from-trigger"
calibration.proceedSilentlyWhenConditions under which the engine acts without asking first.["reversible", "confident"]
calibration.alwaysAskThe floor: these always require asking, no exceptions.["irreversible-or-destructive", "outward-facing", "secrets-or-spend", "scope-change"]
calibration.stageBias.intakeDefault posture during the shaping stages (IDEATE, SPECIFY)."ask"
calibration.stageBias.executionDefault posture during EXECUTE."proceed-and-log"
nudge.relaySend a Relay ping when a question is waiting on you.false
nudge.telegramSend a Telegram ping when a question is waiting on you.false

The comms setting is the one that shapes tone day to day. The engine reads where a request came from (you, live in a /flow session, versus an unattended tick) and checks it against the calibration rules above. That's why IDEATE tends to ask a lot and EXECUTE tends to just proceed: it's one rule reacting to context, not a separate switch per stage.

calibration.alwaysAsk is a floor, not a preference. The file requires at least one entry here (an empty list fails to load), so nobody can accidentally configure the engine into asking for nothing. You can add more items to the floor. You can't remove the last one.

The nudge settings are the only way to get pinged outside your tracker. If you're the only person on the account (the common setup), a parked question otherwise shows up only as a tracker comment and an agent/needs-input label, easy to miss. Turning on nudge.relay or nudge.telegram adds a real notification so a stuck question doesn't sit there unnoticed.

Gates

The checkpoints between stages, under gates:

DialWhat it doesDefaultNotes
planApprovalStop after DECOMPOSE for your sign-off before EXECUTE starts.falseOff by default, so DECOMPOSE flows straight into EXECUTE. Turn on for a checkpoint.
review.requireCiGreenRequire green CI before the review gate clears.truePart of the review gate, which is always on.
review.reapproveOnFunctionalChangeAsk for a fresh approval if the change shifts after review.trueStops a stale approval from covering a diff that's since changed.
circuitBreaker.estimateMultiplierTrip a stop if actual work runs N times over the estimate.2Lower to catch runaway items sooner.
circuitBreaker.tokenBudgetHard token ceiling for a single item before the breaker trips.2000000Raise for large items; lower to cap spend.

The human-review gate (after VERIFY) is always on. There's no setting that turns it off. The plan-approval gate (after DECOMPOSE) is the one you toggle, and it's off by default.

Loops

/flow runs on a small set of internal jobs called reconcilers, one per loop. Each takes the same three dials, under loops.<id>:

  • enabled: whether this loop runs at all.
  • priority: lower numbers run earlier in a tick and win if two loops want the same item at once.
  • intervalMs: how often the scheduler considers this loop due to run (in milliseconds).
LooppriorityintervalMsWhat it does
recovery10300000 (5 min)Picks back up or restarts work that got orphaned mid-flight.
inbox2060000 (1 min)Checks for replies on parked questions and resumes that work.
review25300000 (5 min)Checks the auto-merge rules at the review gate.
dispatch30300000 (5 min)Claims the top-ranked ready item and carries it to its gate.
triage403600000 (1 hour)Classifies incoming work so dispatch never runs out of items.
hygiene5021600000 (6 hours)Housekeeping: flags stuck items, cleans up stale records.

The order isn't arbitrary: recovery goes first because losing in-flight work is the one mistake nobody forgives. Inbox comes next because a person waiting on a reply is the cheapest thing to unblock. Review and dispatch follow, and triage and hygiene bring up the rear as slow background chores. Want replies to resume faster? Lower loops.inbox.intervalMs. Want fewer tracker reads? Raise it.

These priority and intervalMs values describe a schedule for a future always-on process that fires each loop independently. Today, the Pulse tick (see Cadence above) runs the whole set in priority order once per scheduled cron fire, in the same order shown here. The intervalMs numbers are already set to the cadence that future process will use.

Ingestion

How updates from your tracker reach the engine, under ingestion:

DialWhat it doesDefaultNotes
producerWhether /flow polls the tracker or waits for a push."poll""webhook" is reserved for later; it isn't built yet.
pollIntervalMsHow often polling checks the tracker (in milliseconds).60000 (1 min)Lower for faster pickup; raise to ease up on the tracker's API.

Setting producer: "webhook" is accepted but does nothing today: it needs an inbound endpoint that doesn't exist yet. Leave it "poll" until that ships.

Which dial should I touch?

If you want to...Turn this
Turn the autonomous loop on.dork/plugins/flow/skills/flow-drain/SKILL.md frontmatter enabled: true (server required)
Make the autonomous tick fire more oftenSame file's cron field
Run more than one item at a timeNot yet possible; today it's always autonomy.concurrency: "sequential"
Cap how much work is in flightautonomy.wipCap.global / wipCap.perProject
Get pinged when a question is waiting on youinvolvement.nudge.relay / nudge.telegram
Require sign-off on the plan before EXECUTEgates.planApproval: true
Cap spend per itemgates.circuitBreaker.tokenBudget
Make replies resume fasterloops.inbox.intervalMs (lower it)
Ease up on tracker readsingestion.pollIntervalMs (raise it)
Turn a loop offloops.<id>.enabled: false
Change which loop wins when two collideloops.<id>.priority

Next steps