Contents · The Flow Book

Start here

The Flow Book1. Your first run2. Let the outside answer3. Keep the original answer

Build the decision

4. Give the program an input5. Name the work6. Give values a shape

Build the workflow

7. Decide what failure means8. Put the decision in its own file9. Put the pieces together

Go further

10. Work on several things11. Leave room for an answer12. Take Flow into an application

Build an application

13. Investigate a real question14. Challenge: diagnose and extend
Language reference ↗

Put the pieces together

You can now read the workflow as three responsibilities: ask for an amount, describe the decision, and keep failure explicit. Each responsibility has a place in the complete source bundle.

Try it · The workflow together
main.flow
use "./service"
use "./decisions"

pub flow main() -> Text = match service.lookup() {
    Ok(amount) => decisions.describe(amount),
    Err(problem) => { let _ = problem
        "Could not decide"
    },
}
service.flow
@tool pub flow lookup() -> Result<Int, Text>
decisions.flow
pub flow describe(amount: Int) -> Text = if amount > 100 { "Review" } else { "Proceed" }
Expected result"Proceed"

Read the entry first

The entry imports the service and the decision module. It calls the service once. A successful amount goes to the pure helper; an error takes its own branch. The helper does not need to know which process, model or service supplied the amount.

Try the exercise. Change the rule so that an amount of 42 or higher requires review. Keep the failure answer intact. The check tries both a successful and a failed reply, so returning a constant answer is not enough.

Explain the run to someone else

Run successfully and expand its recorded history. Point to the call, its reply, and the result. Explain which part came from outside and which part follows from the source. Replay it after changing the selected fixture. Explain why the current fixture did not change that recorded result.

This is the core habit the book is building: write a program whose decisions you can follow, make its outside observations typed, and use the record to distinguish what happened from what could happen on another run.

What belongs around this program?

The application still chooses the implementations, obtains credentials, handles approvals and manages the run's lifecycle. Those concerns do not become language mechanisms because a Tool has a helpful name. The final chapter takes that program into an application.

Exact rules: application boundary, execution.

Diagnose before repairing

A useful next workflow measures several candidates, finds the failing ones, repairs only those, and measures them again. Keep the decision in Flow: returned facts should select the work that happens next. Keep the specialist calculation behind a typed Tool.

Read the recorded reply beside the predicate that uses it. A passing check only answers its stated question. Ask which units, inputs, time window and reference it covers. “Close enough” pixels cannot establish exact pixels; a build that succeeded for yesterday's source cannot establish today's artifact.

A repair is not a new passing check. Recheck the edited candidate before choosing it. Preserve failures and uncertainty instead of replacing them with a broad “verified” flag.

Practice with shared libraries

From a repository checkout, build the CLI with cargo build -p flow-cli --locked. Run flow test libraries/workflow to exercise the shared selection and repair functions against typed fakes. Run python3 examples/integration-repair/verify.py to bind real numerical Python, C compilation and SQLite implementations.

The exercise checks three candidates. It repairs only the failed Python and C candidates, rechecks all three and selects the greatest passing score. A second scenario changes a dependency after validation: delivery is refused. A third uses a narrower check domain: that evidence cannot justify repair or delivery. The host evidence library records the entire declared dependency set, not only the main source file. Undeclared dependencies remain the application's concern.

Try these steps:

  1. Read libraries/workflow/integration_example.flow: locate observation, repair, recheck and selection. Specialist computation stays behind Tools.
  2. Run the exercise with --receipt target/integration-repair.json --workspace target/integration-workspace (a new directory). Read the ordered repairs and the exact refusal reason for each negative scenario.
  3. Install the toolkit into a new environment with python3 -m venv target/workflow-venv and target/workflow-venv/bin/python -m pip install ./toolkits/python. Run target/workflow-venv/bin/flow-evidence target/integration-workspace/ok/compiler.check.json --subjects target/integration-workspace/ok/compiler.subjects.json --required target/integration-workspace/ok/required-domain.json. It explains freshness and domain mismatches without invoking the validator.
  4. Find the ok run ID in the receipt. Inspect it with flow history RUN --runs target/integration-workspace/ok/.flow/runs --kind Call or MCP. A check receipt is ordinary host reply data; it does not create a permission or a scientific proof.

For general transformations, begin with std's filter, partition, traversal and extrema functions. Put domain-specific workflow functions in a library. Keep processes, dependencies and delivery evidence in the application toolkit.