UI evaluation
Machine-judged visual correctness — capture each declared UI state, score it offline, gate verification on the result.
Screenshots that a human glances at are not evidence. The UI eval pipeline captures
each declared UI state, scores it through an offline visual-eval engine, and writes
machine-readable evidence the verifier consumes and gates on.
Running it
/devflow:ui-eval 6
/devflow:ui-eval path/to/manifest.json
The pipeline
declare surfaces → capture each state → score offline → evidence → verifier gates
(manifest) (Maestro / Playwright) (visual-eval) (JSON) (pass / fail)
The ui-evaluator agent drives it. Backend selection is automatic: Flutter
surfaces go through Maestro, web through Playwright.
Setup
One command handles adoption — it detects missing system tools, builds an install plan, dispatches it through the handoff daemon (or prints it when the daemon is down), and chains the bootstrap:
df-tools flutter-ui setup
df-tools flutter-ui setup --print-only # just show the plan
df-tools flutter-ui setup --auto
Then:
df-tools flutter-ui bootstrap [project-dir]
df-tools flutter-ui eval <manifest|captureResults>
Verification hooks
The verifier consumes eval output through dedicated verify subcommands:
df-tools verify flutter-ui-bootstrap <project-dir> # is the project set up for eval?
df-tools verify flutter-ui-eval <manifest> # did the captured states score green?
df-tools verify flutter-state-coverage <trd-path> # are all declared states covered by tests?
flutter-state-coverage catches the common gap: a widget test that exercises the
happy path and silently ignores loading, empty and error states.
Scoping
df-tools detect flutter-ui-scope <objective>
Determines whether an objective has UI surfaces worth evaluating. The planner uses this to decide whether to include a UI-eval step at all — running the pipeline on an objective with no UI is pure cost.
Why offline scoring
The evaluation engine runs locally against captured images rather than asking a model to look at a screenshot and opine. Two reasons:
- Determinism. The same capture scores the same way every run, so a regression is a real change and not model variance.
- Gate-ability. A numeric score can be a pass/fail threshold the verifier enforces. “Looks fine to me” cannot.
The concrete model id used for the vision judge comes from the models{} map in
references/model-profiles.json. That map is live — sent to the Messages API at
runtime — so a stale id there is a runtime defect, not a documentation slip.