Vibestrate
Vibestrate
docs

Derived flows

Builds a flow from the work itself, rather than forcing every task through one fixed recipe.

In simple words

A flow is a generic recipe, so a fixed one is always slightly wrong: too heavy for a one-line change, too light for a migration touching money.

Deriving builds a flow from the work itself. Give it a task with real parts to it and you get a flow shaped to those parts, rather than the same four steps regardless.

Task: "Add team billing: a teams table, an owner-only invite endpoint, a seat counter"

derived flow
  plan                 -> planner
  architecture         -> architect
  implement-migrate    -> implementer   (schema change, isolated)
  implement-endpoint   -> implementer
  implement-seats      -> implementer
  validation           -> your commands
  review-correctness   -> reviewer      (the `money` tag aims the correctness lens)
  fix                  -> fixer
  revalidation         -> your commands
  verify               -> verifier

Read the order: validation waits for every unit step, and every review lens waits for validation. plan, architecture, fix, revalidation and verify are emitted whatever the task looks like.

Tip

Deriving is the one flow surface with no dashboard control. It is a terminal command that writes nothing: it prints a flow for you to read, and adopting it is the Import button on the dashboard’s Flows page. Treat the output as a proposal from something that read your task, not as a decision already made.

When to reach for it

The task has distinct parts A schema change, an endpoint and a UI are three different risk profiles wearing one title.

The default feels wrong Too many steps for a rename, too few for a migration.

You want a flow you will reuse Derive once, read it, adopt it, and it becomes a normal project flow.

A part needs a different lens Money-touching work can carry a review the rest of the task does not need.

Did you know

A derived flow can include a lens that stands itself down. If the work turns out not to touch the thing that lens was added for, the step reports that it found nothing to check rather than manufacturing a finding to justify its own presence.

Deriving one, and adopting it

Two commands and one button. vibe flows derive "<task>" prints the units, the compiled graph, the reason for every lens, and seat coverage against your crew, then stops.

vibe flows derive "add team billing" --id billing --yaml > billing.yml
vibe flows import billing.yml

--yaml is the canonical flow document, --json the whole draft including the shaping turn’s rationale. --crew <id> checks coverage against a specific crew.

The adopt step also lives on the dashboard: Flows in the sidebar, then Import, which takes Paste YAML or From URL and has an overwrite checkbox for a project flow of the same id. After that it is an ordinary project flow - in the catalog, in the flow editor, and selectable under Flow on the New run form.

Read the coverage check before adopting. A seat reported as ambiguous means two roles in your crew can fill it, and the run will need --seat-role to disambiguate. Cheaper to learn here than eight minutes into a run.

The model describes, it does not design

A shaping turn returns a decomposition, not a flow: the units of work, what each one depends on, and what each one is risky about. Deterministic code turns that into the graph.

That split is the safety property, the same reasoning that makes a block policy owner-only. Every step id, every needs edge and every review lens is computed, so a model influences what gets reviewed only through a closed vocabulary, and can never remove a gate, reorder one, or write a step that renders its own verdict. A poor decomposition produces a flow that is wrong-but-gated, never one that is ungated.

What a fixed flow cannot answer

“c depends on b.” Each unit becomes its own implement step, wired to the units it named. Unit steps are also chained in dependency order even when two units are independent - they share one git worktree and both write the diff, so running them at once would race.

“Testing only once a, b and c are made.” Validation waits for every unit step. A partial tree cannot be meaningfully validated.

“When do we review, and what?” Each unit is tagged with what it is risky about, from a closed set, and each tag maps to a review lens:

taglens
authauthorization
untrusted-inputinjection
secretssecrets and exposure
data-integrity, concurrency, money, migration, public-apicorrectness
uiaccessibility
performanceperformance

Several lenses run in parallel and join at an arbiter. A single lens is the decision, so no arbiter is added and no arbiter seat is declared. A task that declares no risk at all still gets one correctness review: “nothing risky” is a claim about the work, not a licence to ship it unread.

A lens that stands itself down

A risk tag says a unit might touch a subject. Whether the finished code actually does is a different question, and it is answerable from the diff.

So each conditional lens carries a stand-down condition, evaluated against the run’s real changes:

lensstands down when
authorizationnothing in the change decides who may act
injectionno caller-supplied input reaches a sink
accessibilitynothing touches the rendered surface

Correctness has no condition. It is the floor: there is no diff safe to leave entirely unread.

That is what makes a speculative tag cheap: an auth tag on code that turns out to have no permission check costs nothing, instead of a turn spent confirming an absence.

Skipping requires positive evidence of absence. An unreadable diff, an empty change set, or a read-only run all RUN the step. A wrong call can only ever cause more review, never less - the only safe direction for a mechanism whose whole job is deciding when a review does not happen.

The patterns are deliberately broad and deliberately not configurable. Broadening them is harmless; narrowing them silently weakens a gate, and a config file is the wrong place to disarm a review. The control surface is --flow-force <step> on vibe run, which forces a step to run whatever its condition says.

--flow-skip <step> is its counterpart, and it is narrower than it sounds: a step can only be skipped where the flow’s author marked it optional, and a derived flow marks none of its lenses optional. So no flag on the command line turns a derived review off - the resolver refuses the step by name. The switch does have somewhere to bite: the built-in Quality Arbitration flow declares its plan-review step optional, and --flow-skip plan-review drops that one.

Decomposition is not free

Each unit is a separate model turn on the same worktree, and they run one after another, so the implementation spend scales with the number of units. Splitting one cohesive change into eight buys eight turns and improves nothing.

Measured on a small CRUD app: an eight-unit decomposition cost 2.6 times the implementation spend of the same app built in one turn, and scored slightly worse. More expensive, and no better. That measurement is written into the shaping turn’s own prompt, so the model doing the splitting has been shown what splitting costs.

The derive output says how many implement steps it split into, and at four or more it adds a COST: line spelling out the multiplier. --max-units <n> is the hard stop.

© 2026 Vibestrate
v0.3.0 Shonshon - Evolving Technologies