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:
| File | Owns | Validated by |
|---|---|---|
.dork/plugins/flow/config/config.json | The 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:
| Dial | What it does | Default | When to change |
|---|---|---|---|
enabled | Whether the autonomous tick fires at all. | false | Set true to turn on the loop (it's off until you opt in). |
cron | When 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. |
timezone | The timezone the schedule is evaluated in. | America/Los_Angeles | Match your working hours. |
max-runtime | Hard ceiling on how long a single tick may run. | 2h | Raise for large items; lower to fail fast. |
permissions | How much the unattended session is allowed to do without asking. | acceptEdits | Tighten 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:
| Dial | What it does | Default | Notes |
|---|---|---|---|
default | Whether /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. |
concurrency | Work on one item at a time, or several at once. | "sequential" | Today it's always sequential. "parallel" is reserved for later. |
wipCap.global | Max items in flight across all projects at once. | 2 | The ceiling on total concurrent work. |
wipCap.perProject | Max items in flight for a single project. | 1 | Keeps one project from hogging the loop. |
seat | Which 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.
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:
| Dial | What it does | Default |
|---|---|---|
dispatch.rank | The order of tiebreakers used to rank ready items. | ["unblockers", "priority", "projectStatus", "type", "size", "age"] |
dispatch.sizeOrder | At 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:
| Dial | What it does | Default |
|---|---|---|
comms | How the engine decides its tone and channel when it talks to you. | "infer-from-trigger" |
calibration.proceedSilentlyWhen | Conditions under which the engine acts without asking first. | ["reversible", "confident"] |
calibration.alwaysAsk | The floor: these always require asking, no exceptions. | ["irreversible-or-destructive", "outward-facing", "secrets-or-spend", "scope-change"] |
calibration.stageBias.intake | Default posture during the shaping stages (IDEATE, SPECIFY). | "ask" |
calibration.stageBias.execution | Default posture during EXECUTE. | "proceed-and-log" |
nudge.relay | Send a Relay ping when a question is waiting on you. | false |
nudge.telegram | Send 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:
| Dial | What it does | Default | Notes |
|---|---|---|---|
planApproval | Stop after DECOMPOSE for your sign-off before EXECUTE starts. | false | Off by default, so DECOMPOSE flows straight into EXECUTE. Turn on for a checkpoint. |
review.requireCiGreen | Require green CI before the review gate clears. | true | Part of the review gate, which is always on. |
review.reapproveOnFunctionalChange | Ask for a fresh approval if the change shifts after review. | true | Stops a stale approval from covering a diff that's since changed. |
circuitBreaker.estimateMultiplier | Trip a stop if actual work runs N times over the estimate. | 2 | Lower to catch runaway items sooner. |
circuitBreaker.tokenBudget | Hard token ceiling for a single item before the breaker trips. | 2000000 | Raise 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).
| Loop | priority | intervalMs | What it does |
|---|---|---|---|
recovery | 10 | 300000 (5 min) | Picks back up or restarts work that got orphaned mid-flight. |
inbox | 20 | 60000 (1 min) | Checks for replies on parked questions and resumes that work. |
review | 25 | 300000 (5 min) | Checks the auto-merge rules at the review gate. |
dispatch | 30 | 300000 (5 min) | Claims the top-ranked ready item and carries it to its gate. |
triage | 40 | 3600000 (1 hour) | Classifies incoming work so dispatch never runs out of items. |
hygiene | 50 | 21600000 (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:
| Dial | What it does | Default | Notes |
|---|---|---|---|
producer | Whether /flow polls the tracker or waits for a push. | "poll" | "webhook" is reserved for later; it isn't built yet. |
pollIntervalMs | How 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 often | Same file's cron field |
| Run more than one item at a time | Not yet possible; today it's always autonomy.concurrency: "sequential" |
| Cap how much work is in flight | autonomy.wipCap.global / wipCap.perProject |
| Get pinged when a question is waiting on you | involvement.nudge.relay / nudge.telegram |
| Require sign-off on the plan before EXECUTE | gates.planApproval: true |
| Cap spend per item | gates.circuitBreaker.tokenBudget |
| Make replies resume faster | loops.inbox.intervalMs (lower it) |
| Ease up on tracker reads | ingestion.pollIntervalMs (raise it) |
| Turn a loop off | loops.<id>.enabled: false |
| Change which loop wins when two collide | loops.<id>.priority |
Next steps
Turning on the automatic loop
Three ways to let /flow carry work to your review gate on its own, from an attended terminal drain to a scheduled background loop. What works today, and what is still coming.
How it works
The /flow engine architecture, a prioritized reconciler registry over a normalized event seam, driving a set of pure typed decision oracles, all behind one tracker adapter.