Skip to content

Development guidelines

The repository’s contributing guide is the single source of truth for working on Flow. It applies equally to humans and to AI coding assistants. This page summarizes the enforced rules.

Terminal window
pnpm install # JS workspace dependencies
cargo build # Rust workspace
pnpm desktop:dev # desktop app, hot reload
pnpm web:dev # web target against flow-server
pnpm site:dev # this website + docs site
pnpm typecheck # tsc across all JS packages
pnpm lint # eslint
pnpm build # full JS build
cargo test --workspace # Rust tests
pnpm e2e # Playwright UI end-to-end
  • Rust is covered by cargo test with per-crate integration tests.
  • The desktop UI is covered by Playwright end-to-end tests that drive the real React app in a browser. Tauri’s native WebDriver doesn’t support macOS, so the suite runs the web build against the Vite dev server with a Tauri IPC mock. This is the same React app the shell loads, without the native host. Each run starts from fresh UI state. Assert against stable anchors such as titles, roles, and panel headers, never CSS internals.
  • E2E is deliberately not part of the pre-commit gate, because it needs a browser and a dev server. Run it when you change UI behavior, and in CI.

Use the smallest verification set that covers the change. Never hand off work that fails a required check.

Change typeRequired checks
Frontend TypeScript / Reactpnpm typecheck, pnpm lint, pnpm build
Frontend stylingpnpm lint, pnpm build
Rust cratescargo build, cargo test --workspace
Tauri commands / IPCpnpm typecheck, cargo build, relevant Rust tests
DSL grammar/spec changesspec rebuild script, DSL parser tests, a standalone parse check
UI behavior changesrelevant Playwright spec, or pnpm e2e
Docs onlyno build, unless docs feed generated specs

Do not hardcode colors, shadows, margins, padding, or radii in components or stylesheets. Every visual parameter resolves through the --flow-* design tokens for borders, surfaces, radius, and shadow scales. Static dimensions, transitions, and focus rings belong in the modular stylesheet. Do not use inline styles except for dynamic, state-driven values.

Do not use as any, as unknown, or force-casts on dynamic values. Write type guards or runtime validation helpers that check literal unions exhaustively and fall back safely. Status checks match the full union, never loose strings.

Product source, shipped catalogs, tests, fixtures, and user-facing templates must not include vendor brand names, product names, or sample customer paths. The one exception is a surface that is explicitly an integration boundary and requires the real provider name. Use neutral placeholders everywhere else. Vendor-specific data that the user imports at runtime flows through as data, never as literals in the codebase.

Do not hardcode emoji or decorative glyph symbols in source, including log lines, UI and CLI strings, comments, and doc strings. They render inconsistently across terminals and fonts. Use plain descriptive text. If you need a status marker, prefer a word or a shared constant.

Keep comments minimal. Document only non-obvious intent or constraints that the code cannot convey. Do not write narrative prose, change history, or restatements of the code.

  • Short, imperative subject that aims for 50 characters or less, optionally with a conventional prefix: fix: preset shell nodes blocked by validation.
  • No co-author or attribution trailers of any kind.
  • Body only when it earns its place. Use it to explain the why, not the what.
  • Verify first. Run the pre-commit checks for the change type before you commit.

Anything touching the DSL goes through the spec pipeline: the compiled spec is generated, never hand-edited, and it feeds both the in-app generator prompt and the model-training corpus downstream. Preserve the round-trip property (serialize(parse(s)) is a fixed point), and re-run the spec build plus the parser tests. See the tech stack.