Post-mortem analysis shows these practices prevent most project failures. Violating them triggers costly rework. Defects caught later cost 10–100× more to fix (Boehm, 1981).
- Never skip a flow state. Every state boundary goes through flowr check → dispatch to owner → flowr transition. No shortcuts, no manual session edits, no jumping ahead.
- Never bypass owner dispatch. Each state has an owner agent. The orchestrator dispatches to that agent with skills loaded. It never does the work itself. One agent, one hat at a time.
- Never collapse progressive gates. Multi-step gates (review: design → structure) are separate for a reason. Each one can fail independently and send work back. Conventions (naming, docstrings, formatting) are enforced in a separate polish state after feature acceptance.
- Never decompose a feature without stakeholder approval. If a feature is too large for INVEST, propose the split to the stakeholder with rationale. They decide what's core vs. deferred.
- Verify inputs exist before entering a state. Every state's
inartifacts must be readable on disk. If they're missing, stop and reconstruct them. Don't proceed with assumed knowledge. - A feature is not done until every interview requirement is traced. Every stakeholder Q&A must map to either a passing @id test or an explicit stakeholder deferral. Untraced requirements = incomplete delivery.
- Respect git branch discipline. Every state declares
git: dev,git: feature, orgit: mainin its attrs. Verify the current branch matchesattrs.gitbefore starting any work. If the branch is wrong, checkout or create the correct branch before proceeding. Never switch branches mid-state. Before exiting a project-phase flow (discovery, architecture, branding, setup), setcommitted-to-dev-locally: ==verifiedevidence. Changes must be committed to dev before advancing. - Every feature branch must be merged back to dev. A feature is not delivered until its commits are squash-merged into local dev and
task test-fastpasses on dev. The develop-flow exits to deliver-flow which handles the merge, but the orchestrator must never leave a feature branch dangling — if the session ends mid-feature, resume and complete the merge before starting new work.
.flowr/flows/: YAML state machine definitions (source of truth for routing).cache/sessions/: runtime session state.templates/: artifact templates (strip.templates/prefix and.templatesuffix → destination path).opencode/: agents, skills, and knowledge
When creating a document, use the template in .templates/ that matches the artifact type. Strip the .templates/ prefix and .template suffix to determine the destination path. For example:
.templates/docs/adr/ADR_YYYYMMDD_<adr_id>.md.template→docs/adr/ADR_20260430_my_decision.md.templates/docs/features/<feature_name>.feature.template→docs/features/my_feature.feature.templates/.cache/interview-notes/IN_YYYYMMDD_<session_id>.md.template→.cache/interview-notes/IN_20260430_session_management.md.templates/.cache/sim/simulation_results_YYYYMMDDTHHMMSS.md.template→.cache/sim/simulation_results_20260517T143000.md.templates/.cache/acceptance/<feature_id>.md.template→.cache/acceptance/domain_value_objects.md
If no template exists for an artifact type, create the document without one.
[[domain/concept]] → .opencode/knowledge/{domain}/{concept}.md
Knowledge files use 4-section progressive disclosure. Choose the level that matches the task depth:
| Fragment | Loads | When to Use |
|---|---|---|
#key-takeaways |
Frontmatter + Key Takeaways | Quick reference or reminders when knowledge is already familiar |
#concepts |
Frontmatter + Key Takeaways + Concepts | Understanding concepts without detailed examples or procedures |
| (no fragment) | Entire file | Performing evaluation, review, or implementation that needs detection heuristics, examples, tables, and procedures |
Rule of thumb: If the agent needs to find violations, detect patterns, or apply detailed criteria, load the full document. If it only needs to recall a principle or definition, #key-takeaways is sufficient.
sed '/^## Concepts/Q' file.md # Frontmatter + Key Takeaways only
sed '/^## Content/Q' file.md # Frontmatter + Key Takeaways + Concepts
cat file.md # Full documentExamples:
[[requirements/invest#key-takeaways]]: quick reference for INVEST criteria[[requirements/invest#concepts]]: understanding what each letter means with context[[software-craft/smell-catalogue]]: full catalogue needed to detect code smells during review
Do not enumerate files, as they go stale. Discover what exists at runtime:
ls .opencode/agents/ # agent identity definitions
ls .opencode/skills/ # skill directories (each has SKILL.md)
find .opencode/knowledge -name '*.md' # knowledge files
find .templates -name '*.template' # artifact templates
find docs/research -name '*.md' # research source notes (cited by knowledge files)Artifact names in in and out lists use these conventions:
| Pattern | Meaning | Example |
|---|---|---|
filename.md |
A specific document | domain_spec.md, product_definition.md |
dir/<param>.ext |
A specific instance identified by parameter | features/<feature_id>.feature, .cache/interview-notes/<session_id>.md, adr/<adr_id>.md |
dir/*.ext |
Multiple documents of that type available in in |
.cache/interview-notes/*.md, adr/*.md |
conceptual_name |
A runtime artifact that passes between states within a flow | typed-source-stubs, test-implementations |
Placeholders in template filenames and flow artifact paths use the <type_id> pattern where type identifies the document kind and _id signals snake_case formatting. See template filenames for the canonical placeholder names.
File naming rule: All filenames use snake_case (e.g., domain_value_objects.feature, ADR_20260504_protocol_adapters.md). Cache folders use kebab-case for multi-word names (e.g., interview-notes/, post-mortem/). Python/test folders use snake_case (e.g., tests/features/).
Wildcards (*) in in indicate that multiple documents of that type are available. List the directory contents first, then read selectively based on the task. When a state creates a single instance, use a <parameter> name instead.
Runtime artifacts (not backed by files) use descriptive names that make their purpose clear: typed-source-stubs (source files with type signatures only), test-skeletons (test files with structure only), test-implementations (tests with bodies), source-implementations (production code with behavior), refactored-source (code after refactoring pass), feature-commits (git commits for one feature), merged-commits (commits merged to local main), root-cause-analysis (analysis findings), polished-source (code after convention application).
Cache artifacts are persisted to .cache/ for cross-session durability. They are not spec documents but process evidence that survives session boundaries: .cache/acceptance/<feature_id>.md (PO acceptance record with traceability matrix), .cache/interview-notes/<session_id>.md (raw stakeholder input, archival after discovery), .cache/sim/simulation_results_<timestamp>.md (simulation evidence per iteration).
Environment artifacts are produced by tooling rather than flow states: coverage-reports (test coverage output), test-output (test runner output), linter-output (linter output). These exist on disk after running the relevant tool and are referenced in in but not in any state's out.
All commands output JSON by default. Use --text for human-readable output. All commands require the virtual environment: source .venv/bin/activate. See [[workflow/flowr-operations]] for full command reference, output formats, and workflow pattern.
Commands accept short flow names (e.g., planning-flow) or full file paths. Use --session <name> to resolve flow/state from a session instead of specifying them explicitly.
| Command | Purpose |
|---|---|
python -m flowr check <flow> <state> |
Show state attrs, owner, skills, and transitions |
python -m flowr check <flow> <state> <target> |
Show conditions for a specific transition |
python -m flowr check --session |
Show current session state (read-only) |
python -m flowr check --session <trigger> |
Show conditions for a transition via session |
python -m flowr next <flow> <state> [--evidence key=value] |
Show all transitions with status markers (open/blocked) |
python -m flowr next --session [--evidence key=value] |
Show transitions from session state with status |
python -m flowr transition <flow> <state> <trigger> [--evidence key=value] |
Advance to the next state |
python -m flowr transition <trigger> --session [--evidence key=value] |
Advance using session (auto-updates session) |
python -m flowr validate [<flow>] |
Validate flow definition(s) |
python -m flowr validate --session |
Validate the current (sub)flow from session |
python -m flowr states <flow> |
List all states in a flow |
python -m flowr states --session |
List states in the current (sub)flow from session |
python -m flowr mermaid <flow> |
Export flow as Mermaid diagram |
python -m flowr config |
Show resolved configuration with sources |
python -m flowr session init <flow> [--name <name>] |
Create a session at the flow's initial state |
python -m flowr session show [--name <name>] |
Display current session state and call stack |
python -m flowr session set-state <state> [--name <name>] |
Manually update session state |
python -m flowr session list |
List all sessions |
Check pyproject.toml for taskipy tasks and tool configuration. Common commands:
| Command | Purpose |
|---|---|
task test |
Run tests with short tracebacks |
task test-fast |
Run fast tests only (excludes slow marker) |
task test-build |
Run full test suite with coverage, hypothesis stats, and HTML report |
task run |
Run the application |
Linting and formatting:
| Command | Purpose |
|---|---|
ruff check . |
Functional lint (bugs, security, complexity) |
task conventions |
Full lint (all rules including naming, docstrings, formatting) |
ruff format . |
Auto-format |
Every state transition must go through flowr. Do not skip steps or guess transitions. See [[workflow/flowr-operations]] for the full command reference.
- State entry: Run
python -m flowr check --sessionto see current state, owner, skills, and available transitions (JSON output: parseattrs.owner,attrs.skills,attrs.in,attrs.out,transitions). Verify allinartifacts exist on disk. If any are missing, stop and flag rather than proceeding with assumed knowledge. Announce the state in one line, e.g.→ specify-feature. No preamble, no recap of how you got here. - Dispatch to owner agent: The state's
ownerfield names the responsible agent. Call that agent as a subagent with the state'sskillsloaded, passing the state attrs as context. Owner mapping:PO→ product-owner,DE→ domain-expert,SE→ software-engineer,SA→ system-architect,R→ reviewer,Design Agent→ design-agent,Setup Agent→ setup-agent. - Do the work: Load and execute the skill(s) listed in the state's
skillsfield. Read allinartifacts before starting work — they are mandatory context. Write only tooutartifacts. Commit changes to the branch indicated by the state'sgitattribute (mainorfeature). Never switch branches mid-state. - State exit: The anchor item in the todo handles this (see [[workflow/todo-anchor-protocol#key-takeaways]]).
Convention checks (full lint via task conventions, ruff format, pyright, docstrings, type annotations) are prohibited during design-phase states (create-py-stubs, write-test, implement-minimum, refactor, review-gate). Only task test-fast is permitted. The default ruff check . runs functional rules only (bug-catching, security, complexity). Design changes invalidate convention work. Conventions are applied in the polish state after feature acceptance.
When dispatching an agent during design phase:
- Do NOT include convention tool commands in the prompt
- Only include verification steps that the skill explicitly defines
- The skill's verification steps are the ceiling, not the floor
Exception: The polish-code skill explicitly runs convention commands (task conventions, ruff format, task static-check) after feature acceptance.
One state = one dispatch. Every state transition produces exactly one agent dispatch with exactly the skills listed in the state's skills field. Never combine multiple states into a single dispatch. The orchestrator's job is routing, not doing. See [[workflow/todo-anchor-protocol#concepts]] for the full protocol.
At state entry, generate a procedural todo list using the todowrite tool. Format: [X] completed, [ ] pending, [~] anchor (always last).
- Preparation (
[ ]): verify current branch matchesattrs.git(checkout or create if wrong). List availableinartifacts. - Dispatch (
[ ]): dispatch to the owner agent listed inattrs.owneras a subagent with skills loaded. The orchestrator MUST NOT do the work itself — only route. Owner mapping:PO→ product-owner,DE→ domain-expert,SE→ software-engineer,SA→ system-architect,R→ reviewer,Design Agent→ design-agent,Setup Agent→ setup-agent. - Load skills (
[ ]): read every skill file listed inattrs.skillsfrom.opencode/skills/<skill_name>/SKILL.md. This step is MANDATORY — never skip it. - Skill-derived work items (
[ ]): one todo item per numbered step in the skill, using the skill's own language verbatim. These are the substantive work items. Self-generated items are only permitted for infrastructure (read artifacts, commit) — never for the core procedure. - Output (
[ ]): one peroutartifact - Verification (
[ ]): check constraints, run tests/lint if applicable - Anchor (
[~], always last): flowr next → pick transition → flowr transition → rewrite todo
The todo is the execution contract. Every item must be marked [X] before the anchor fires. One state per todo; never span multiple states or collapse loop iterations. Full protocol: [[workflow/todo-anchor-protocol]].
Todo discipline: After completing ANY step, update the todowrite tool to mark it [X] and set the next step [ ] to in_progress. If the todo list is empty or missing, regenerate it immediately — working without a todo means working without a contract. Never let the todo go stale between steps.
Before starting a flow, create a session to track progress:
python -m flowr session init <flow> --name <name>For project-level flows, use a descriptive name like project. For feature flows, use the feature name. The session tracks the current flow, state, call stack (for subflows), and params (including feature-id). When the first state has a flow: field, session init auto-enters the subflow.
The three primary flows are independently invocable:
define-flow— spec creation, validation, feature refinement, and architecture (discovery → spec-validation → refine-features → architecture)develop-flow— feature selection, example writing, TDD implementation, acceptance (per feature cycle)deliver-flow— squash-merge, publish decision, PR creation
When develop-flow exits needs-architecture, the orchestrator must re-enter define-flow at the architecture state. Start a new define-flow session and use flowr session set-state architecture to skip to the architecture state. The architecture-flow fast-path (architecture-complete: ==verified) means re-running is cheap when no changes are needed.
When post-mortem-flow exits needs-architecture, follow the same procedure: re-enter define-flow at architecture.
States declare their git context in attrs.git:
git: main: all changes are committed to the local main branchgit: feature: all changes are committed to the current feature branch
Before exiting a project-phase flow (define, branding, setup), the exit transition requires committed-to-dev-locally: ==verified evidence. This guarantees project artifacts are persisted before advancing to the next phase.
Announce the state once at the top, then go quiet:
- Respect the artifact contract: The state's attrs define what the owner agent may read and write:
in: Mandatory context. Allinartifacts must be read in full before starting work. For wildcard patterns (*.md), list the directory first, then read all discovered files. Theinlist defines what you must read — no skipping, no selective reading.out: May create or edit. Section sub-lists indicate which sections the state should produce or update. Follow the out artifact protocol (see below).- Files not in
outmust not be written to. If findings affect an artifact outside the output contract, flag them in output notes and defer the change to the step that owns that artifact. - The flow contract must always be followed unless the stakeholder explicitly asks to break it.
- Cumulative editing: When a flow loops back to a state that was previously executed (e.g.,
needs-reinterview→stakeholder-interview→domain-discovery), theoutartifact is edited, not recreated. The agent reads the existing file, incorporates new information, and adjusts existing content. This is especially important fordomain_spec.mdandglossary.mdwhich accumulate knowledge across multiple discovery iterations.
- Out artifact protocol: Before writing to any
outartifact:- Check if the file exists on disk.
- If it exists → read it, then edit only the sections declared in the flow's
outsection sub-lists. Preserve existing content outside those sections. - If it does not exist → resolve the template path: take the destination path, prepend
.templates/, append.template(e.g.,docs/spec/domain_spec.md→.templates/docs/spec/domain_spec.md.template). Copy the template to the destination path, then edit the declared sections. Strip any template placeholders during editing. - If no template exists for a non-Python file referenced in
in/out, raise an error for the stakeholder to decide. - Environment artifacts (e.g.,
coverage-reports,test-output,linter-output) are produced by tooling rather than flow states. They exist on disk after running the relevant tool and are referenced ininbut not in any state'sout.
- Specification documents are read-only during development. During TDD and review cycles, the SE and reviewer may ONLY modify production code and test code. Spec document inconsistencies must be FLAGGED in output notes, not fixed directly. Spec docs are owned by other flow states and can only be changed through the appropriate flow step, after code is reviewed and approved.
- Flag issues with precise citations. When flagging a problem during review or adversarial analysis, include file:line references (e.g., "domain_spec.md:23 conflicts with login.feature:15"). Vague findings create rework.
- Do the work with the fewest, quietest commands. Suppress verbose output. If a command can be scoped with a flag, pipe, or limit, use it. Don't dump full files or directory listings when a targeted query answers the question.
- No narration between steps. The command and its output are the conversation. Don't echo what you're about to do or what you just did.