Contributing overview
This page is the workflow: how a change moves from a failing test to a merged commit. The rule that gates everything is one command. Run it before you commit:
CI=true melos run gateThe gate runs format, analyze, lint, and the coverage check. All four must be green. Never leave the tree broken. The rest of this page explains each step and the rules around it.
Set up the workspace
Section titled “Set up the workspace”Clone the repo and resolve the workspace, then install the hooks:
git clone https://github.com/SimonErich/fluvie.git && cd fluviedart pub get # resolves the workspace rootmelos bootstrap # resolves all packagesbash .githooks/install.sh # installs the git hooks (required)You need Flutter 3.44+, Dart 3.12+, and ffmpeg on your PATH for the
render-integration tests.
Test first, always
Section titled “Test first, always”Every change starts with a failing test. No production .dart file lands before
its test exists and has failed for the right reason. Red, then green, then
refactor. See the Testing guide for the suites and fakes.
The quality gate
Section titled “The quality gate”The gate is four steps, each its own melos script:
melos run formatchecksdart formatleft nothing to change.melos run analyzerunsdart analyzewith--fatal-infosand--fatal-warnings, so an info is a failure.melos run lint --no-selectruns the custom lints (dart run custom_lint).melos run coverage:checkenforces the 97% line-coverage floor.
Goldens run separately with melos run test:goldens on the Linux baseline. Run
melos non-interactively with CI=true to avoid the prompt path.
The layering law
Section titled “The layering law”Dependencies point down only, inside packages/fluvie/lib/src/:
core pure Dart: Time, Keyframe, enums, Defaults, contracts ^timing TimeScope, frame resolution, the trigger/anchor resolver ^features rendering, animation, composition, elements, media, audio, theme ^diagnostics the inspector model and timeline table; nothing depends on itcore depends on nothing but Flutter math types. timing depends only on
core. A feature may depend on core and timing, never on diagnostics.
Nothing imports another package’s src/. The layering and no_src_import
custom lints enforce all of this, so a violation fails melos run lint.
A forward dependency goes through an interface in the lowest stable layer, with a fake for tests and the real implementation injected through a Riverpod provider.
Capture is headless
Section titled “Capture is headless”In capture mode the frame is the only clock: no wall-clock and no async work
inside a frame, and media is pre-resolved before the loop. For effects, prefer
seeded noise(seed) and random(seed) so a golden stays stable run to run.
Fluvie does not guarantee byte-identical output across machines or encoders.
Regenerate goldens with flutter test --update-goldens --tags golden and review
the PNG.
Code standards
Section titled “Code standards”- One primary public type per file. No
utils.dartdumping grounds. - 200 lines per production file, 80 lines per widget
build. The pre-commit hook enforces the file budget. - Every public member of
packages/fluviecarries dartdoc. - No
TODO,FIXME,print, or unjustifieddynamic. - Page width 100, trailing commas required.
- A new public API needs its dartdoc and its documentation page, in the same change. Docs snippets come from compiled sources, never hand-typed; see the voice rules in the documentation index.
Commits
Section titled “Commits”Use Conventional Commits, one concern per commit (feat(timing): …,
fix(rendering): …). Bundle nothing unrelated. The commit-msg hook enforces the
format.
Where to next
Section titled “Where to next”- Testing guide: the suites, the tags, and the fakes.
- Coverage and the ignore policy: the 97% floor and when an ignore is allowed.